You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过多进程加速Selenium爬虫?动态页面爬取优化咨询

问题背景
  • 核心需求:从批量动态加载网站的URL列表中,爬取单个目标数据点(对应页面的最便宜机票价格)
  • 现有方案痛点:基于Selenium实现的单线程爬虫运行速度过慢,无法满足效率要求
  • 已尝试方案的局限性:
    • 原生Scrapy框架不支持直接爬取需要JS渲染的动态页面
    • Scrapy对接Splash的方案仅适配单动态页加载后解析的场景,无法支撑海量URL的批量爬取需求
  • 当前待确认方向:考虑使用multiprocessing多进程方案优化,但不清楚具体上手路径,也不确定该方案能否和Selenium良好兼容

现有待优化的爬取代码

def get_cost(url):
driver.get(url)
try:
    element = WebDriverWait(driver, 4).until(
        EC.presence_of_element_located((By.XPATH,'/html/body/c-wiz[2]/div/div[2]/c-wiz/div/c-wiz/c-wiz/div[2]/div[2]/ul[1]/li[1]/div/div[2]/div/div[9]/div[2]/span'))
    )
    cost = element.get_attribute('textContent')
except:
    cost = "-"
finally:
    driver.quit()
return cost
可落地的优化方案

第一步:先修复现有代码的基础问题

你当前的代码本身就不支持并发运行,先调整这几个问题:

  • 不要使用全局共享的单个driver实例,多任务并发时会出现实例抢占,导致大量报错
  • 不要在单个URL爬取完成后就执行driver.quit(),浏览器启动/销毁的开销占比很高,频繁启停会大幅拉低爬取速度
  • 你现在用的是从根节点开始的绝对XPath定位,只要页面前端稍微调整结构就会定位失败,建议改成基于元素属性、相邻固定文本的相对XPath,稳定性更高
  • 启动Selenium时不要用完整的默认浏览器配置,加上无头模式、禁用图片/扩展/不必要插件的启动参数,单页加载速度可以提升40%以上

第二步:多进程方案适配Selenium(完全兼容,上手成本低)

multiprocessing和Selenium的兼容性很好,比多线程方案更稳定——受Python GIL锁限制,爬取这类IO密集型任务用多进程的资源利用率更高。
核心实现逻辑:每个子进程初始化时创建独立的Selenium driver实例,进程内的所有爬取任务复用这个实例,等进程所有任务跑完再统一销毁driver,避免频繁启停浏览器的开销。
参考实现代码:

from multiprocessing import Pool
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.chrome.options import Options

def init_driver():
    # 每个子进程初始化专属driver
    global driver
    chrome_opt = Options()
    # 无头模式,不弹出浏览器窗口
    chrome_opt.add_argument("--headless=new")
    chrome_opt.add_argument("--disable-gpu")
    chrome_opt.add_argument("--no-sandbox")
    chrome_opt.add_argument("--disable-dev-shm-usage")
    # 禁用图片加载,提速
    chrome_opt.add_argument("blink-settings=imagesEnabled=false")
    chrome_opt.add_argument("--disable-extensions")
    driver = webdriver.Chrome(options=chrome_opt)

def get_cost(url):
    driver.get(url)
    try:
        element = WebDriverWait(driver, 4).until(
            EC.presence_of_element_located((By.XPATH,'/html/body/c-wiz[2]/div/div[2]/c-wiz/div/c-wiz/c-wiz/div[2]/div[2]/ul[1]/li[1]/div/div[2]/div/div[9]/div[2]/span'))
        )
        cost = element.get_attribute('textContent')
    except:
        cost = "-"
    return cost

def close_driver():
    # 进程任务全部完成后再关闭driver
    driver.quit()

if __name__ == "__main__":
    # 替换成你的URL列表
    url_list = ["url1", "url2", "url3"]
    # 进程数建议设置为CPU核心数的1-2倍,不要开太大,避免资源抢占反而拖慢速度,也容易触发站点反爬
    with Pool(processes=4, initializer=init_driver) as pool:
        results = pool.map(get_cost, url_list)
        pool.apply(close_driver)
    print(results)

Windows系统下运行多进程代码,必须把进程池启动逻辑放在if __name__ == "__main__":代码块内,否则会出现反复启动子进程的报错。

第三步:海量URL场景的进一步优化

如果你的URL量级达到十万甚至百万级,Selenium多进程的方案还是偏重,可以换更高性能的实现:

  • 优先抓包分析页面数据来源:绝大多数机票类站点的价格数据都是通过后端接口直接返回的,不需要渲染整个页面。如果能找到对应接口,直接用aiohttp异步请求接口,爬取速度是Selenium方案的几十上百倍,资源占用极低
  • 如果确实需要JS渲染环境,替换成Playwright/Pyppeteer的异步模式,相同硬件配置下的并发能力是Selenium多进程的3-5倍,资源占用更低
  • 不管用哪种方案,都要配置随机请求间隔、代理池、随机UA,避免短时间请求量过大被目标站点封禁IP,反而影响爬取效率

内容的提问来源于stack exchange,提问作者max

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 12:51:24