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

使用Selenium提取JS图表<g>标签数据及解决迭代报错问题

爬取充电桩历史功率数据的问题与优化方案

问题背景

需要爬取本地IP访问的电动汽车充电桩网页(需登录),目标是获取JS图表中跨度超1年的历史功率数据,用于对比充电时段与家庭可用功率。

已实现内容

  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()
  1. 坐标轴数据提取:可提取图表可见的坐标轴参数,代码及输出示例:
# 提取可见的坐标轴参数
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
  1. 功率数据提取:通过XPATH定位<g>标签,从aria-label中获取时间与功率的结构化数据,示例结果:
[{'kW data': 'Building power 20:40:00 3.711'}, {'kW data': 'Building power 20:45:00 5.235'}, ...]
  1. 时间回退操作:点击左箭头实现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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 08:17:34