使用concurrent.futures时如何保持返回列表与原始输入顺序一致
解决方案
问题原因
你当前结果顺序随机的核心原因是concurrent.futures.as_completed()会按任务完成的先后顺序返回future对象,和任务提交的顺序无关,所以直接按返回顺序追加结果,自然和原始URL列表的顺序不匹配。
方案1:使用executor.map()(最简洁的标准实现)
executor.map()是官方提供的专门针对批量顺序返回的接口,返回结果的顺序和传入的可迭代对象(这里就是你的URL列表)顺序完全一致,代码改动量最小。
修改后的完整代码如下:
from urllib.request import urlopen from bs4 import BeautifulSoup import concurrent.futures # 可根据自身需求调整线程数 CONNECTIONS = 8 archive_url_list = ['https://web.archive.org/web/20171220002410/http://www.manueldrivingschool.co.uk:80/areas-covered-for-driving-lessons', 'https://web.archive.org/web/20210301102140/https://www.manueldrivingschool.co.uk/contact.php', 'https://web.archive.org/web/20210301102140/https://www.manueldrivingschool.co.uk/contact.php', 'https://web.archive.org/web/20171220002415/http://www.manueldrivingschool.co.uk:80/contact', 'https://web.archive.org/web/20160520140505/http://www.manueldrivingschool.co.uk:80/about.php', 'https://web.archive.org/web/20180102123922/http://www.manueldrivingschool.co.uk:80/about'] def get_archive_h1(h1_url): try: html = urlopen(h1_url) bsh = BeautifulSoup(html.read(), 'lxml') return bsh.h1.text.strip() except Exception: return "No Data Received!" def concurrent_calls(): with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor: # map返回的结果顺序和archive_url_list完全对应 return list(executor.map(get_archive_h1, archive_url_list)) if __name__ == '__main__': archive_h1_list = concurrent_calls() print(archive_h1_list)
方案2:绑定索引提交任务(适合需要自定义任务控制的场景)
如果你需要使用submit+as_completed的组合做更灵活的任务控制(比如实时打印完成进度),可以在提交任务时绑定每个future对应的原始索引,任务完成后把结果写入结果列表的对应位置即可保证顺序。
仅需要修改concurrent_calls函数部分:
def concurrent_calls(): # 先初始化和URL列表等长的结果列表 archive_h1_list = [None] * len(archive_url_list) with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor: # 提交任务时建立future和原始索引的映射关系 future_map = {executor.submit(get_archive_h1, url): idx for idx, url in enumerate(archive_url_list)} for future in concurrent.futures.as_completed(future_map): # 获取当前future对应的原始索引 current_idx = future_map[future] try: data = future.result() except Exception: data = "No Data Received!" # 把结果写入对应索引位置 archive_h1_list[current_idx] = data return archive_h1_list
内容的提问来源于stack exchange,提问作者Lee Roy
相关产品推荐
相关产品推荐

