Python使用Selenium爬取下拉表格出现StaleElementException问题
问题原因
你遇到的是Selenium常见的StaleElementReferenceException(元素未附加到文档异常),触发的核心原因有两个:
- 你在循环开始前提前缓存了下拉框的
options集合,点击提交按钮触发页面刷新后,之前缓存的所有DOM元素引用全部失效,下一轮循环调用旧引用就会触发报错 - 页面刷新后,之前选中的单选按钮、下拉框选中状态全部重置,你没有重新定位选中就直接循环旧的option引用,进一步加剧了元素失效问题
修复方案
核心调整逻辑
- 不要提前缓存任何下拉框、选项集合,每次进入对应层级循环前,重新定位对应的页面元素
- 替换所有固定
time.sleep为显式等待,增加页面刷新完成校验,确保上一次提交后的页面刷新完全结束后,再执行下一轮定位操作 - 最外层加循环重置状态:每次提交页面刷新后,重新选中「PANCHAYAT SAMITY」单选按钮,再执行后续下拉框选择逻辑
修复后代码
###Purpose: To retrieve gp election data import time from selenium import webdriver from selenium.webdriver.support.ui import Select from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC # 初始化驱动 driver = webdriver.Chrome(executable_path = '/usr/local/bin/chromedriver') driver.get('http://wbsec.gov.in/results/panchayat_election_detailed_result?election_year=2013') driver.maximize_window() wait = WebDriverWait(driver, 10) # 先获取区县总数,避免每次刷新重复获取 # 首次定位单选按钮并点击 wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@value='PANCHAYAT SAMITY']"))).click() # 首次获取区县总数 districts_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'zilla_parishad')))) district_count = len(districts_select.options) # 遍历区县 for district_idx in range(district_count): # 每次进入区县循环,先重置单选按钮状态 wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@value='PANCHAYAT SAMITY']"))).click() # 重新获取区县下拉框,选择对应索引的区县 districts_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'zilla_parishad')))) district = districts_select.options[district_idx] district.click() # 获取当前区县下的块总数 blocks_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'panchayat_samity')))) block_count = len(blocks_select.options) # 遍历块 for block_idx in range(block_count): # 重新选区县(防止刷新后状态丢失,可根据实际情况判断是否需要省略) districts_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'zilla_parishad')))) districts_select.options[district_idx].click() # 重新获取块下拉框,选对应索引的块 blocks_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'panchayat_samity')))) block = blocks_select.options[block_idx] block.click() # 获取当前块下的选举日期总数 poles_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'election_date')))) pole_count = len(poles_select.options) # 遍历选举日期 for pole_idx in range(pole_count): # 重新选区县、块 districts_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'zilla_parishad')))) districts_select.options[district_idx].click() blocks_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'panchayat_samity')))) blocks_select.options[block_idx].click() # 重新获取日期下拉框,选对应索引的日期 poles_select = Select(wait.until(EC.presence_of_element_located((By.ID, 'election_date')))) pole = poles_select.options[pole_idx] pole.click() # 定位提交按钮点击 submit_btn = wait.until(EC.element_to_be_clickable((By.XPATH, "//input[@type='submit']"))) submit_btn.click() # 等待页面刷新完成:判断旧的提交按钮已经失效,说明页面已经开始刷新 wait.until(EC.staleness_of(submit_btn)) # 等待新页面的提交按钮加载完成,说明刷新结束 wait.until(EC.presence_of_element_located((By.XPATH, "//input[@type='submit']"))) # 这里加你提取表格数据的逻辑 # **********
补充说明
如果还是偶发报错,可以适当调大显式等待的最长时长(代码里是10秒,可根据网络情况改到15-20秒),不需要加额外的time.sleep。
内容的提问来源于stack exchange,提问作者ak7880
相关产品推荐
相关产品推荐

