Selenium自动化Python(Web Scraping):解决Chrome页面自动刷新导致抓取中断及耗时过长问题
Selenium自动化Python(Web Scraping):解决Chrome页面自动刷新导致抓取中断及耗时过长问题
嘿,我之前做Python爬虫时也碰到过一模一样的糟心事——Chrome自动刷新打断抓取流程不说,整个过程慢得让人抓狂!给你分享几个我亲测有效的解决方案,分两部分解决你的问题:
一、搞定页面自动刷新的问题
1. 直接拦截刷新请求
我最常用的方法是通过Chrome DevTools协议拦截页面的刷新请求,从根源上阻止页面重载。具体是监听网络请求,当检测到当前页面的文档请求(也就是刷新操作)时直接取消它。代码示例如下:
from selenium import webdriver # 初始化Chrome浏览器 driver = webdriver.Chrome() # 启用Network监控 driver.execute_cdp_cmd("Network.enable", {}) def intercept_refresh(request): # 匹配当前页面的GET请求(也就是刷新) if request['request']['url'] == driver.current_url and request['request']['method'] == 'GET': # 取消这个刷新请求 driver.execute_cdp_cmd("Network.cancelRequest", {"requestId": request['requestId']}) print("拦截了一次页面自动刷新") else: # 其他请求正常放行 driver.execute_cdp_cmd("Network.continueRequest", {"requestId": request['requestId']}) # 设置请求拦截规则,只拦截文档类型的请求 driver.execute_cdp_cmd( "Network.setRequestInterception", {"patterns": [{"urlPattern": "*", "resourceType": "Document"}]} ) # 添加拦截事件监听 driver.add_event_listener("Network.requestIntercepted", intercept_refresh)
2. 检测刷新后自动恢复状态
如果拦截请求的方法不生效(比如有些网站的刷新是通过JS触发的特殊请求),那就退而求其次:检测页面是否刷新,一旦发现就自动恢复到之前的抓取状态。核心是利用StaleElementReferenceException——当页面刷新后,之前定位的元素会失效,抛出这个异常。代码示例:
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import StaleElementReferenceException # 记录初始页面URL和关键元素的定位器 original_url = driver.current_url target_locator = ("id", "your-target-element-id") def check_and_recover(): try: # 尝试访问关键元素,判断页面是否正常 WebDriverWait(driver, 5).until(EC.presence_of_element_located(target_locator)) except StaleElementReferenceException: # 页面刷新了,重新导航到原页面并恢复操作 driver.get(original_url) # 这里可以添加你需要恢复的步骤,比如重新滚动到目标位置、点击之前的按钮等 print("页面自动刷新了,已恢复抓取状态") # 在抓取过程中定期调用这个检测函数 # 比如每执行几步操作就检查一次 check_and_recover()
3. 禁用页面的JS刷新逻辑
有些网站的自动刷新是通过JS的setInterval或者location.reload()实现的,我们可以直接在页面中覆盖这些函数,让它们失效:
# 覆盖window.reload函数,让它什么都不做 driver.execute_script("window.reload = function() {};") # 清除所有已设置的定时器,防止定时刷新 driver.execute_script(""" var maxIntervalId = window.setInterval(function(){}, 1000); for(var i = 0; i < maxIntervalId; i++) { window.clearInterval(i); } """)
二、优化抓取速度,解决耗时过长问题
1. 禁用不必要的资源加载
页面加载图片、CSS、广告等资源会占用大量时间,如果你的抓取不需要这些,直接禁用它们:
from selenium import webdriver chrome_options = webdriver.ChromeOptions() # 禁用图片加载 prefs = {"profile.managed_default_content_settings.images": 2} chrome_options.add_experimental_option("prefs", prefs) # 禁用CSS(如果页面结构不依赖CSS的话,可选) chrome_options.add_argument("--disable-css") # 禁用非必要的JavaScript(如果抓取不需要JS渲染数据,可选) chrome_options.add_argument("--disable-javascript") # 用配置好的options启动浏览器 driver = webdriver.Chrome(options=chrome_options)
2. 使用无头模式运行Chrome
无头模式下Chrome不会渲染可视化界面,速度会快很多,而且占用资源更少:
chrome_options.add_argument("--headless=new") # 新版本Chrome推荐的无头模式 chrome_options.add_argument("--disable-gpu") # 禁用GPU加速,避免兼容性问题
3. 替换time.sleep()为显式等待
别再用固定的time.sleep()等待了!显式等待只会等到目标元素出现再继续,能大幅减少不必要的等待时间:
# 不好的写法:不管元素是否加载完成,硬等10秒 import time time.sleep(10) # 推荐的写法:最多等10秒,元素出现就立刻继续 element = WebDriverWait(driver, 10).until( EC.presence_of_element_located(("id", "target-element")) )
4. 批量抓取用多线程/多进程
如果要抓取多个页面,单线程效率太低,可以用多线程(注意每个线程要单独初始化一个Chrome实例):
from concurrent.futures import ThreadPoolExecutor from selenium import webdriver def scrape_single_page(url): # 每个线程单独创建driver chrome_options = webdriver.ChromeOptions() chrome_options.add_argument("--headless=new") driver = webdriver.Chrome(options=chrome_options) driver.get(url) # 这里写你的抓取逻辑 page_data = driver.find_element("tag name", "body").text driver.quit() return page_data # 要抓取的页面列表 target_urls = ["https://example.com/page1", "https://example.com/page2", "https://example.com/page3"] # 用线程池批量处理,max_workers根据你的机器性能调整 with ThreadPoolExecutor(max_workers=3) as executor: results = list(executor.map(scrape_single_page, target_urls)) print("所有页面抓取完成:", results)
备注:内容来源于stack exchange,提问作者Mayesh Mangyal
相关产品推荐
相关产品推荐

