requests-futures爬虫速度过慢问题排查及高性能替代方案咨询
先分析你当前代码的几个关键问题,这些是导致速度慢的主要原因,然后再聊替代方案:
一、修复当前requests-futures代码的问题
你的爬取速率只有0.5个请求/秒,和as_completed关系不大,反而代码里有几个明显的错误和优化点:
1. 多进程调用的致命错误
你这段代码里创建了multiprocessing.Process但没有调用start(),等于这些进程根本没运行,所有的解析任务都没执行,还白白消耗了资源。另外,在ThreadPool的线程里再开多进程完全没必要——解析HTML是CPU轻量型任务,直接在线程里处理就好,多进程的进程间通信反而会增加开销。
把这部分改成直接调用:
try: main_func(resp) except requests.exceptions.JSONDecodeError: logging.warning("[requests.custom.debug]: JSONDecodeError when parsing response")
或者更高效的方式是用FuturesSession的回调函数,让请求完成后自动触发解析,不用在as_completed里等待:
def handle_response(resp, *args, **kwargs): try: main_func(resp) except requests.exceptions.JSONDecodeError: logging.warning("[requests.custom.debug]: JSONDecodeError when parsing response") finally: resp.close() # 创建session的时候绑定回调 with FuturesSession(executor=ThreadPoolExecutor(max_workers=16)) as session: for url in url_list: proxy = random.choice(proxy_list).replace("https:/", "http:/") session.get(url, proxies={'http': proxy, 'https': proxy}, # 同一个代理,避免重复连接 headers={ 'User-Agent': str(ua.chrome), 'Accept': '*/*', 'Accept-Encoding': 'gzip, deflate, br', 'Connection': 'keep-alive', 'Content-Type': 'text/plain;charset=UTF-8', }, timeout=10, # 关键:设置超时,防止请求挂起 hooks={'response': handle_response})
2. 代理选择的优化
你之前给http和https各随机选一个代理,这会导致每个请求建立两个不同的连接,大大增加了TCP握手的开销。应该给同一个请求用同一个代理,减少连接建立的时间。
3. 增加超时与重试机制
没有设置timeout的话,某个请求卡住会拖慢整个线程池的效率。另外,添加重试机制可以处理临时的网络错误或代理失效:
from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry # 配置重试 retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503, 504], allowed_methods=["GET"] ) adapter = HTTPAdapter(max_retries=retry_strategy) with FuturesSession(executor=ThreadPoolExecutor(max_workers=16)) as session: session.mount("http://", adapter) session.mount("https://", adapter) # 后续请求代码...
4. 调整线程池大小
max_workers=8可能太小了,你可以尝试增加到16或32(根据目标网站的反爬强度调整,不要一下子设得太大,避免被封)。
二、比requests-futures性能更出色的替代工具
如果优化后还是达不到预期速度,这些工具的性能会更优:
1. aiohttp(异步HTTP客户端)
基于asyncio的异步HTTP库,是单线程异步模型,没有线程切换的开销,并发量可以轻松达到数百甚至上千,适合高并发爬取。示例代码框架:
import asyncio import aiohttp async def fetch(session, url, proxy): try: async with session.get(url, proxy=proxy, timeout=10) as resp: html = await resp.text() main_func(html) # 这里可以直接处理解析 except Exception as e: logging.warning(f"Error fetching {url}: {e}") async def main(): connector = aiohttp.TCPConnector(limit=100) # 控制并发连接数 async with aiohttp.ClientSession(connector=connector, headers=your_headers) as session: tasks = [] for url in url_list: proxy = random.choice(proxy_list) tasks.append(fetch(session, url, proxy)) await asyncio.gather(*tasks) if __name__ == "__main__": asyncio.run(main())
2. httpx
支持同步和异步两种模式,API设计和requests几乎一致,学习成本低,性能和aiohttp接近,还支持HTTP/2,适合现代网站的爬取。
3. Scrapy
专为大规模爬虫设计的框架,自带并发调度、去重、重试、反爬处理等功能,适合需要爬取大量页面的场景。它的底层是基于Twisted异步框架,性能非常出色,而且扩展性强,可以轻松添加中间件处理代理、UA池等。
总结
先修复当前代码中的错误(尤其是多进程没start、代理选择、超时设置),调整线程池大小,应该能大幅提升爬取速度。如果还是需要更高的并发,优先考虑aiohttp或httpx的异步模式,大规模爬取则选Scrapy。
内容的提问来源于stack exchange,提问作者Coder

