asyncio.semaphore 是限流核心,asyncio.queue 仅用于排队;须用 aiohttp 或 httpx 替代 requests;需配置连接池、分级处理 429/connectionreseterror/timeout;避免嵌套 asyncio.run(),主入口应扁平并妥善清理任务。

asyncio.Semaphore 是限流的核心,不是 asyncio.Queue
限流的关键是控制并发请求数,不是缓冲任务。用 asyncio.Semaphore 才能真正阻塞协程直到有可用信号量;asyncio.Queue 只管排队,不自动限流,容易因堆积大量待发请求导致内存暴涨或服务端拒绝连接。
常见错误是把 asyncio.Queue 当作限流器,结果在 while not queue.empty() 循环里疯狂 await queue.get(),却忘了加 semaphore 控制实际发出的请求数。
- 初始化时传入最大并发数:
sem = asyncio.Semaphore(5) - 每个请求前必须
await sem.acquire(),结束后sem.release()(建议用async with sem:自动管理) - 不要在
async with sem:外部做耗时解析——否则信号量占用时间远超网络等待,实际并发度被严重低估
requests 不支持 async,必须换 aiohttp 或 httpx
直接在 async def 里调用 requests.get() 会阻塞整个事件循环,失去异步意义。Python 3.11 的 asyncio 本身不提供 HTTP 客户端,必须选原生异步库。
aiohttp 更成熟,httpx 语法更接近 requests 且支持同步/异步双模式,但注意:两者默认都不复用连接池,高频请求下必须显式配置 Connector。
- 用
aiohttp.TCPConnector(limit=10, limit_per_host=5)避免单域名连接打满 -
httpx.AsyncClient(limits=httpx.Limits(max_connections=10))同理 - 忘记关闭客户端(
await client.aclose()或async with)会导致连接泄漏,运行越久越慢
处理 429 和 ConnectionResetError 要主动退避,不能靠重试次数硬扛
限流不是万能的——目标网站可能按 IP 或 token 动态调整速率限制,单纯卡死并发数无法避免 429。真实场景中必须结合响应状态码和异常类型做分级退避。
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
常见误操作是把所有异常都丢进 try/except Exception 然后统一 await asyncio.sleep(1),结果 ConnectionResetError(服务端主动断连)和 TimeoutError(本地网络问题)都被当成限流信号,反而拖慢正常请求。
- 对
429 Too Many Requests:指数退避,如await asyncio.sleep(2 ** retry * 0.5) - 对
ConnectionResetError或ClientOSError:短延时重试(0.1–0.5s),可能是瞬时抖动 - 对超时:优先调大
timeout参数(aiohttp.ClientTimeout(total=30)),而非盲目重试
asyncio.run() 在脚本末尾别乱嵌套,3.11 的 top-level await 不适用这里
Python 3.11 支持模块级 await,但仅限于 REPL 或交互环境;写成脚本文件时,仍必须用 asyncio.run(main()) 启动,且只能调用一次。常见错误是在循环里反复调用 asyncio.run(),会触发 RuntimeError: asyncio.run() cannot be called from a running event loop。
另一个坑是把整个爬取逻辑包进一个函数再 await,却不考虑取消传播——比如用户 Ctrl+C 时,正在运行的 aiohttp 请求可能卡住不释放连接。
- 主入口保持扁平:
if __name__ == "__main__": asyncio.run(main()) - 在
main()内用asyncio.create_task()启动多个 worker 协程,而非await逐个执行 - 捕获
KeyboardInterrupt后,用asyncio.all_tasks()+task.cancel()主动清理,避免僵尸 task
真正难的不是写出让请求“看起来并行”的代码,而是让每个请求在失败、限流、超时、取消这些边界条件下,既不漏掉也不卡死——信号量、客户端配置、错误分类、事件循环生命周期,四个点缺一不可。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










