
asyncio并发请求 的核心逻辑
Python 中用 asyncio 发并发 HTTP 请求,关键不是“同时发 100 个”,而是让 IO 等待时不阻塞主线程——比如发请求后不等响应,立刻切去处理下一个请求或已返回的数据。真正起效的前提是:所有 IO 操作(如 HTTP 请求)都得是异步的,不能混用 requests 这类同步库。
用 aiohttp 替代 requests 发异步请求
aiohttp是 asyncio 生态中最常用的异步 HTTP 客户端。它本身基于 async/await 设计,能自然融入事件循环。安装后直接用 session.get() 发起请求,配合 async with 管理连接生命周期,避免资源泄漏。
- 创建
aiohttp.ClientSession()一次,复用给多个请求,别每次新建 - 每个请求用
await session.get(url),不是session.get(url).text() - 响应体用
await resp.text()或await resp.json(),加await - 加
timeout参数防卡死,例如timeout=aiohttp.ClientTimeout(total=10)
控制并发数防止目标服务器压力过大
无限制并发(如 asyncio.gather(*tasks) 扔几百个任务)可能触发对方限流、封 IP,或耗尽本地文件描述符。推荐用 asyncio.Semaphore 做并发限流:
- 初始化一个信号量:
sem = asyncio.Semaphore(10),表示最多 10 个并发 - 每个请求前
async with sem:,自动排队获取许可 - 结合
asyncio.create_task()启动任务,比gather更灵活,便于错误隔离和重试
异常处理与重试不能省略
网络请求失败很常见:超时、连接拒绝、状态码非 2xx、JSON 解析失败……这些在异步里不会自动中断整个程序,但若不捕获,错误会被静默吞掉或导致后续逻辑出错。
立即学习“Python 免费学习笔记(深入)”;
- 每个
await session.get()外层套try/except aiohttp.ClientError - 检查
resp.status,非 2xx 时主动raise Exception(f"HTTP {resp.status}") - 重试建议用
tenacity库(支持 async),或手写带指数退避的async def fetch_with_retry() - 记录失败 URL 和原因,方便事后排查,别只打印然后跳过
完整最小可运行示例
以下代码实现 10 个并发请求,每秒最多 5 个,带基础错误处理:
import asyncio import aiohttp <p>async def fetch(session, url, sem): async with sem: try: async with session.get(url, timeout=5) as resp: if resp.status == 200: return await resp.text() else: raise Exception(f"HTTP {resp.status}") except Exception as e: return f"ERROR: {url} → {e}"</p><p>async def main(): urls = ["<a href="https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c">https://www.php.cn/link/5f69e19efaba426d62faeab93c308f5c</a>"] <em> 20 sem = asyncio.Semaphore(5) async with aiohttp.ClientSession() as session: tasks = [fetch(session, url, sem) for url in urls] results = await asyncio.gather(</em>tasks, return_exceptions=True) print(f" 完成 {len(results)} 个请求 ")</p><p>asyncio.run(main())</p>






























