gevent 不适合现代爬虫——它依赖 monkey patch,与 asyncio 冲突,且 http/2、连接池、重试等支持薄弱;https 易出 ssl 错误,dns 和文件描述符限制并发,混用 asyncio 会崩溃。

gevent 不适合直接写现代爬虫——它依赖 monkey patch,与 asyncio 生态冲突,且对 HTTP/2、连接池、重试策略等关键能力支持薄弱。
为什么 gevent 在爬虫场景下容易出问题
你写的 gevent.spawn() 看似并发,但实际受制于全局解释器锁(GIL)和底层 socket 行为。更关键的是:gevent.monkey.patch_all() 会无差别劫持标准库模块(如 ssl、socket、threading),一旦引入其他依赖(比如 requests + urllib3 + pyopenssl 组合),就极易触发死锁或 SSL handshake timeout。
- HTTPS 请求失败率高,尤其在大量域名混合访问时,
ssl.SSLError: [SSL: UNKNOWN_PROTOCOL]频发 -
gevent.pool.Pool的 size 设置不等于真实并发连接数,底层仍受限于系统文件描述符和 DNS 解析阻塞 - 无法原生配合
aiohttp或httpx,而这两个是当前 Python 异步 HTTP 的事实标准
如果必须用 gevent,只推荐用于纯 HTTP/1.1 内网爬取
限定场景:目标域名固定、证书可信、无重定向、无 Cookie 会话管理、响应体小。此时可最小化 patch 范围,避免破坏关键模块:
from gevent import monkey # 仅 patch socket 和 ssl,跳过 threading 和 subprocess monkey.patch_socket() monkey.patch_ssl() <p>import gevent from gevent.pool import Pool import urllib.request</p><p>def fetch(url): try: with urllib.request.urlopen(url, timeout=5) as resp: return len(resp.read()) except Exception as e: return str(e)</p><p>pool = Pool(20) results = pool.map(fetch, ['<a href="https://www.php.cn/link/374cad868cb62202053d308252bc4040">https://www.php.cn/link/374cad868cb62202053d308252bc4040</a>'] * 100) </p>
注意:urllib.request 是唯一能被 gevent 安全接管的内置模块;用 requests 会导致 patch 失效,退化为同步行为。
gevent 与 asyncio 混用会直接崩溃
常见错误是想“兼顾两者”,比如在 async def 函数里调用 gevent.sleep(),或用 gevent.joinall() 等待 asyncio Task。这会造成事件循环嵌套、上下文错乱,抛出 RuntimeError: This event loop is already running 或静默卡死。
- 不要在同一个进程里同时启动
asyncio.run()和gevent.spawn() - 不要用
gevent.getcurrent()判断协程身份——asyncio 的asyncio.current_task()与之不兼容 - 第三方库如
celery若配置了 gevent 并发模型,就不能再在其 worker 中跑httpx.AsyncClient
真正需要高并发爬虫时,gevent 的 patch 机制就像给汽车发动机换上自行车链条——能转,但随时崩断。重点该花时间调优 httpx.AsyncClient 的 limits 参数和 asyncio.Semaphore 控制并发粒度,而不是绕路调试 SSL 握手超时。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











