You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何通过多进程加速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

火山引擎 最新活动