应改用 aiohttp + asyncio 替代 requests.get() 串行请求,因其在IO密集场景下性能高3倍以上、资源占用更低,但需合理配置连接池、超时及并发数,并避免漏写 await、混入阻塞操作、忽略异常等常见错误。

直接换 aiohttp + asyncio,别再用 requests.get() 串行发请求了。单线程协程在 IO 密集场景下,性能常比多线程高 3 倍以上,且内存和 CPU 占用更低——前提是连接池配对、错误不炸穿、并发数不瞎设。
为什么 requests + time.sleep 一跑就卡死
每次调用 requests.get() 都新建 TCP 连接,三次握手 + SSL 握手耗时动辄 200–500ms;time.sleep(1) 更是让整个线程停摆,100 个 URL 就得等 100 秒。这不是爬虫,是排队打卡。
关键问题不在“慢”,而在“阻塞”:一个请求没回,别的全干等。真实网络环境里,90% 时间花在等响应上,CPU 却闲着。
-
requests默认不复用连接,除非你显式传Session并启用pool_connections -
time.sleep()是同步阻塞,协程里绝对不能用,会挂起整个事件循环 - 没设超时的
requests.get()可能卡住几十秒,而aiohttp必须显式指定timeout
aiohttp.ClientSession 的连接池必须手动配
aiohttp 的并发能力不是开箱即用的。默认 ClientSession 的连接池极小(通常只有 10–20 个空闲连接),并发数一超就排队等连接,反而比串行还慢。
立即学习“Python免费学习笔记(深入)”;
实操建议:
- 初始化
ClientSession时务必设置connector,例如:aiohttp.TCPConnector(limit=100, limit_per_host=30) -
limit控制全局最大连接数,limit_per_host防止单域名打爆(避免被封) - 记得加
timeout:用aiohttp.ClientTimeout(total=10),别依赖默认值 - 不要在循环里反复创建
ClientSession,它本就是为复用设计的
async/await 写法里最容易崩的三个点
协程代码看着像同步,但执行逻辑完全不同。以下错误会导致任务静默失败、内存暴涨或 event loop 崩溃:
- 漏写
await:比如调用fetch(session, url)却没加await,返回的是coroutine对象而非结果,asyncio.gather()会直接报TypeError: an integer is required - 把阻塞函数塞进协程:比如在
fetch()里调用time.sleep()或json.loads()大文本,会卡住整个 loop;大解析任务建议用loop.run_in_executor()拆出去 - 异常没捕获:一个请求 404 或超时未
try/except,整个gather()就中断;推荐用return_exceptions=True让失败不中断其他任务
并发数不是越大越好,得看目标站扛不扛得住
本地压测 100 并发可能跑得飞快,但真实场景中,目标网站的反爬策略、CDN 限速、后端 QPS 限制才是瓶颈。盲目提高并发,换来的是 429、503、IP 封禁。
稳妥做法:
- 先从 10–20 并发起步,观察平均响应时间与成功率
- 用
aiohttp的raise_for_status()主动抛异常,配合重试(如tenacity库)比硬扛更稳 - 如果发现大量
ClientConnectorError或ServerDisconnectedError,说明连接池或目标服务已到极限,该降并发或加延迟 - 别忘了 User-Agent 和 Referer —— 没这些,有些站连 DNS 解析都给你缓存成 403
真正卡住人的从来不是语法,而是连接池参数没调、异常没兜底、并发数拍脑袋定。这三处不动,换再 fancy 的框架也白搭。


















