如何通过多进程加速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
相关产品推荐
相关产品推荐

