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

DDD
发布: 2025-07-20 10:38:01
原创
751人浏览过

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等)在初始化时会直接在<script>标签中定义数据数组。

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

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

网易人工智能
网易人工智能

网易数帆多媒体智能生产力平台

网易人工智能 39
查看详情 网易人工智能

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网络爬虫:高效提取网页图表悬停数据的详细内容,更多请关注php中文网其它相关文章!

最佳 Windows 性能的顶级免费优化软件
最佳 Windows 性能的顶级免费优化软件

每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。

下载
来源:php中文网
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新问题
开源免费商场系统广告
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新 English
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习

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