Python Selenium WebDriverWait偶发返回异常问题排查求助
Selenium代码不稳定问题排查与修复
报错原因分析
- Selenium版本兼容问题:代码中使用的
find_element_by_id、find_elements_by_css_selector是Selenium 3的旧API,在Selenium 4+版本中已被移除,运行时会偶发调用失败 - 弹窗遮挡元素:目标网站首次访问会弹出cookie同意框,未处理的情况下会遮挡搜索框,导致输入、点击操作偶发无法执行
- 元素引用失效:提前获取的全量产品列表,在页面DOM动态更新(懒加载、广告插入、DOM重绘)后引用会失效,遍历操作时会抛出
StaleElementReferenceException - 异常处理逻辑不全:主容器加载失败的分支里仅执行了
driver.quit(),没有终止后续代码执行,后续操作空引用、已销毁的driver都会触发报错 - 文本解析无边界判断:非Apple产品的名称如果没有
cu关键字,raw_name.find("cu")会返回-1,计算得到的索引是负数,字符串切片会触发索引越界异常 - 元素等待逻辑漏洞:遍历单个产品时用
WebDriverWait(product, 10)等待子元素,当product本身已经失效的情况下,等待逻辑完全不生效
修复方案
- 统一使用Selenium 4标准的
find_element(By.xxx, 选择器)写法,兼容所有主流版本 - 进入网站后先处理cookie同意弹窗,避免元素被遮挡导致操作失败
- 放弃预获取全量产品列表,改为按索引动态获取单个产品元素,每次获取都是最新的DOM引用,彻底避免stale元素异常
- 全局包裹异常捕获逻辑,所有失败场景统一处理,最终无论是否成功都关闭driver,避免资源泄漏
- 给文本解析增加边界判断,找不到指定关键字时 fallback 取完整名称,避免索引越界
- 所有元素等待统一使用driver层级的显性等待,确保等待逻辑稳定生效
修复后完整代码
from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time # 可选:使用webdriver-manager自动管理chromedriver,不需要手动指定路径 # 不想安装的话可以注释掉下面两行,换回你原来的路径写法即可 from webdriver_manager.chrome import ChromeDriverManager driver = webdriver.Chrome(ChromeDriverManager().install()) # 原手动指定路径的Selenium4兼容写法: # Path = "C:\Program Files (x86)\chromedriver.exe" # driver = webdriver.Chrome(executable_path=Path) driver.get("https://www.emag.ro/") driver.maximize_window() try: # 处理cookie同意弹窗 WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//button[text()='Accept']")) ).click() time.sleep(1) # 操作搜索框 search_bar = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.ID, "searchboxTrigger")) ) search_bar.send_keys("laptopuri") search_bar.send_keys(Keys.RETURN) # 等待主容器加载 main = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "main-container")) ) print("Page loaded,main retrived succesfully") # 等待产品列表容器加载 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "card_grid")) ) # 先获取产品总数量 products_count = len(driver.find_elements(By.CSS_SELECTOR, "div.card-item.js-product-data")) count = 0 # 按索引遍历,每次重新获取元素避免stale异常 for i in range(products_count): product = driver.find_elements(By.CSS_SELECTOR, "div.card-item.js-product-data")[i] # 等待名称元素加载 raw_name = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, "h2.card-body.product-title-zone")) ).text # 等待价格元素加载 raw_price = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "product-new-price")) ).text # 解析产品名,增加边界判断 raw_name = raw_name.replace("Laptop", "").strip() if raw_name.startswith("Apple"): sEnd = raw_name.find(",") else: sEnd = raw_name.find("cu") - 1 product_name = raw_name[:sEnd] if sEnd > 0 else raw_name # 解析价格,增加边界判断 space_idx = raw_price.find(" ") parsed_price = raw_price[:space_idx] if space_idx > 0 else raw_price print(f"产品名:{product_name},价格:{parsed_price}") count += 1 print(f"{count} results returned") except Exception as e: print(f"运行出错:{str(e)}") finally: driver.quit()
内容的提问来源于stack exchange,提问作者Dragos Ciupe
相关产品推荐
相关产品推荐

