为什么Python中使用concurrent.futures搭配BS4没有优化运行速度?
问题原因分析
executor.map的传参逻辑错误executor.map(func, iterable)会将可迭代对象的每个元素单独作为参数传入目标函数执行。你当前代码将page_list直接传入map,相当于每次给get_url传的是单个URL字符串,而函数内部还在遍历data参数,就会遍历单个字符串的每个字符,发起的请求自然无效,最终url_list没有结果。单独调用get_url(page_list)时你传入的是完整的URL列表,遍历逻辑正常所以能拿到结果。- 代码缺少必要依赖导入
你使用了requests.get但代码开头没有导入requests库,运行时会抛出导入异常。 submit方法无提速的核心原因
大概率是你在每次调用submit后立刻调用了.result()方法,相当于提交一个任务就阻塞等待结果,本质还是串行执行,没有用到多线程的并发能力。- 全局变量的使用不规范
多线程场景下尽量避免直接操作全局变量,虽然Python列表的append是原子操作不会出现数据竞争,但代码可读性和可维护性很差,更推荐让每个线程返回自己抓取的结果,最后统一合并。
修正后的代码示例
# 导入所有必要依赖 import requests import concurrent.futures from bs4 import BeautifulSoup def page_builder(amount): page_list = [] for i in range(1, amount): url = 'https://quotes.toscrape.com/page/' + str(i) + '/' page_list.append(url) print('---- URL LIST SUCCESSFULLY CREATED ----') return page_list # 修改为接收单个URL,返回当前页面抓取的作者列表 def get_url(url): try : r = requests.get(url) soup = BeautifulSoup(r.content, 'html.parser') titles = soup.find_all("small", {"class": "author"}) return [title.text for title in titles] except Exception as e: print(f"Issue is --> {e}") return [] page_list = page_builder(10) url_list = [] with concurrent.futures.ThreadPoolExecutor() as executor : # map返回的是每个函数执行的结果迭代器,直接遍历合并即可 for result in executor.map(get_url, page_list): url_list.extend(result) print(url_list)
如果要使用submit方法,正确的并发写法如下:
with concurrent.futures.ThreadPoolExecutor() as executor: # 先提交所有任务,不阻塞等待结果 futures = [executor.submit(get_url, url) for url in page_list] # 统一等待任务完成后收集结果 for future in concurrent.futures.as_completed(futures): url_list.extend(future.result())
内容的提问来源于stack exchange,提问作者Julien Roche
相关产品推荐
相关产品推荐

