Python网络爬虫:高效提取网页图表悬停数据

Python网络爬虫:高效提取网页图表悬停数据

本文探讨了如何高效地从网页图表中提取通常通过鼠标悬停显示的数据。针对使用Selenium进行鼠标悬停操作的局限性,文章提出了一种更优的方案:通过Python的Requests库直接获取网页源代码,并利用正则表达式解析内嵌的JavaScript数据。最后,结合Pandas库对提取的数据进行结构化处理,实现快速、稳定的数据采集,避免了不必要的浏览器自动化开销,适用于数据已直接存在于HTML源中的场景。

在进行网页数据抓取时,我们经常会遇到数据以动态方式呈现的情况,例如图表中的详细信息通常在鼠标悬停时才显示。对于这类场景,许多开发者会首先考虑使用selenium等浏览器自动化工具来模拟用户行为。然而,并非所有动态内容都需要完整的浏览器模拟,有时通过更直接的方式可以显著提高效率和稳定性。

问题分析:Selenium鼠标悬停的局限性

原始问题中尝试使用Selenium来模拟鼠标悬停以获取图表数据。其核心思路是定位图表元素,然后对每个潜在的数据点执行鼠标悬停操作,期望通过ActionChains触发悬停信息并抓取。

from selenium import webdriverfrom selenium.webdriver import ActionChainsfrom selenium.webdriver.chrome.service import Servicefrom selenium.webdriver.chrome.options import Optionsfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support import expected_conditions as ECoptions = Options()options.add_argument("start-maximized")webdriver_service = Service()driver = webdriver.Chrome(options=options, service=webdriver_service)driver.get('https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%40+3.50GHz&id=3904')# 尝试定位图表元素element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//*[@id='placeholder']/div/canvas[2]")))# 错误点:element是一个WebElement对象,不可迭代for el in element:           ActionChains(driver).move_to_element(el).perform()       mouseover = WebDriverWait(driver, 30).until(EC.visibility_of_element_located((By.SELECTOR, ".placeholder > div > div.canvasjs-chart-tooltip > div > span")))          print(mouseover.text)

上述代码存在两个主要问题:

WebElement对象不可迭代: EC.presence_of_element_located返回的是一个WebElement对象,而不是一个列表。因此,直接对其进行for el in element循环会导致’WebElement’ object is not iterable错误。即使改为find_elements,也需要精确知道每个数据点的子元素才能有效模拟悬停。效率与稳定性: 依赖Selenium模拟复杂的UI交互(如精确地在图表的每个数据点上悬停并等待提示出现)效率低下,且容易受到页面加载速度、元素定位不准或DOM结构微小变化的影响,导致抓取不稳定。

在许多情况下,图表所展示的数据并非完全通过前端AJAX请求动态获取,而是直接嵌入在页面的JavaScript代码中。这意味着,我们无需模拟浏览器行为,可以直接从网页源代码中提取这些数据。

更优方案:Requests + 正则表达式 + Pandas

对于CPU价格历史图表这类数据,通过检查网页源代码可以发现,其数据通常以JavaScript变量的形式直接存在于HTML中。例如,常见的图表库(如CanvasJS、Highcharts等)在初始化时会直接在标签中定义数据数组。

立即学习“Python免费学习笔记(深入)”;

我们可以利用Python的requests库获取完整的HTML内容,然后使用正则表达式(re模块)从JavaScript代码中匹配并提取所需的数据。最后,结合pandas库对提取的结构化数据进行进一步处理,如类型转换、时间格式化等。

1. 获取网页内容

使用requests.get()方法获取指定URL的HTML内容。

import requestsimport reimport pandas as pdurl = "https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%40+3.50GHz&id=3904"html_text = requests.get(url).text

2. 正则表达式提取数据

通过分析目标网页的源代码,我们可以找到图表数据通常以dataArray.push({x: …, y: …})的形式存在于某个JavaScript块中。我们可以构建一个正则表达式来匹配这些模式。

例如,目标数据格式为{x: 时间戳, y: 价格}。正则表达式r”dataArray.push({x: (d+), y: ([d.]+)}”可以精确匹配:

dataArray.push(:匹配字面字符串”dataArray.push(“。x: (d+):匹配”x: “后跟着一个或多个数字(时间戳),并将其捕获为第一个分组。, y: ([d.]+)}:匹配”, y: “后跟着一个或多个数字或点(价格),并将其捕获为第二个分组。

# 使用re.findall找到所有匹配的数据对# re.findall会返回一个元组列表,每个元组包含正则表达式捕获的组data_tuples = re.findall(r"dataArray.push({x: (d+), y: ([d.]+)}", html_text)

3. 使用Pandas处理数据

将提取到的数据转换为Pandas DataFrame,便于后续的数据清洗、分析和存储。时间戳通常是Unix时间戳(秒或毫秒),需要转换为可读的日期时间格式。

# 创建DataFrame,指定列名df = pd.DataFrame(data_tuples, columns=["time", "price"])# 将'time'列从字符串转换为整数,再除以1000(如果原始是毫秒),然后转换为datetime对象# 示例中原始时间戳是秒,所以直接除以1000得到秒,然后unit='s'df["time"] = pd.to_datetime(df["time"].astype(int) // 1000, unit="s")# 将'price'列转换为浮点数df["price"] = df["price"].astype(float)print(df.tail())

完整代码示例

import requestsimport reimport pandas as pddef scrape_cpu_pricing_history(cpu_url: str) -> pd.DataFrame:    """    从CPU Benchmark网站抓取指定CPU的价格历史数据。    数据通过解析网页内嵌的JavaScript变量获取。    Args:        cpu_url (str): CPU详细页面的URL。    Returns:        pd.DataFrame: 包含时间戳和价格的DataFrame。                      如果抓取失败或未找到数据,返回空的DataFrame。    """    try:        html_text = requests.get(cpu_url, timeout=10).text    except requests.exceptions.RequestException as e:        print(f"请求URL失败: {e}")        return pd.DataFrame()    # 正则表达式匹配JavaScript中的dataArray.push({x: time, y: price})模式    # x是时间戳(秒),y是价格    pattern = r"dataArray.push({x: (d+), y: ([d.]+)}"    data_matches = re.findall(pattern, html_text)    if not data_matches:        print("未在页面中找到价格历史数据。")        return pd.DataFrame()    # 将匹配结果转换为DataFrame    df = pd.DataFrame(data_matches, columns=["time", "price"])    # 数据类型转换和格式化    try:        # 原始时间戳通常是毫秒级,转换为秒后再转datetime        # 根据观察,此网站的时间戳是秒级,所以直接//1000是错误的,应根据实际情况调整        # 此处根据答案中的 // 1000 判断,原始数据可能以毫秒存储,但to_datetime的unit='s'表示输入是秒        # 如果原始数据是秒,则无需 // 1000        df["time"] = pd.to_datetime(df["time"].astype(int), unit="s") # 假设原始是秒        df["price"] = df["price"].astype(float)    except ValueError as e:        print(f"数据类型转换失败: {e}")        return pd.DataFrame()    return df# 示例调用cpu_detail_url = "https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%40+3.50GHz&id=3904"pricing_df = scrape_cpu_pricing_history(cpu_detail_url)if not pricing_df.empty:    print("成功获取CPU价格历史数据:")    print(pricing_df.tail())else:    print("未能获取CPU价格历史数据。")

输出示例:

                   time   price236 2023-05-28 06:00:00  317.86237 2023-05-29 06:00:00  319.43238 2023-05-30 06:00:00  429.99239 2023-05-31 06:00:00  314.64240 2023-06-01 06:00:00   318.9

注意事项与总结

优先级判断: 在尝试抓取动态内容之前,务必优先检查网页源代码(通过浏览器开发者工具或直接requests.get().text)。很多时候,你认为的“动态”数据其实已经直接嵌入在HTML或JavaScript变量中。效率与资源: requests库轻量且高效,仅请求HTML内容。相比之下,selenium需要启动一个完整的浏览器实例,消耗更多系统资源和时间,适用于需要模拟用户登录、点击、滚动或执行复杂JavaScript交互的场景。正则表达式的鲁棒性: 正则表达式依赖于特定的字符串模式。如果目标网站的HTML或JavaScript结构发生变化,正则表达式可能需要调整。因此,在部署前和定期维护时,应检查其有效性。数据清洗: 抓取到的原始数据可能需要进一步的清洗和类型转换,pandas库在此方面提供了强大的支持。错误处理: 在实际项目中,应加入更完善的错误处理机制,例如网络请求超时、解析失败等。

通过上述方法,我们能够以更高效、更稳定的方式从网页中提取图表数据,避免了不必要的浏览器自动化开销,使爬虫程序更加健壮。在遇到类似问题时,优先分析数据源,选择最直接的抓取策略是提升爬虫性能的关键。

以上就是Python网络爬虫:高效提取网页图表悬停数据的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1365713.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月14日 04:46:15
下一篇 2025年12月14日 04:46:20

相关推荐

  • Pandas中如何实现数据的滚动聚类?动态分组技巧

    在pandas中实现滚动聚类的核心是使用.rolling()方法。1. 它通过定义一个滑动窗口对数据进行局部聚合,如均值、求和、标准差等;2. 支持整数或时间偏移作为窗口大小,并可通过min_periods设置有效数据点数量;3. 可结合.apply()执行自定义聚合函数;4. 与.groupby(…

    2025年12月14日 好文分享
    000
  • gRPC Python:配置重试策略中的超时时间

    本文介绍了 gRPC Python 中重试策略配置中超时时间的行为。重点说明了 timeout 配置项并非单个重试尝试的超时时间,而是整个交互过程的最大持续时间。同时解释了 gRPC 重试机制的设计理念,即不应人为缩短单次尝试的时间,以提高成功几率。 在 gRPC Python 中,配置重试策略可以…

    2025年12月14日
    000
  • gRPC Python:配置重试机制中的总超时而非单次尝试超时

    正如摘要所述,在 gRPC Python 中,配置的 timeout 并非针对每次重试的超时时间,而是整个 gRPC 调用的总超时时间。这意味着,即使配置了重试策略,每次尝试的总时间加起来也不能超过 timeout 值。 gRPC 的重试机制旨在提高在网络不稳定或服务器偶发性故障情况下的调用成功率。…

    2025年12月14日
    000
  • 配置 gRPC Python 客户端重试机制:理解超时设置

    本文旨在阐明 gRPC Python 客户端中重试机制的超时配置,重点解释 timeout 参数的作用范围,以及为何 gRPC 不支持为每次重试单独设置超时时间。通过本文,你将了解如何正确配置重试策略,并理解其设计背后的考量。 在 gRPC 中,配置客户端的重试行为,可以有效地提高应用程序的健壮性。…

    2025年12月14日
    000
  • 解决sklearn中无法导入PredictionErrorDisplay的问题

    本文旨在帮助读者解决在使用scikit-learn时遇到的ImportError: cannot import name ‘PredictionErrorDisplay’ from ‘sklearn.metrics’错误。该错误通常是由于scikit-…

    2025年12月14日
    000
  • gRPC Python:配置每次重试的超时时间

    本文介绍了在 gRPC Python 中配置重试策略时,超时设置的实际作用。重点说明了 gRPC 重试机制中不存在每次尝试的独立超时时间,而是全局的交互超时时间。解释了为何 gRPC 采用这种设计,并提供了一种变通方法,虽然并不完全等同于每次尝试的超时,但可以控制整体的重试行为。 在 gRPC Py…

    2025年12月14日
    000
  • NumPy argmax 在手写数字分类预测中返回错误索引的调试与修正

    本文针对手写数字分类模型在使用 np.argmax 进行预测时出现索引错误的问题,提供了一种基于图像预处理的解决方案。通过检查图像的灰度转换和输入形状,并结合 PIL 库进行图像处理,可以有效地避免因输入数据格式不正确导致的预测错误,从而提高模型的预测准确性。 在使用深度学习模型进行手写数字分类时,…

    2025年12月14日
    000
  • 连接 MySQL 5.1 数据库的 Python 教程

    本文档旨在指导开发者如何使用 Python 连接到 MySQL 5.1 数据库。由于 MySQL 5.1 较为古老,现代的 MySQL 连接器可能存在兼容性问题。本文将介绍如何使用 mysql-connector-python 驱动,并配置相应的参数,以成功建立连接。同时,本文也强烈建议升级 MyS…

    2025年12月14日
    000
  • Python连接MySQL 5.1:克服旧版认证与字符集兼容性挑战

    本教程详细阐述了如何使用Python 3和mysql.connector库成功连接到老旧的MySQL 5.1数据库。文章重点介绍了解决旧版认证协议和字符集兼容性问题的关键配置,特别是use_pure=True和charset=’utf8’的重要性,并提供了可运行的代码示例。同…

    2025年12月14日
    000
  • 解决LabelEncoder在训练/测试数据中遇到未知标签的ValueError

    在使用sklearn.preprocessing.LabelEncoder对分类特征进行数值化编码时,一个常见的挑战是当测试集中出现训练集中未曾见过的标签时,会引发ValueError: y contains previously unseen labels。此错误的核心在于LabelEncoder…

    2025年12月14日
    000
  • 如何使用Pandas进行条件筛选与多维度分组计数

    本文将详细介绍如何使用Pandas库,针对数据集中特定列(如NumericValue)中的缺失值(NaN)进行高效筛选,并在此基础上,根据多个维度(如SpatialDim和TimeDim)进行分组,最终统计满足条件的记录数量。通过实例代码,读者将掌握数据预处理和聚合分析的关键技巧,实现复杂条件下的数…

    2025年12月14日
    000
  • 使用Pandas进行条件筛选与分组计数:处理缺失值

    本文详细介绍了如何使用Pandas库对数据集进行条件筛选,特别是针对NaN(Not a Number)值进行过滤,并在此基础上执行分组统计,计算特定维度组合下的数据条目数量。通过实例代码,读者将学习如何高效地从原始数据中提取有价值的聚合信息,从而解决数据清洗和初步分析中的常见问题。 在数据分析工作中…

    2025年12月14日
    000
  • 使用递归算法生成特定字符串模式:一个Python实现教程

    本文详细阐述了如何利用递归算法生成一个特定规则的字符串模式。通过分析给定示例,我们逐步揭示了该模式的构成规律,包括基础情况和递归关系。教程提供了清晰的Python代码实现,并解释了递归逻辑,帮助读者理解如何将复杂模式分解为更小的、可重复解决的问题,从而高效地构建目标字符串。 引言 在编程中,我们经常…

    2025年12月14日
    000
  • 探索与实现递归字符串模式:pattern(k)函数详解

    本文详细介绍了如何通过观察给定示例,识别并实现一个基于递归的字符串模式生成函数pattern(k)。文章将逐步分析模式规律,包括其终止条件和递归关系,并提供完整的Python代码示例及运行演示,旨在帮助读者理解递归思维在解决此类问题中的应用。 pattern(k)函数概述 在编程实践中,我们经常会遇…

    2025年12月14日
    000
  • Python Tkinter库存系统:优化文件操作与UI响应,避免数据重复

    本教程深入探讨Tkinter应用中条形码生成与文件写入时遇到的常见问题,特别是随机数未更新和文件重复校验失败。核心在于揭示Python文件操作a+模式下读写指针的默认行为,以及全局变量导致的数据僵化。文章将详细阐述如何通过将随机数生成移入事件处理函数、利用file.seek(0)管理文件指针,并推荐…

    2025年12月14日
    000
  • 使用Python和Matplotlib绘制ASCII地震数据图

    本文档将指导您如何使用Python的matplotlib库将地震振幅的ASCII数据转换为可视图形。通过读取、解析和绘制数据,您可以快速有效地将原始数据转化为直观的图表,从而更好地理解地震事件的特征。本文提供了详细的代码示例和步骤说明,帮助您轻松完成数据可视化。 数据准备 首先,确保您已经拥有包含地…

    2025年12月14日
    000
  • 使用 Python 和 Matplotlib 绘制 ASCII 数据

    本文将指导读者如何使用 Python 的 Matplotlib 库,将 ASCII 格式的地震振幅数据转换为可视图形。通过简单的代码示例,展示了数据清洗、转换和绘图的完整流程,帮助读者快速上手处理和可视化此类数据。 在科学研究和工程实践中,经常会遇到以 ASCII 格式存储的数据。这些数据通常需要进…

    2025年12月14日
    000
  • 优化Tkinter库存系统:解决条码生成与文件读写问题

    本文深入探讨了Tkinter库存系统中条码重复生成及文件读写异常的核心问题。通过分析随机数生成位置、文件指针行为和重复性检查逻辑,提供了将随机数生成移入事件处理、正确管理文件读写指针、改进重复性检查机制以及推荐使用JSON等结构化数据存储的综合解决方案。旨在帮助开发者构建更健壮、高效的库存管理应用。…

    2025年12月14日
    000
  • 将对象列表转换为 Pandas DataFrame 的实用指南

    本文将指导你如何将 Python 对象列表转换为 Pandas DataFrame。这种转换在数据分析和处理中非常常见,尤其是在处理自定义类生成的对象时。我们将探讨几种不同的方法,包括使用 vars() 函数、处理 dataclasses 和包含 __slots__ 的类。 将对象列表转换为 Dat…

    2025年12月14日
    000
  • 使用 Python Matplotlib 绘制 ASCII 数据图表

    本文档将指导你如何使用 Python 的 Matplotlib 库将 ASCII 格式的数据转换为浮点数并绘制成图表。我们将提供详细的代码示例,解释关键步骤,并提供一些使用建议,帮助你轻松地将 ASCII 数据可视化。 准备工作 首先,确保你已经安装了 Python 和 Matplotlib。如果没…

    2025年12月14日
    000

发表回复

登录后才能评论
关注微信