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

使用Selenium爬取Kayak航班页面报错:element is not attached to the page document

解决Selenium中"stale element reference"报错问题

这个报错我太熟悉了,本质是你之前获取的元素引用已经失效——简单说就是这些元素已经从页面的DOM结构里被移除或者更新了,而你的代码还在尝试用旧的引用去操作它们。针对Kayak这个场景,大概率是页面在你循环处理航班列表时发生了动态更新(比如Kayak会自动刷新航班数据、加载更多内容,或者弹出Cookie提示改变了页面结构)。

报错核心原因

  • 你提前获取了list_flights这个元素列表,但在循环过程中,页面DOM发生了变化(比如刷新、动态加载),导致列表里的每个元素都和当前页面"脱节"了,此时再调用flight.find_elements_by_css_selector就会触发这个错误。
  • 另一种可能是你没有等待页面完全加载完成就开始操作元素,元素还没稳定就被你获取了引用。

针对性解决办法

我给你调整了代码,结合Kayak的动态特性做了优化:

1. 先导入必要的等待模块

from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import StaleElementReferenceException

2. 修改核心逻辑,避免复用失效元素引用

wd = webdriver.Chrome('chromedriver', chrome_options=chrome_options)
link = 'https://www.kayak.it/flights/MIL-BCN/2021-09-01/2021-09-02/?sort=bestflight_a'
wd.get(link)
import pprint
detail_flights = []

# 初始化等待器,最长等待10秒
wait = WebDriverWait(wd, 10)

# 先处理Kayak常见的Cookie弹窗(如果有的话),避免弹窗干扰DOM
try:
    cookie_btn = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "button[data-testid='accept']")))
    cookie_btn.click()
except:
    pass  # 没有弹窗就跳过

# 等待航班列表完全加载(这里的.flight需要替换成你原来获取list_flights的实际选择器)
wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".flight")))

# 改用索引循环,每次循环都重新获取最新的航班元素列表
flight_elements = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".flight")))
for i in range(len(flight_elements)):
    try:
        # 每次循环都重新获取当前索引的航班元素,确保引用是最新的
        current_flight = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".flight")))[i]
        # 获取航班信息,处理元素不存在的情况
        main_info = current_flight.find_element(By.CSS_SELECTOR, ".mainInfo").text if current_flight.find_elements(By.CSS_SELECTOR, ".mainInfo") else ""
        detail_flights.append({'flights': main_info})
    except StaleElementReferenceException:
        # 遇到失效引用时,重新获取一次元素
        current_flight = wait.until(EC.presence_of_all_elements_located((By.CSS_SELECTOR, ".flight")))[i]
        main_info = current_flight.find_element(By.CSS_SELECTOR, ".mainInfo").text if current_flight.find_elements(By.CSS_SELECTOR, ".mainInfo") else ""
        detail_flights.append({'flights': main_info})

len(detail_flights)
pprint.pprint(detail_flights[0:4])

关键优化点说明

  • 避免提前存储元素列表:不要把list_flights提前存成变量,而是每次循环都重新获取最新的元素集合,确保引用和当前DOM一致。
  • 强制等待元素稳定:用WebDriverWait替代直接操作元素,确保航班列表完全加载后再开始处理,同时处理可能的Cookie弹窗(这是Kayak常见的DOM变化触发点)。
  • 捕获异常重试:针对StaleElementReferenceException添加捕获逻辑,遇到失效引用时重新获取元素,提升代码稳定性。

另外补充一点:Kayak有反爬机制,如果你频繁请求可能会触发页面刷新或者验证,建议在循环中添加随机延迟(比如time.sleep(random.uniform(1,3))),降低被检测的概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 07:19:04