使用Selenium提取JS图表<g>标签数据及解决迭代报错问题
爬取充电桩历史功率数据的问题与优化方案
问题背景
需要爬取本地IP访问的电动汽车充电桩网页(需登录),目标是获取JS图表中跨度超1年的历史功率数据,用于对比充电时段与家庭可用功率。
已实现内容
- 登录自动化:完成系统登录流程,代码如下:
driver_path = r"C:\chromedriver-win64\chromedriver.exe" ACCOUNT = "login@evch.com" PASSWORD = "pw" driver = webdriver.Chrome(driver_path) driver.maximize_window() wait = WebDriverWait(driver, 10) driver.get("http://192.168.1.245/#!/login") # 登录充电桩系统 WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@type='text']"))).send_keys("login@evch.com") WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//input[@type='password']"))).send_keys("pw") WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit']"))).click()
- 坐标轴数据提取:可提取图表可见的坐标轴参数,代码及输出示例:
# 提取可见的坐标轴参数 el = driver.find_element(By.XPATH, "//div[@id='powerManagementDashboard']//*[local-name()='svg']").text print(el)
输出:
08:00 09:00 10:00 11:00 12:00 13:00 -10 0 10 20 30 40 50 kW
- 功率数据提取:通过XPATH定位
<g>标签,从aria-label中获取时间与功率的结构化数据,示例结果:
[{'kW data': 'Building power 20:40:00 3.711'}, {'kW data': 'Building power 20:45:00 5.235'}, ...]
- 时间回退操作:点击左箭头实现6小时时间回退,代码:
# 点击左箭头回退6小时 WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//i[@ng-click='ChartAddHour(-6)']"))).click()
遇到的核心问题
循环执行时间回退操作时,首次点击后立即抛出StaleElementReferenceException: stale element not found in current frame异常,无法完成数十至上百次循环以获取全量数据。
优化与解决建议
1. 解决StaleElementReferenceException异常
- 每次循环重新定位元素:不要提前缓存回退箭头元素,每次点击前通过
WebDriverWait重新查找,避免页面刷新后元素引用失效。修改后的循环示例:
total_pages = 100 # 根据实际需求设置循环次数 for _ in range(total_pages): # 提取当前页面功率数据 power_data = [] g_elements = WebDriverWait(driver, 20).until( EC.presence_of_all_elements_located((By.XPATH, "//div[@id='powerManagementDashboard']//*[local-name()='g' and @aria-label]")) ) for g in g_elements: aria_label = g.get_attribute('aria-label') power_data.append({'kW data': aria_label}) # 处理或保存当前页数据 print(power_data) # 重新定位并点击回退箭头 try: back_btn = WebDriverWait(driver, 20).until( EC.element_to_be_clickable((By.XPATH, "//i[@ng-click='ChartAddHour(-6)']")) ) back_btn.click() # 等待页面数据更新完成(等待旧元素失效确认页面刷新) WebDriverWait(driver, 15).until(EC.staleness_of(g_elements[0])) except Exception as e: print(f"回退失败: {str(e)}") break
- 添加显式等待:点击回退后,等待图表数据完全更新(比如等待旧的
<g>元素失效,或坐标轴文本变化),再执行下一次数据提取。
2. 数据提取效率优化
- 直接读取页面JS数据:如果图表数据存储在页面全局JS变量中,可通过
execute_script直接提取,无需操作DOM元素,效率大幅提升:
# 假设功率数据存储在window.powerHistory全局变量中 raw_data = driver.execute_script("return window.powerHistory;") print(raw_data)
- 批量提取元素:每次页面加载完成后,一次性定位所有目标
<g>元素,减少重复查找的资源消耗。
3. 循环逻辑优化
- 动态终止循环:除固定次数外,添加终止条件(比如回退箭头变为不可点击时,说明已到最早数据):
while True: # 提取当前页数据... # 检查回退箭头是否可点击 try: back_btn = WebDriverWait(driver, 10).until( EC.element_to_be_clickable((By.XPATH, "//i[@ng-click='ChartAddHour(-6)']")) ) back_btn.click() # 等待页面加载... WebDriverWait(driver, 15).until(EC.staleness_of(g_elements[0])) except TimeoutException: print("已获取全部历史数据") break
- 添加重试机制:对数据提取和点击操作添加重试逻辑,避免单次异常导致程序终止:
from selenium.common.exceptions import StaleElementReferenceException def extract_power_data(): retry_count = 3 while retry_count > 0: try: g_elements = WebDriverWait(driver, 20).until( EC.presence_of_all_elements_located((By.XPATH, "//div[@id='powerManagementDashboard']//*[local-name()='g' and @aria-label]")) ) return [{'kW data': g.get_attribute('aria-label')} for g in g_elements] except StaleElementReferenceException: retry_count -= 1 print(f"数据提取重试,剩余次数: {retry_count}") return []
内容的提问来源于stack exchange,提问作者DvdV
相关产品推荐
相关产品推荐

