
本文旨在探讨从网页动态图表中高效提取数据的方法。针对鼠标悬停显示数据的场景,我们将对比传统的Selenium模拟交互方式与更优的直接解析HTML中嵌入的JavaScript数据的方法。通过实际案例,我们将展示如何利用Python的requests、re和pandas库,直接从网页源代码中提取并结构化图表数据,从而避免复杂的浏览器自动化操作,提高数据抓取的效率和稳定性。
在进行网页数据抓取时,我们经常会遇到动态加载或通过JavaScript渲染的内容,尤其是那些以图表形式展示的数据,如历史价格曲线、性能指标等。这些数据往往在用户鼠标悬停时才以工具提示(tooltip)的形式显示,这给传统的爬虫带来了挑战。
挑战:使用Selenium模拟鼠标悬停抓取动态数据
许多初学者在面对这类问题时,自然会想到使用Selenium等浏览器自动化工具来模拟用户行为。例如,对于一个展示CPU价格历史的图表,尝试通过模拟鼠标悬停在图表上的每一个点来获取对应的价格和日期信息。
以下是一个尝试使用Selenium模拟鼠标悬停的示例代码:
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 EC# 初始化WebDriveroptions = 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+%403.50GHz&id=3904')# 等待图表元素加载# 注意:EC.presence_of_element_located 返回的是单个WebElement对象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.CSS_SELECTOR, ".placeholder > div > div.canvasjs-chart-tooltip > div > span")))# print(mouseover.text)
上述代码中存在一个常见错误:WebDriverWait.until(EC.presence_of_element_located(…))方法返回的是一个WebElement对象,代表页面上找到的第一个匹配元素,而不是一个可迭代的元素列表。因此,直接对其进行for el in element:循环会导致’WebElement’ object is not iterable的运行时错误。
更重要的是,即使修复了迭代问题,通过模拟鼠标悬停来逐点抓取图表数据,效率也极其低下且不稳定。页面渲染、动画效果和JavaScript执行的延迟都可能导致抓取失败或数据不完整。
解决方案:直接解析HTML中的JavaScript数据
对于许多动态图表,其数据往往直接嵌入在页面的JavaScript代码中,而不是通过Ajax请求动态加载。在这种情况下,最有效且高效的方法是直接从网页的HTML源代码中提取这些JavaScript变量。
以目标网站为例,图表数据通常以类似dataArray.push({x: …, y: …})的格式存储在标签中。我们可以利用Python的requests库获取页面HTML,然后使用正则表达式re模块来匹配并提取这些数据。
核心步骤:
获取网页内容: 使用requests库发送HTTP GET请求,获取目标URL的HTML源代码。查找数据模式: 分析HTML源代码,定位包含图表数据的JavaScript代码块。识别数据点的通用模式,例如{x: 时间戳, y: 价格}。使用正则表达式提取: 编写正则表达式来匹配并捕获这些数据点中的时间戳和价格值。结构化数据: 将提取到的数据转换为结构化的格式,例如Pandas DataFrame,以便后续分析。数据类型转换: 对提取到的时间戳进行转换,使其变为可读的日期时间格式。
示例代码:
import reimport pandas as pdimport requests# 目标URLurl = "https://www.cpubenchmark.net/cpu.php?cpu=Intel+Core+i9-11900K+%403.50GHz&id=3904"# 使用requests获取网页HTML内容html_text = requests.get(url).text# 使用正则表达式从HTML中查找并提取数据# 模式解释:# dataArray.push({x: (d+), y: ([d.]+)}# - dataArray.push({x: : 匹配字面字符串 "dataArray.push({x: "# - (d+) : 捕获一个或多个数字(时间戳)# - , y: : 匹配字面字符串 ", y: "# - ([d.]+) : 捕获一个或多个数字或点(价格,可能包含小数点)# - } : 匹配字面字符串 "}"df = pd.DataFrame( re.findall(r"dataArray.push({x: (d+), y: ([d.]+)}", html_text), columns=["time", "price"],)# 将时间戳(Unix时间戳,通常是毫秒)转换为可读的日期时间格式# 网站给出的时间戳是毫秒,需要除以1000转换为秒df["time"] = pd.to_datetime(df["time"].astype(int) // 1000, unit="s")# 打印数据框的最后几行以验证结果print(df.tail())
运行结果示例:
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
优势与注意事项
这种直接解析JavaScript数据的方法具有显著优势:
高效性: 无需启动浏览器,大大减少了资源消耗和运行时间。稳定性: 不依赖于页面渲染和JavaScript执行的复杂性,减少了因页面加载延迟或元素定位不准导致的错误。简洁性: 代码逻辑更清晰,易于维护。
然而,此方法并非万能,有其适用范围和注意事项:
适用场景: 主要适用于数据直接嵌入在HTML源代码的标签中,或通过简单的JavaScript变量赋值、数组推送等方式存储的情况。不适用场景:数据通过复杂的AJAX请求在页面加载后异步获取,且这些请求的URL和参数难以推断。数据经过高度混淆或加密,难以通过正则表达式直接解析。需要模拟用户登录、表单提交、点击按钮等复杂交互才能获取数据的情况。正则表达式的健壮性: 正则表达式需要根据目标网站的HTML结构和JavaScript代码模式进行定制。如果网站结构发生变化,正则表达式可能需要调整。数据清洗: 提取出的数据可能需要进一步的清洗和类型转换,例如时间戳转换、数值类型转换等。
总结
在进行网页数据抓取时,面对动态内容,我们应优先考虑从网页源代码中直接提取数据。相比于依赖Selenium模拟复杂的用户交互,直接解析HTML中嵌入的JavaScript数据通常是更高效、稳定且资源友好的选择。只有当数据确实无法通过静态解析获取,或者需要模拟复杂的浏览器行为时,才考虑使用Selenium等浏览器自动化工具。理解这两种方法的优劣和适用场景,是成为一名高效网络爬虫开发者的关键。
以上就是如何高效抓取网页图表中的动态数据的详细内容,更多请关注创想鸟其它相关文章!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1365855.html
微信扫一扫
支付宝扫一扫