如何通过多进程加速Python网络爬虫代码的运行效率?
多进程优化Python网页爬取速度方案
嘿,我懂你现在的烦恼——串行爬取40个页面要花60秒确实效率太低了,多进程刚好能解决这类IO密集型任务的痛点,我来帮你把代码改好,还会给你讲清楚每一步的逻辑~
先看看你的原串行代码(已修正xpath转义问题)
import requests from lxml import html returns = [] for x in range(40, 80): url = f'https://www.mutualfundindia.com/MF/Performance/Details?id={x}' r = requests.get(url) tree = html.fromstring(r.content) # 修正了原代码里的"为双引号,否则xpath会报错 inception = tree.xpath('//*[@id="collPerformanceAnalysis"]/div/div[3]/div[7]') for i in inception: if i.text != ' ': returns.append(str.strip(i.text))
为什么串行这么慢?
这段代码是串行执行的:每发一个请求,都要等服务器返回数据后才能处理下一个,大部分时间CPU都在空闲等待网络响应,完全浪费了硬件资源。多进程可以让多个请求同时发送,把等待的时间利用起来,大幅缩短总耗时。
优化后的多进程代码
我用concurrent.futures.ProcessPoolExecutor来实现,这个模块比原生multiprocessing更简洁,上手更快:
import requests from lxml import html from concurrent.futures import ProcessPoolExecutor # 把单个页面的爬取逻辑封装成独立函数,每个进程会执行这个函数 def fetch_inception(x): url = f'https://www.mutualfundindia.com/MF/Performance/Details?id={x}' try: # 加个请求头模拟浏览器,降低被反爬的概率 headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36' } r = requests.get(url, headers=headers, timeout=10) r.raise_for_status() # 如果请求返回错误码,直接抛出异常 tree = html.fromstring(r.content) inception = tree.xpath('//*[@id="collPerformanceAnalysis"]/div/div[3]/div[7]') for i in inception: if i.text and i.text.strip(): # 更严谨的非空判断 return i.text.strip() return None # 如果没找到数据,返回None except Exception as e: print(f"爬取id={x}时出错: {str(e)}") return None if __name__ == '__main__': returns = [] # 创建进程池,max_workers是进程数,建议设为CPU核心数的2-4倍(比如4-8),别太激进避免被封 with ProcessPoolExecutor(max_workers=8) as executor: # 把range(40,80)的任务分配给进程池,map会自动收集结果 results = executor.map(fetch_inception, range(40, 80)) # 过滤掉None值,整理有效结果 returns = [res for res in results if res is not None] print(f"共获取到{len(returns)}条有效数据")
关键优化点说明
- 封装独立函数:把单页爬取逻辑做成
fetch_inception,让每个进程可以独立执行,避免全局变量的冲突。 - 进程池管理:用
ProcessPoolExecutor自动管理进程的创建和销毁,不用手动处理进程的启动/关闭逻辑。 - 异常处理:加入了请求超时、错误码捕获,避免单个请求失败导致整个程序崩溃,还能打印错误信息方便排查。
- 严谨的数据判断:把原代码的
if i.text!=' '改成if i.text and i.text.strip(),避免空文本或纯空格的情况。 - 请求头模拟:加了User-Agent,降低被网站识别为爬虫的概率。
注意事项
- 进程数别设太大:比如你CPU是4核,设8个进程就够了,太多进程可能会被网站封IP,也会增加本地系统负担。
- 可按需加延迟:如果网站反爬严格,在
fetch_inception里加个time.sleep(0.5),但多进程下即使加了延迟,总耗时还是会比串行少很多。 - 测试先小范围试:比如先爬
range(40,45)看看效果,没问题再扩大范围。
内容的提问来源于stack exchange,提问作者Kabir Raole




