0

0

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

DDD

DDD

发布时间:2025-07-20 10:38:01

|

761人浏览过

|

来源于php中文网

原创

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

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

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

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

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

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC

options = 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)

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

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

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

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

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

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

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

Red Panda AI
Red Panda AI

AI文本生成图像

下载

1. 获取网页内容

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

import requests
import re
import pandas as pd

url = "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 requests
import re
import pandas as pd

def 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   price
236 2023-05-28 06:00:00  317.86
237 2023-05-29 06:00:00  319.43
238 2023-05-30 06:00:00  429.99
239 2023-05-31 06:00:00  314.64
240 2023-06-01 06:00:00   318.9

注意事项与总结

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

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

相关专题

更多
python开发工具
python开发工具

php中文网为大家提供各种python开发工具,好的开发工具,可帮助开发者攻克编程学习中的基础障碍,理解每一行源代码在程序执行时在计算机中的过程。php中文网还为大家带来python相关课程以及相关文章等内容,供大家免费下载使用。

715

2023.06.15

python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

625

2023.07.20

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

739

2023.07.25

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

617

2023.07.31

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

1235

2023.08.03

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

547

2023.08.04

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

575

2023.08.04

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

698

2023.08.11

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

3

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Django 教程
Django 教程

共28课时 | 2.6万人学习

SciPy 教程
SciPy 教程

共10课时 | 1.0万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号