使用Selenium爬取Octopus Energy数据遇NoneType属性错误及优化需求
解决Octopus Energy仪表读数提取问题
问题核心
- 直接抓取
MeterReadingHistory__content的文本会得到拼接后的混乱内容,需拆分出日期和读数; - 定位动态生成的类名
MeterReadingHistorystyled__StyledReadingsContainer-sc-1mqak03-0.cEwaaW时返回None,触发AttributeError。
为什么定位容器失败?
- 动态类名不稳定:这类带随机后缀的类名是Styled Components等前端框架自动生成的,页面更新或刷新后可能变化;
- 未等待元素加载:直接调用
find_element时,目标元素可能还未渲染完成; - 元素结构变化:可能该容器类名已被前端修改,或元素嵌套在未切换的iframe中(需检查页面结构确认)。
高效提取方案(无需正则)
方法1:定位单个读数条目
利用页面结构中每个读数的独立容器,通过显式等待确保元素加载后提取:
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 # 登录后等待读数区域加载完成 wait.until(EC.presence_of_element_located((By.CLASS_NAME, "MeterReadingHistory__content"))) # 定位所有独立的读数条目(需根据实际页面结构调整选择器) reading_items = wait.until(EC.presence_of_all_elements_located( (By.XPATH, "//div[contains(@class, 'MeterReadingHistory__reading')]") )) data = [] for item in reading_items: # 从条目内提取日期和读数(根据页面元素结构调整子元素选择器) date = item.find_element(By.XPATH, ".//span[contains(@class, 'MeterReadingHistory__date')]").text reading = item.find_element(By.XPATH, ".//span[contains(@class, 'MeterReadingHistory__value')]").text data.append({"日期": date, "读数": reading}) # 转换为DataFrame df = pd.DataFrame(data) print(df)
方法2:基于文本内容定位
如果类名过于动态,可通过固定文本(如"Your reading")反向定位关联元素:
# 提取所有读数对应的日期和数值 dates = wait.until(EC.presence_of_all_elements_located( (By.XPATH, "//div[contains(text(), 'Your reading')]/preceding-sibling::div") )) readings = wait.until(EC.presence_of_all_elements_located( (By.XPATH, "//div[contains(text(), 'Your reading')]/following-sibling::div") )) # 打包数据并生成DataFrame data = list(zip([d.text.strip() for d in dates], [r.text.strip() for r in readings])) df = pd.DataFrame(data, columns=["日期", "读数"])
修复NoneType错误的要点
- 验证元素存在性:打开浏览器开发者工具,搜索目标类名,确认元素是否真实存在;
- 改用显式等待:替换
driver.find_element为等待方法,避免元素未加载时查找:container = wait.until(EC.presence_of_element_located( (By.CLASS_NAME, "MeterReadingHistorystyled__StyledReadingsContainer-sc-1mqak03-0.cEwaaW") )) - 检查iframe:如果读数区域嵌套在iframe中,需先切换到iframe再定位元素:
iframe = wait.until(EC.presence_of_element_located((By.TAG_NAME, "iframe"))) driver.switch_to.frame(iframe) # 之后再执行元素定位
内容的提问来源于stack exchange,提问作者Cam
相关产品推荐
相关产品推荐

