Selenium中ElementClickInterceptedException异常求助
解决ElementClickInterceptedException及Flashscore赛事抓取脚本优化
编写Selenium脚本抓取Flashscore每日赛事数据时,频繁触发ElementClickInterceptedException。流程为点击链接打开新窗口、抓取数据后关闭窗口,再处理下一个链接,但最多点击3次就报错。已尝试
wait.until(EC.element_to_be_clickable())和最大化窗口,问题仍未解决。原代码如下:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.common.exceptions import TimeoutException, NoSuchElementException, ElementNotInteractableException, StaleElementReferenceException from time import sleep l = "https://www.flashscore.com/" options = FirefoxOptions() #options.add_argument("--headless") driver = webdriver.Firefox(executable_path="geckodriver.exe", firefox_options=options) driver.install_addon('C:\\Windows\\adblock_plus-3.10.1-an+fx.xpi') driver.maximize_window() driver.get(l) driver.implicitly_wait(5) cnt = 0 sleep(5) wait = WebDriverWait(driver, 20) a = driver.window_handles[0] b = driver.window_handles[1] driver.switch_to.window(a) # Close Adblock tab if 'Adblock' in driver.title: driver.close() driver.switch_to.window(a) else: driver.switch_to.window(b) driver.close() driver.switch_to.window(a) var1 = driver.find_elements_by_xpath("//div[@class='leagues--live ']/div/div") knt = 0 for i in range(len(var1)): if (var1[i].get_attribute("id")): knt += 1 #sleep(2) #driver.switch_to.window(driver.window_handles) var1[i].click() sleep(2) #var2 = wait.until(EC.visibility_of_element_located((By.XPATH, "//div[contains(@classs, 'event__match event__match--last event__match--twoLine')]"))) print(len(driver.window_handles)) driver.switch_to.window(driver.window_handles[1]) try: sleep(4) driver.close() driver.switch_to.window(a) #sleep(3) except(Exception): print("Exception caught") #WebDriverWait(driver, 30).until(EC.visibility_of_element_located((By.CLASS_NAME, "event__match event__match--last event__match--twoLine"))) sleep(10) driver.close()
核心解决思路
- 禁止混合隐式/显式等待:隐式等待会与显式等待叠加,导致等待逻辑混乱,移除
driver.implicitly_wait(5),全程使用显式等待。 - 动态重新定位元素:页面可能动态刷新,初始获取的元素列表会失效(触发
StaleElementReferenceException),每次循环重新定位目标元素,而非复用初始列表。 - 确保元素无遮挡且可交互:用显式等待确认元素可点击,若仍被遮挡,改用JavaScript点击作为兜底方案。
- 安全处理窗口切换:打开新窗口后,等待窗口数量变化,再通过句柄差异筛选目标窗口,避免依赖固定索引。
- 替换sleep为显式等待:sleep是不稳定的等待方式,改用等待元素可见、窗口数量变化等条件,提升脚本稳定性。
优化后的代码
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.common.exceptions import ( TimeoutException, NoSuchElementException, ElementNotInteractableException, StaleElementReferenceException, ElementClickInterceptedException ) # 初始化浏览器 options = FirefoxOptions() # options.add_argument("--headless") driver = webdriver.Firefox(executable_path="geckodriver.exe", firefox_options=options) driver.install_addon('C:\\Windows\\adblock_plus-3.10.1-an+fx.xpi') driver.maximize_window() wait = WebDriverWait(driver, 20) # 打开目标站点 driver.get("https://www.flashscore.com/") # 关闭Adblock新增标签页 initial_windows = driver.window_handles if len(initial_windows) > 1: for handle in initial_windows: driver.switch_to.window(handle) if "Adblock" in driver.title: driver.close() # 切回主窗口 driver.switch_to.window([h for h in driver.window_handles if h != handle][0]) # 循环处理赛事链接 knt = 0 while True: try: # 每次循环重新定位元素,避免过时引用 league_items = wait.until(EC.presence_of_all_elements_located( (By.XPATH, "//div[@class='leagues--live ']/div/div[@id]") )) if knt >= len(league_items): break target_item = league_items[knt] knt += 1 # 等待元素可点击,尝试常规点击,失败则用JS点击 try: wait.until(EC.element_to_be_clickable(target_item)).click() except ElementClickInterceptedException: driver.execute_script("arguments[0].click();", target_item) # 等待新窗口打开并切换 wait.until(lambda d: len(d.window_handles) == 2) new_window = [h for h in driver.window_handles if h != driver.current_window_handle][0] driver.switch_to.window(new_window) # 这里添加抓取数据的逻辑,示例用等待元素可见 wait.until(EC.presence_of_element_located((By.CLASS_NAME, "event__match"))) # 关闭新窗口并切回主窗口 driver.close() driver.switch_to.window(driver.window_handles[0]) except StaleElementReferenceException: # 元素过时则重新开始循环,重新定位元素 continue except TimeoutException: print("等待超时,终止循环") break driver.quit()
代码关键优化点
- 移除隐式等待:仅使用
WebDriverWait显式等待,避免等待逻辑冲突。 - 动态元素定位:每次循环重新获取
league_items,解决页面刷新导致的元素过时问题。 - 双重点击策略:先尝试常规点击,触发拦截异常时用JavaScript直接触发点击,绕过遮挡。
- 安全窗口切换:通过窗口数量变化确认新窗口打开,再通过句柄差异筛选目标窗口,避免索引依赖。
- 异常处理:捕获
StaleElementReferenceException后重新定位元素,提升鲁棒性。
内容的提问来源于stack exchange,提问作者Hugh Heimler
相关产品推荐
相关产品推荐

