优化存在页面加载时间不一致问题的Selenium网页爬虫
优化存在页面加载时间不一致问题的Selenium网页爬虫
我太懂你这种闹心的情况了——CloudFront防护+动态渲染的页面本来就是爬虫的硬骨头,尤其是Selenium这种全浏览器渲染工具,动不动就因为反爬校验、资源加载波动导致速度忽快忽慢。咱们一步步拆解问题,看看怎么把稳定性提上来。
一、先定位可能的性能瓶颈
结合你遇到的driver.get()卡顿、等待超时、总时长波动这几个问题,核心原因大概率是这几个:
- CloudFront的动态校验:哪怕你改了UA、禁用了自动化标志,CloudFront有时候还是会触发隐形的JS挑战(比如指纹校验),这会让页面加载突然卡10-30秒。
- 不必要的资源拖慢加载:Selenium默认会加载页面所有资源(图片、广告、第三方脚本),这些资源的加载速度完全不受控,直接导致
driver.get()时长波动。 - 等待逻辑的盲区:你用
presence_of_element_located只判断了表格DOM存在,但有时候表格框架出来了,里面的内容还在异步加载,这时候后续解析也会出问题;或者CloudFront的校验JS还在跑,页面处于假加载状态,导致等待超时。 - 固定范围随机延迟的双刃剑:1.5-4秒的延迟看似安全,但如果CloudFront已经盯上你,这种规律化的延迟反而可能被识别,而且会叠加页面本身的加载慢问题。
二、结构改进:减少阻塞和无效等待
针对你的脚本,我整理了几个立竿见影的优化点:
1. 给Chrome“瘦个身”:禁用不必要的资源加载
直接修改create_driver函数的Chrome选项,砍掉所有非必需的资源加载,能大幅降低driver.get()的波动:
def create_driver(): options = Options() options.add_argument('--no-sandbox') options.add_argument('--disable-dev-shm-usage') # 新无头模式:更接近真实浏览器,比旧无头模式难被检测 options.add_argument('--headless=new') # 禁用所有非必需资源 options.add_argument('--disable-images') options.add_argument('--disable-css') options.add_argument('--disable-plugins') options.add_argument('--disable-extensions') options.add_argument('--disable-gpu') options.add_argument('--blink-settings=imagesEnabled=false') # 双重禁用图片 # 页面加载策略改为eager:DOM加载完成就返回,不用等所有资源加载完 options.page_load_strategy = 'eager' # UA池:每次随机选一个真实UA,比固定UA更安全 ua_list = [ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36" ] options.add_argument(f"user-agent={random.choice(ua_list)}") # 自动化特征隐藏 options.add_argument("--disable-blink-features=AutomationControlled") options.add_experimental_option("excludeSwitches", ["enable-automation"]) options.add_experimental_option("useAutomationExtension", False) driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()), options=options) driver.set_page_load_timeout(20) # 把超时时间从30秒缩到20,避免无意义等待 driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})") return driver
2. 改进等待逻辑:从“等DOM存在”到“等内容加载完成”
原来的presence_of_element_located太宽松了,改成等表格有真实数据行,避免空表格导致的无效解析:
# 替换原来的WebDriverWait代码 try: # 等待表格至少有3行(表头+2行数据),确保内容加载完成 WebDriverWait(driver, 15).until( lambda d: len(d.find_elements(By.CSS_SELECTOR, "#example tr")) > 2 ) # 加个0.5秒小延迟,确保异步内容完全渲染 time.sleep(0.5) except TimeoutException: print(f"❌ [{tc_code}] 表格内容加载超时") return []
3. 加重试机制:应对临时的CloudFront波动
偶尔的超时或403可能是CloudFront的临时校验,给crawl_vote_table加个重试逻辑,不用直接放弃:
def crawl_vote_table(driver, tc_code, url, max_retries=2): for retry in range(max_retries + 1): print(f" [{tc_code}] 检查中... (第{retry+1}次尝试)") # 随机延迟范围放大一点,避免规律化 time.sleep(random.uniform(1.0, 5.0)) try: driver.get(url) html = driver.page_source if is_cloudfront_403(html): print(f" [{tc_code}] CloudFront 403拦截 (第{retry+1}次尝试)") if retry == max_retries: return None continue # 等待表格加载完成(用上面改进后的等待逻辑) WebDriverWait(driver, 15).until( lambda d: len(d.find_elements(By.CSS_SELECTOR, "#example tr")) > 2 ) time.sleep(0.5) soup = BeautifulSoup(driver.page_source, 'html.parser') table = soup.find('table', {'id': 'example'}) if not table: return [] # 后面的解析逻辑和你原来的一致... return results except TimeoutException: print(f"❌ [{tc_code}] 页面加载超时 (第{retry+1}次尝试)") if retry == max_retries: return [] except WebDriverException as e: print(f"❌ [{tc_code}] 连接失败 (第{retry+1}次尝试): {str(e)}") if retry == max_retries: return None
三、要不要彻底换掉Selenium?
这得看你能不能挖到页面的隐藏API:
- 优先找API:你的目标页面是Oracle APEX框架(URL里的
f?p=103:26是典型特征),这类页面的表格数据通常是通过AJAX请求获取的。打开浏览器DevTools的Network标签,刷新页面,找带FSP_ORG_ID/FSP_LANG_ID参数的XHR/Fetch请求,大概率能直接拿到JSON格式的表格数据。如果能拿到API,用requests直接请求比Selenium快5-10倍,而且完全避开CloudFront的浏览器指纹校验。 - 实在找不到API再用Selenium:如果API有复杂的签名或加密,那只能继续用Selenium,但一定要按上面的优化点把浏览器缩到最轻量,减少波动。
最后小提醒
建议你先花10分钟抓包看看有没有隐藏API,这是性价比最高的优化。如果必须用Selenium,按上面的调整后,应该能把总时长波动控制在1分钟以内,超时和卡顿的概率也会大幅降低。要是还有问题,咱们再进一步调试CloudFront的指纹特征~




