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

Selenium Python循环中元素存在仍报StaleElementReferenceException问题

问题根因
  • 陈旧元素引用异常:仅在循环外获取了一次下一页按钮的元素对象,页面跳转后DOM刷新,旧的元素引用失效,点击时直接触发StaleElementReferenceException。
  • 循环判断逻辑失效:here变量仅在循环启动前赋值一次,后续页面跳转后没有重新校验下一页按钮状态,判断逻辑和实际页面状态完全脱节。
  • 等待逻辑鲁棒性差:大量硬编码的固定时长等待、写死的滚动像素值,无法适配TripAdvisor不同分页的加载节奏和布局差异,经常出现元素未加载完成、未滚动到可视区域就触发点击的问题。
修正后的实现代码
import unittest
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.common.exceptions import TimeoutException, StaleElementReferenceException
import time

URL = "https://www.tripadvisor.com/Hotels-g60763-New_York_City_New_York-Hotels.html"

class PythonOrgSearch(unittest.TestCase):
    
    def setUp(self):
        self.driver = webdriver.Chrome()
        # 配置显式等待,最长等待20秒,自动忽略临时的陈旧元素异常
        self.wait = WebDriverWait(
            self.driver, 
            20, 
            ignored_exceptions=[StaleElementReferenceException]
        )

    def test_search_in_python_org(self):
        driver = self.driver
        wait = self.wait
        driver.get(URL)
        self.assertIn("Hotel", driver.title)
        
        # 定位并点击"See all"按钮
        see_all_btn = wait.until(EC.presence_of_element_located(
            (By.XPATH, '//*[@id="component_6"]/div/button')
        ))
        driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", see_all_btn)
        time.sleep(1)
        wait.until(EC.element_to_be_clickable(
            (By.XPATH, '//*[@id="component_6"]/div/button')
        )).click()

        while True:
            try:
                # 每次循环重新查找下一页按钮,避免持有旧页面的失效元素引用
                next_btn = wait.until(EC.element_to_be_clickable(
                    (By.CSS_SELECTOR, "a.nav.next.ui_button.primary")
                ))
                # 滚动到按钮可视区域
                driver.execute_script("arguments[0].scrollIntoView({block: 'center'});", next_btn)
                time.sleep(1)
                # 点击下一页
                next_btn.click()
                # 等待旧按钮失效,确认页面完成跳转
                wait.until(EC.staleness_of(next_btn))
            except TimeoutException:
                # 找不到可点击的下一页按钮,说明到达最后一页,退出循环
                break

    def tearDown(self):
        # 测试结束自动关闭浏览器,避免残留进程占用资源
        self.driver.quit()

if __name__ == "__main__":
    unittest.main()
关键修改说明
  • 移除所有不可靠的固定像素滚动、长时长硬等待,替换为显式等待逻辑:只有当目标元素达到可交互状态时才执行操作,遇到临时的元素陈旧异常会自动重试查找,兼顾运行效率和稳定性。
  • 每次循环都重新定位下一页按钮,点击后等待旧按钮元素失效,确认页面完成跳转后再进入下一轮逻辑,从根源解决元素引用陈旧的问题。
  • 滚动操作改为直接定位到目标元素位置,适配不同分页的布局差异,不会出现滚动位置不对导致按钮不可见的问题。
  • 去掉原代码中一次性赋值的here判断,改为每次循环实时校验下一页按钮是否可点击,捕获到超时(找不到可点击按钮)就判定为爬取完所有分页,自动退出。
  • 移除了原代码中未使用的冗余导入,如果你后续需要做页面解析、数据存储,可以自行加回BeautifulSoup、pandas等对应的依赖库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:45:55