使用Python aiohttp批量解析链接如何实现并发而非串行执行?
问题原因分析
你现有代码存在3个核心问题导致无法实现并发:
- 执行逻辑为串行:for循环中每次发起请求后,都会等待当前请求响应完全返回才会处理下一个链接,6000个链接自然耗时极长
- 存在语法错误:
response.json()是异步方法,必须加await才能拿到实际返回值,原代码会直接打印协程对象而非接口返回内容 - 变量命名不规范:用了Python内置关键字
list作为变量名,会覆盖内置方法,容易引发未知问题
优化实现方案
核心思路是把每个链接的请求封装为独立协程任务,通过异步调度实现并发,同时增加并发数控制避免同时发起过多请求被目标服务限流或导致本地端口耗尽。
import aiohttp import asyncio # 替换原list变量名,避免和内置关键字冲突 url_params = ['value1', 'value2', 'value3', '.....', 'value6000'] # 最大并发数,可根据目标服务器耐受度调整,建议初始设置为10-50测试 MAX_CONCURRENT = 20 async def fetch(session: aiohttp.ClientSession, param: str): """单个请求的协程封装""" try: async with session.get(f"https://example.com/{param}") as response: # 补充await调用异步方法,正确获取JSON返回值 res = await response.json(content_type=None) print(f"参数{param}返回结果:", res) return res except Exception as e: print(f"参数{param}请求失败:", str(e)) return None async def main(): # 初始化信号量控制并发上限 semaphore = asyncio.Semaphore(MAX_CONCURRENT) # 复用ClientSession减少TCP握手、TLS协商开销,提升请求效率 async with aiohttp.ClientSession() as session: # 如果不需要无限循环解析,可删除while True语句块 while True: # 批量生成所有请求任务 tasks = [] for param in url_params: # 用信号量包裹请求逻辑,避免并发超限 async def bounded_fetch(p): async with semaphore: return await fetch(session, p) tasks.append(bounded_fetch(param)) # 并发执行所有任务 await asyncio.gather(*tasks) # 每次全量解析完成后可设置间隔,避免请求过于频繁,不需要可以删除 await asyncio.sleep(10) if __name__ == "__main__": # Python3.7+ 可以直接用以下方法运行,兼容旧版本可以替换为原loop写法 asyncio.run(main()) # 旧版本兼容写法: # loop = asyncio.get_event_loop() # loop.run_until_complete(main())
核心优化点说明
- 并发调度:将每个链接的请求封装为独立协程任务,通过
asyncio.gather批量调度,实现同时处理多个请求 - 流量控制:通过
asyncio.Semaphore限制同时发起的请求数量,避免触发目标服务器限流规则、或者本地端口被占满报错 - 容错提升:增加了单请求异常捕获逻辑,单个请求失败不会中断整体任务运行
- 性能优化:全程复用
ClientSession,减少重复建立连接的开销,进一步提升请求速度
内容的提问来源于stack exchange,提问作者ReD
相关产品推荐
相关产品推荐

