使用Selenium获取CSV下载链接并导入Pandas DataFrame遇问题
问题分析与解决方案
为什么Selenium抓取的href为空?
你访问的这个新西兰统计局COVID dashboard是基于Shiny框架开发的,这类应用的下载链接有两个关键特性:
- 动态生成+会话绑定:只有当你点击弹窗里的下载按钮时,后端才会生成一个临时的、和当前浏览器会话绑定的下载链接,再通过JavaScript把链接填充到按钮的
href属性里。 - 等待逻辑不匹配:你用的
EC.presence_of_element_located只保证元素出现在DOM中,但此时JavaScript可能还没完成href的填充,所以拿到的是初始空值;另外直接获取outerHTML可能拿到的是元素刚创建时的静态HTML,而非更新后的状态。
改进的Selenium实现方法
我们可以调整等待逻辑,确保等到href属性不为空再获取,同时直接读取元素的href属性(比outerHTML更可靠):
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import pandas as pd driver = webdriver.Chrome(executable_path=r'/usr/local/bin/chromedriver') URL = 'https://statisticsnz.shinyapps.io/covid_19_dashboard/' driver.get(URL) try: # 等待并点击"Download Data"按钮打开弹窗 download_btn = WebDriverWait(driver, 15).until( EC.element_to_be_clickable((By.ID, 'download_data-show')) ) download_btn.click() # 等待下载按钮的href属性填充完成(不为空) csv_link = WebDriverWait(driver, 15).until( lambda d: d.find_element(By.ID, 'download_data-downloadData').get_attribute('href') != '' ) # 直接用pandas读取CSV df = pd.read_csv(csv_link) print(df.head()) finally: driver.quit()
更高效的替代方案:捕获网络请求
如果不想依赖页面元素的交互状态,还可以用Selenium的网络日志捕获下载请求的URL——Shiny的下载本质是一个GET请求,我们可以过滤出对应的请求链接:
from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import pandas as pd import json # 配置Chrome以捕获网络请求日志 chrome_options = webdriver.ChromeOptions() chrome_options.set_capability('goog:loggingPrefs', {'performance': 'ALL'}) driver = webdriver.Chrome(executable_path=r'/usr/local/bin/chromedriver', options=chrome_options) URL = 'https://statisticsnz.shinyapps.io/covid_19_dashboard/' driver.get(URL) try: # 打开下载弹窗 download_btn = WebDriverWait(driver, 15).until( EC.element_to_be_clickable((By.ID, 'download_data-show')) ) download_btn.click() # 点击下载按钮触发请求 csv_download_btn = WebDriverWait(driver, 15).until( EC.element_to_be_clickable((By.ID, 'download_data-downloadData')) ) csv_download_btn.click() # 从网络日志中过滤出下载链接 logs = driver.get_log('performance') download_url = None for log in logs: log_entry = json.loads(log['message'])['message'] if log_entry['method'] == 'Network.requestWillBeSent' and 'download_data-downloadData' in log_entry['params']['request']['url']: download_url = log_entry['params']['request']['url'] break if download_url: df = pd.read_csv(download_url) print(df.head()) finally: driver.quit()
注意事项
- Shiny生成的下载链接是会话专属的,有效期很短,必须在同一个浏览器会话中获取链接后立即用pandas读取。
- 确保你的ChromeDriver版本和本地Chrome浏览器版本完全匹配,避免兼容性问题导致的元素定位或网络日志捕获失败。
内容的提问来源于stack exchange,提问作者Trizzy
相关产品推荐
相关产品推荐

