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

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()

代码关键优化点

  1. 移除隐式等待:仅使用WebDriverWait显式等待,避免等待逻辑冲突。
  2. 动态元素定位:每次循环重新获取league_items,解决页面刷新导致的元素过时问题。
  3. 双重点击策略:先尝试常规点击,触发拦截异常时用JavaScript直接触发点击,绕过遮挡。
  4. 安全窗口切换:通过窗口数量变化确认新窗口打开,再通过句柄差异筛选目标窗口,避免索引依赖。
  5. 异常处理:捕获StaleElementReferenceException后重新定位元素,提升鲁棒性。

内容的提问来源于stack exchange,提问作者Hugh Heimler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 22:15:35