Python中Selenium StaleElementReferenceException问题排查求助
我在完成Angela的《100 Days of Python》课程项目,用Selenium持续点击http://orteil.dashnet.org/experiments/cookie/页面的Cookie,每5秒检查右侧升级项并购买当前能负担的最贵升级。但运行代码时随机抛出以下错误:
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: element is not attached to the page document
我的代码如下:
from selenium import webdriver from selenium.webdriver.chrome.service import Service import time service = Service("~/Downloads/chromedriver_mac64/chromedriver") driver = webdriver.Chrome(service=service) driver.get("http://orteil.dashnet.org/experiments/cookie/") def format_items(): """Format the data in [{name, cost, element (to click)}]""" store_items = driver.find_elements("css selector", "#store div b") items_ = [] for store_item in store_items: if store_item.text != "": description = store_item.text.split(" - ") name = description[0] cost = int(description[1].replace(",", "")) element = store_item items_.append({"name": name, "cost": cost, "element": element}) return items_ cookie = driver.find_element("id", "cookie") def find_most_expensive(): affordable_upgrades = [] for item in items: cost = item["cost"] if cost <= cookie_count: affordable_upgrades.append(item) expensive_affordable_cost = 0 expensive_affordable_dict = {} for upgrade in affordable_upgrades: if upgrade["cost"] > expensive_affordable_cost: expensive_affordable_cost = upgrade["cost"] expensive_affordable_dict = upgrade return expensive_affordable_dict base = time.time() + 5 while True: cookie.click() items = format_items() if time.time() >= base: cookie_count = int(driver.find_element("id", "money").text.replace(",", "")) most_expensive_item = find_most_expensive() if len(most_expensive_item) != 0: most_expensive_item["element"].click() base += 5 driver.quit()
我已在循环中每次调用format_items()重新获取元素,也查阅过相关文章但未解决,恳请帮忙排查错误原因。
错误原因及修复方案
核心原因
你遇到的StaleElementReferenceException是因为:当点击升级项后,页面的商店区域(#store)会重新渲染(比如新增升级项、更新已有项的状态),导致之前获取的WebElement对象与当前DOM文档脱节,引用失效。哪怕每次循环调用format_items(),在两次检查升级的间隔内,页面可能已经发生更新,导致后续要点击的元素引用过期。
具体问题点
- 直接保存WebElement对象到字典中,该对象的生命周期与DOM绑定,页面更新后引用立即失效。
- 每次点击Cookie后都调用
format_items(),频繁获取元素但未及时使用,增加了元素引用过期的概率。 - 原代码中点击的是
<b>标签元素,而页面实际可点击的升级区域是其父<div>标签,点击<b>可能触发不稳定的DOM更新。
修复方案
- 不保存WebElement,改用元素定位标识:比如保存每个升级项的父div的索引或CSS选择器,需要点击时再重新查找元素,确保引用最新。
- 调整
format_items()逻辑:获取每个升级项的父div,提取文本信息并保存定位方式,而非元素本身。 - 优化获取时机:只在需要检查升级的5秒间隔到来时,才获取最新的升级项数据,避免无效的元素引用。
修改后的代码
from selenium import webdriver from selenium.webdriver.chrome.service import Service import time service = Service("~/Downloads/chromedriver_mac64/chromedriver") driver = webdriver.Chrome(service=service) driver.get("http://orteil.dashnet.org/experiments/cookie/") def format_items(): """Format the data in [{name, cost, index}]""" store_divs = driver.find_elements("css selector", "#store div") items_ = [] for index, div in enumerate(store_divs): b_element = div.find_element("tag name", "b") text = b_element.text.strip() if text != "": description = text.split(" - ") name = description[0] cost = int(description[1].replace(",", "")) items_.append({"name": name, "cost": cost, "index": index}) return items_ cookie = driver.find_element("id", "cookie") def find_most_expensive(available_items, current_cookies): affordable_upgrades = [item for item in available_items if item["cost"] <= current_cookies] if not affordable_upgrades: return None # 按成本降序排序,取第一个 affordable_upgrades.sort(key=lambda x: x["cost"], reverse=True) return affordable_upgrades[0] base = time.time() + 5 while True: cookie.click() if time.time() >= base: cookie_count = int(driver.find_element("id", "money").text.replace(",", "")) items = format_items() most_expensive_item = find_most_expensive(items, cookie_count) if most_expensive_item: # 根据索引重新获取对应div并点击 target_div = driver.find_elements("css selector", "#store div")[most_expensive_item["index"]] target_div.click() base += 5 driver.quit()
代码说明
format_items()现在保存每个升级项的父div索引,而非WebElement,避免引用过期。find_most_expensive()改用列表推导和排序简化逻辑,更清晰。- 仅在5秒检查间隔到来时才获取
items和cookie_count,减少不必要的DOM操作,降低元素失效概率。 - 点击升级项时,通过索引重新查找父div元素,确保每次点击的都是当前DOM中有效的元素。
内容的提问来源于stack exchange,提问作者Daniel Crompton
相关产品推荐
相关产品推荐

