使用Selenium爬取TradingView图表数据时无限循环遭遇StaleElementReferenceException异常的解决求助
解决Selenium爬取TradingView时的StaleElementReferenceException异常
你遇到的StaleElementReferenceException在TradingView这类实时动态更新的页面里特别常见——本质原因是页面元素会随着行情数据刷新被重新渲染,你之前定位到的元素引用会因为DOM结构变化而失效,哪怕用了显式等待也可能踩坑,因为presence_of_element_located只保证元素在DOM中存在,但不保证它是最新的、可交互的。
下面给你几个针对性的优化方案和修改后的代码:
核心优化方向
1. 改用visibility_of_element_located代替presence_of_element_located
presence_of_element_located仅检查元素是否存在于DOM中,但元素可能还没加载完成或者不可见,这时候获取文本大概率会失败。visibility_of_element_located会确保元素可见,更适合需要读取文本的场景。
2. 优化定位函数的逻辑
- 用
elif替代多个独立if,避免逻辑分支混乱 - 不要依赖全局变量
chrome,把driver作为参数传入,让函数更健壮 - 增加异常捕获后的提示,方便排查问题
3. 每次获取数据都重新定位元素
在无限循环中,永远不要复用之前的元素对象——下一次循环时,这个元素很可能已经被页面刷新替换了,必须重新定位才能拿到有效的引用。
4. 替换脆弱的绝对XPath
你用的绝对XPath(比如/html/body/div[11]/div/div[2]/div/...)非常容易失效,只要页面结构有微小变化(比如弹窗顺序、div层级调整)就会定位失败。建议用相对XPath,结合元素的class、属性等特征来定位。
修改后的完整代码示例
from selenium.common.exceptions import NoSuchElementException, StaleElementReferenceException 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 import webdriver from selenium.webdriver.common.keys import Keys import time def locate(driver, path, locator_type="xpath", timeout=5): """改进后的定位函数,传入driver,改用可见性检查""" ignored_exceptions = (NoSuchElementException, StaleElementReferenceException) try: if locator_type == "xpath": element = WebDriverWait(driver, timeout, ignored_exceptions=ignored_exceptions).until( EC.visibility_of_element_located((By.XPATH, path)) ) elif locator_type == "link_text": element = WebDriverWait(driver, timeout, ignored_exceptions=ignored_exceptions).until( EC.visibility_of_element_located((By.LINK_TEXT, path)) ) elif locator_type == "name": element = WebDriverWait(driver, timeout, ignored_exceptions=ignored_exceptions).until( EC.visibility_of_element_located((By.NAME, path)) ) else: raise ValueError(f"不支持的定位类型: {locator_type}") return element except Exception as e: print(f"定位元素失败: {e}") return None def login(driver): driver.get("https://in.tradingview.com/chart/iVucV9D0/") driver.maximize_window() sign_in_btn = locate(driver, "Sign in", "link_text") if sign_in_btn: sign_in_btn.click() # 替换绝对XPath为更稳定的相对路径(你需要根据实际页面调整) google_login_btn = locate(driver, "//span[contains(text(), 'Google')]/parent::div") if google_login_btn: google_login_btn.click() username_input = locate(driver, "username", "name") if username_input: username_input.send_keys("myemail") password_input = locate(driver, "password", "name") if password_input: password_input.send_keys("mypassword" + Keys.ENTER) time.sleep(3) confirm_btn = locate(driver, "//button[contains(text(), 'Continue')]") if confirm_btn: confirm_btn.click() def get_buy_price(driver): # 建议替换为更稳定的相对XPath(示例,需根据实际页面元素调整) price_element = locate(driver, "//div[contains(@class, 'supertrend-buy')]/span") if price_element and price_element.text != "n/a": try: return float(price_element.text) except ValueError: print("价格格式解析失败") return "na" return "na" def get_sell_price(driver): price_element = locate(driver, "//div[contains(@class, 'supertrend-sell')]/span") if price_element and price_element.text != "n/a": try: return float(price_element.text) except ValueError: print("价格格式解析失败") return "na" return "na" if __name__ == "__main__": driver_path = "D:\\Repositories\\Bot\\chromedriver v89.0.4389.23.exe" with webdriver.Chrome(driver_path) as chrome: login(chrome) while True: buy_price = get_buy_price(chrome) if buy_price != "na": print(f"Supertrend Buy detected, 价格: {buy_price}") # 执行后续逻辑 sell_price = get_sell_price(chrome) if sell_price != "na": print(f"Supertrend Sell Detected, 价格: {sell_price}") # 执行后续逻辑 # 加短延迟,避免频繁请求导致页面卡顿或触发反爬 time.sleep(1)
额外注意事项
- TradingView反爬机制:TradingView有严格的反爬措施,频繁的自动化操作可能会导致账号被限制或IP被封,建议控制请求频率,不要过于激进。
- ChromeDriver版本匹配:确保你的ChromeDriver版本和本地Chrome浏览器版本一致,版本不兼容也可能导致各种奇怪的异常。
- 元素定位优化:尽量用
ID、class、data-*属性等更稳定的定位方式,避免依赖绝对XPath或页面层级结构。
内容的提问来源于stack exchange,提问作者Shivansh Goel
相关产品推荐
相关产品推荐

