Selenium下拉菜单操作报错及加拿大安卫报表批量下载求助
问题描述
我正在做一个和加拿大职业健康安全行业职业规划相关的趣味项目,顺便提升Python技能。目标是自动化获取加拿大职业健康安全统计数据网站上的所有报表组合:
- 与页面三个下拉菜单交互:年份、数据分类(性别、职业等)、事故类型(死亡事故、误工索赔LTC)
- 点击「生成」按钮加载表格后,导出为.csv文件
- 自动下载所有年份、分类、事故类型的组合报表
作为Python新手,尝试几天遇到了问题,当前代码运行时选择年份选项报错:UnexpectedTagNameException: Select only works on <select> elements, not on option。
当前代码:
# First, let's import the Selenium library and the required modules for this project. from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.ui import Select import time # Then lets specify the URL of interest and navigate to the webpage. url = "https://awcbc.org/en/statistics/national-work-injury-disease-and-fatality-statistics-nwisp-year-at-a-glance/" driver = webdriver.Chrome() driver.get(url) time.sleep(7) # The following helps locate the iframe that contains the drop-down menus needed. try: iframe = driver.find_element(By.XPATH, "//*[@id='res-inventry-page']/div/div/div/div/p[7]/iframe") driver.switch_to.frame(iframe) except: print("Failed to locate iframe") else: print("iframe successfully switched.") time.sleep(7) wait = WebDriverWait(driver, 10) # Wait for a maximum of 10 seconds # Define all the drop-down menus within the iframe and try and select one option in each. ddyear = driver.find_element(By.ID, "DropdownListYear") Select(ddyear) years = driver.find_elements(By.XPATH, "//select[@id='DropdownListYear']/option") years = years[1:] yr2022 = driver.find_element(By.XPATH, '//*[@id="DropdownListYear"]/option[2]') Select(yr2022) # for year in years: # print(year.text) category = driver.find_elements(By.ID, "DropdownReportType") categories = driver.find_elements(By.XPATH, '//*[@id="DropdownReportType"]/option') categories = categories[1:] result = driver.find_element(By.ID, "DropdownScope") results = driver.find_elements(By.XPATH, '//*[@id="DropdownScope"]/option') results = results[1:] generate_button = driver.find_element(By.XPATH, '//*[@id="generateReport"]') export_csv = driver.find_element(By.XPATH, '//*[@id="ExportExcelLB"]')
希望得到问题解决方法、循环遍历所有组合的实现建议,也可以接受先获取下载链接再用wget下载的方案,需要编写脚本的方向指导。
解决方案
1. 修复Select元素错误
你遇到的UnexpectedTagNameException是因为错误地把<option>元素传给了Select类,Select只能作用于<select>标签。正确用法是:
- 先获取对应的
<select>元素,实例化Select对象 - 通过
Select提供的方法选择选项,比如select_by_index()、select_by_value()或select_by_visible_text()
示例代码(替换你选年份的部分):
# 处理年份下拉 year_select = Select(driver.find_element(By.ID, "DropdownListYear")) # 选择2022年(index从0开始,第二个选项index是1) year_select.select_by_index(1) # 或者用文本选择:year_select.select_by_visible_text("2022")
2. 遍历所有报表组合的实现思路
步骤拆解:
- 获取三个下拉菜单的所有可选选项(排除第一个默认占位选项)
- 三重循环遍历所有年份、分类、事故类型的组合
- 每次循环中:
- 选择当前组合的三个选项
- 点击「生成」按钮,等待表格加载完成
- 点击「导出CSV」按钮,等待下载完成
- (可选)重命名下载文件,避免覆盖
优化建议:
- 用
WebDriverWait替代time.sleep,等待元素可交互/可见,提升稳定性 - 配置Chrome下载路径,避免每次确认保存位置
示例代码框架:
# 配置Chrome下载路径(可选,避免弹窗) options = webdriver.ChromeOptions() prefs = {"download.default_directory": "/your/download/path"} options.add_experimental_option("prefs", prefs) driver = webdriver.Chrome(options=options) # 切换到iframe后,初始化三个Select对象 year_select = Select(driver.find_element(By.ID, "DropdownListYear")) category_select = Select(driver.find_element(By.ID, "DropdownReportType")) scope_select = Select(driver.find_element(By.ID, "DropdownScope")) # 获取所有可选选项(排除第一个默认项) year_options = [opt for opt in year_select.options if opt.get_attribute("value")] category_options = [opt for opt in category_select.options if opt.get_attribute("value")] scope_options = [opt for opt in scope_select.options if opt.get_attribute("value")] # 三重循环遍历所有组合 for year_opt in year_options: year = year_opt.text year_select.select_by_visible_text(year) for cat_opt in category_options: category = cat_opt.text category_select.select_by_visible_text(category) for scope_opt in scope_options: scope = scope_opt.text scope_select.select_by_visible_text(scope) # 点击生成按钮,等待表格加载 generate_btn = wait.until(EC.element_to_be_clickable((By.ID, "generateReport"))) generate_btn.click() # 等待导出按钮可点击(表示表格加载完成) export_btn = wait.until(EC.element_to_be_clickable((By.ID, "ExportExcelLB"))) export_btn.click() # 等待文件下载完成(简单处理:根据文件后缀等待,或者检查下载目录) time.sleep(3) # 可选:重命名文件,比如 f"{year}_{category}_{scope}.csv"
3. 备选方案:获取下载链接后用wget下载
如果直接点击下载不稳定,可以通过获取导出按钮的下载链接,用wget或requests下载:
- 点击生成后,等待导出按钮加载
- 获取导出按钮的
href属性(可能是动态生成的链接) - 用
subprocess.run(["wget", url])或requests.get保存文件
示例代码片段:
# 点击生成后,等待导出按钮 export_btn = wait.until(EC.presence_of_element_located((By.ID, "ExportExcelLB"))) download_url = export_btn.get_attribute("href") # 用wget下载 import subprocess subprocess.run(["wget", "-O", f"{year}_{category}_{scope}.csv", download_url]) # 或者用requests import requests response = requests.get(download_url) with open(f"{year}_{category}_{scope}.csv", "wb") as f: f.write(response.content)
额外注意事项
- 网站可能有反爬机制,不要设置过快的循环间隔,适当添加等待时间
- 确保iframe定位正确,若页面结构变化,需要调整XPATH
- 处理可能的元素加载失败情况,添加try-except块
内容的提问来源于stack exchange,提问作者Abdirahman Ahmed
相关产品推荐
相关产品推荐

