如何并行调用分页API?Python最优实现方案咨询
如何优雅地并行请求分页API(解决顺序请求慢的问题)
针对你的场景,最符合Python风格的实现方式是使用**标准库中的concurrent.futures.ThreadPoolExecutor**来做并行请求。原因很直观:
- 你的任务是IO密集型(网络请求),GIL不会成为瓶颈——当请求处于等待响应的状态时,Python会自动释放GIL,让其他线程继续运行。
- 线程池的开销远小于多进程,而且完全不需要处理多进程间的数据共享问题(这正是你担心的全局列表追加出错的根源)。
concurrent.futures封装了线程管理的所有细节,代码简洁易维护,完全符合Python的"优雅"原则。
改造后的代码示例
首先我们把单页请求的逻辑抽成独立函数,方便线程池调用:
import requests import concurrent.futures def fetch_single_page(token, offset, limit): """获取单页用户数据的独立函数""" url = f"https://example.com/def/v1/users?offset={offset}&limit={limit}" response = requests.get(url, headers={'Authorization': token}).json() return response["data"] def downloadUsers(token, totalUsers): limit = 200 # 计算所有需要请求的偏移量(按每页200条自动拆分) offsets = range(0, totalUsers, limit) allUsers = [] # 创建线程池,max_workers根据API限流情况调整(建议10-20) with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: # 提交所有分页请求任务 futures = [ executor.submit(fetch_single_page, token, offset, limit) for offset in offsets ] # 逐个获取完成的请求结果,统一在主线程合并数据 for future in concurrent.futures.as_completed(futures): try: page_data = future.result() allUsers.extend(page_data) except Exception as e: # 单个请求失败不影响全局,可根据需求添加重试或日志逻辑 print(f"请求偏移量{offset}失败: {str(e)}") # 后续处理逻辑保持不变 allUsers = doSomethingElse(allUsers) return allUsers
关键细节说明
- 彻底避免数据竞争:这里所有线程只负责请求并返回数据,列表的
extend操作完全在主线程中完成——不存在多个线程同时修改同一个列表的情况,从根源上杜绝了数据错误。 - 线程数合理控制:
max_workers不要设置得过大,否则可能触发API的限流机制(如果有的话),一般10-20个线程足以大幅提升请求速度,同时不会给服务器造成过大压力。 - 容错性提升:通过
try-except捕获单个请求的异常,确保某个分页请求失败不会导致整个任务终止,你还可以根据需求添加重试逻辑。 - 为什么不用多进程:多进程适合CPU密集型任务,而网络请求属于IO等待场景,线程池的效率更高、开销更小,代码也更简洁。如果非要用多进程,需要借助
multiprocessing.Queue或Manager来共享数据,反而增加了复杂度。
额外提示
如果API返回的has_more比预先知道的totalUsers更可靠(比如用户总数可能动态变化),可以先请求第一页获取最新的totalUsers,再生成偏移量列表,确保不会遗漏或多请求数据。
内容的提问来源于stack exchange,提问作者simplex123
相关产品推荐
相关产品推荐

