Python 3:使用requests、asyncio和concurrent的多线程请求示例
关于并行请求方案合理性的分析
嘿,这个思路本身是站得住脚的,但得结合你具体用的是asyncio还是concurrent.futures,以及细节处理来判断是否够合理——咱们拆开来唠:
核心合理性基础
不管是用asyncio的Task futures,还是concurrent.futures的线程/进程池futures,针对网络请求这种IO密集型任务做并行处理,都是完全正确的方向:网络请求大部分时间在等响应,并行能把空闲资源利用起来,大幅提升效率。
分场景的细节优化建议
1. 如果你用的是asyncio(异步方案)
- 最优搭配:一定要用异步HTTP库(比如
aiohttp),别用同步的requests——后者会直接阻塞事件循环,等于白搞异步。 - 必须加的限制:给并发数加个Semaphore(比如限制10-20个并发),不然一下子发几百个请求,要么被目标服务器封IP,要么自己的程序扛不住。
- 错误处理不能少:直接调用
future.result()会在请求失败时抛出异常搞崩程序,建议用asyncio.gather(return_exceptions=True)来捕获异常,后续再过滤无效结果。
举个优化后的小例子:
import asyncio import aiohttp async def getUserId(session, name): async with session.get(f"https://example.com/users/{name}") as resp: resp.raise_for_status() # 主动抛出HTTP错误 return (await resp.json()).get("id") async def main(names): # 限制并发数,避免请求过猛 sem = asyncio.Semaphore(15) async with aiohttp.ClientSession() as session: async def bounded_request(name): async with sem: return await getUserId(session, name) # 创建所有任务,批量执行并捕获异常 tasks = [asyncio.create_task(bounded_request(name)) for name in names] results = await asyncio.gather(*tasks, return_exceptions=True) # 过滤掉异常结果 valid_ids = [res for res in results if not isinstance(res, Exception)] return valid_ids if __name__ == "__main__": user_names = ["alice", "bob", "charlie"] print(asyncio.run(main(user_names)))
2. 如果你用的是concurrent.futures(线程/进程池)
- 选对池类型:网络请求是IO密集型,用
ThreadPoolExecutor就够了,ProcessPoolExecutor完全没必要——进程启动开销大,反而拖慢效率。 - 线程数要合理:
max_workers别设得太大(比如10-20就行),太多线程会增加切换开销,反而得不偿失。 - 错误处理同样重要:在
getUserId里加try-except,或者在取future.result()时捕获异常,避免单个请求失败导致整个流程卡壳。
示例代码参考:
import concurrent.futures import requests def getUserId(name): try: resp = requests.get(f"https://example.com/users/{name}") resp.raise_for_status() return resp.json().get("id") except Exception as e: print(f"Failed to fetch ID for {name}: {str(e)}") return None def main(names): with concurrent.futures.ThreadPoolExecutor(max_workers=15) as executor: futures = [executor.submit(getUserId, name) for name in names] # 可以用as_completed按完成顺序取结果,也直接用列表推导式 results = [future.result() for future in concurrent.futures.as_completed(futures)] valid_ids = [res for res in results if res is not None] return valid_ids if __name__ == "__main__": user_names = ["alice", "bob", "charlie"] print(main(user_names))
总结
你的方案核心逻辑是合理的,但要注意以上这些细节优化——尤其是并发限制和错误处理,不然很容易出现“看起来能跑,但一遇到异常或者高并发就崩”的情况。如果是追求极致效率,优先选asyncio + aiohttp的异步方案;如果是想快速实现、依赖同步库,用ThreadPoolExecutor也完全没问题。
内容的提问来源于stack exchange,提问作者dpolicastro
相关产品推荐
相关产品推荐

