导读:本期聚焦于小伙伴创作的《如何高效抓取网页图表数据:绕过鼠标悬停,直取JavaScript变量》,敬请观看详情。想从可视化报表里拿数据,却只能靠鼠标悬停看 tooltip?这种交互式图表把数值藏在闭包和运行时变量中,传统选择器抓取往往落空。其实不少图表库会把原始数据挂到全局对象或组件实例上,用浏览器开发者工具在 Sources 面板搜索关键字,就能定位存放数组的变量。比起模拟悬停再解析浮层文本,直接读取内存中的数据结构更快也更稳,还能避开分页与动画导致的丢数。下文以 ECharts 与 Highcharts 为例,演示如何通过控制台求值拿到 series 数据,并给出用 Puppeteer 注入脚本批量导出的思路。

在抓取带有图表的网页时,很多人习惯用爬虫模拟鼠标移动,等 tooltip 弹出后再去读浮层里的文字。这种做法不仅慢,还容易因为动画延迟、图例切换而漏掉数据。实际上,大多数前端图表库在渲染时都会把原始数据集保留在 JavaScript 变量或组件实例里,我们完全可以在页面上下文中直接读取这些变量,绕过所有交互层。

如何高效抓取网页图表数据:绕过鼠标悬停,直取JavaScript变量

为什么鼠标悬停抓取不可靠

鼠标悬停触发的 tooltip 本质是 DOM 动态插入的节点,其内容由图表库根据当前鼠标坐标从内部数据做格式化后生成。这意味着你拿到的不是源数据,而是经过四舍五入、单位拼接甚至本地化后的展示值。一旦图表做了节流处理,快速移动时部分点根本不会渲染 tooltip,数据就永久丢失了。

此外,很多报表页面采用懒加载或按需聚合,悬停时才会向后台请求明细。这种场景下模拟悬停等于被迫触发一堆网络请求,效率极低。如果目标只是拿到完整数据集,直接访问图表对象显然更合理。下面的例子展示了 ECharts 实例如何暴露数据。

// 假设页面已用 echarts.init 挂载到 dom
var chartDom = document.getElementById('main');
var inst = echarts.getInstanceByDom(chartDom);
// 获取所有系列的原始数据
var series = inst.getOption().series;
series.forEach(function(s){
  console.log(s.name, s.data);
});

从全局变量定位图表数据

有些老旧系统会把数据直接挂到 window 上,比如 window.chartDatawindow.__SERIES__。打开开发者工具的 Sources 面板,搜索接口返回的关键字段名,往往能发现一个被赋值的全局变量。这种变量在页面生命周期内都存在,用控制台打印即可拿到未经渲染损耗的数组。

如果代码经过打包压缩,变量名可能变成 a.b.c 这样的结构。此时可以下断点到 echarts.initHighcharts.chart 的调用处,从调用栈的闭包里找传入的 option 对象。只要能拿到 option,series 里的 data 就是我们要的内容。下面是用 Highcharts 读取的方式。

// Highcharts 将实例挂在元素上
var el = document.getElementById('container');
var hc = el.highcharts();
// 输出第一个系列的点的 y 值
var pts = hc.series[0].points.map(function(p){ return p.y; });
console.log(pts);

用无头浏览器批量导出

当需要在服务端批量抓取时,可以用 Puppeteer 启动无头 Chrome,注入上面那段读取脚本,通过 page.evaluate 拿到返回值。这样不需要任何鼠标事件,页面加载完即可取数。注意要等待图表渲染完成,通常监听 window 上的自定义事件或延时几百毫秒。

下面的 Node 脚本演示了打开页面、等待、取值的全过程。它比用 request 抓 HTML 再正则匹配稳健得多,因为数据本来就是结构化对象,不需要再做文本清洗。如果页面用了 iframe,记得先切换到对应 frame 再执行求值。

const puppeteer = require('puppeteer');
(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://ipipp.com/report');
  // 等待图表挂载
  await page.waitForSelector('#main');
  await new Promise(r => setTimeout(r, 800));
  const data = await page.evaluate(() => {
    var inst = echarts.getInstanceByDom(document.getElementById('main'));
    return inst.getOption().series.map(s => s.data);
  });
  console.log(JSON.stringify(data));
  await browser.close();
})();

注意事项与反爬应对

部分网站会对 getOption 之类的方法做混淆,或者把数据加密后再塞进图表。此时可改为在 XMLHttpRequest 响应拦截里取原始 JSON,因为图表数据大多来自接口。在 Puppeteer 中监听 response 事件即可截获。

另一个常见问题是图表用了 Web Worker 计算,主线程变量里只有结果引用。这种情况可以退而求其次,用 canvas 的 toDataURL 拿到图样再做 OCR,但成本和误差都高。优先建议和对方产品确认是否提供数据导出 API,直取变量只是在没有 API 时的工程折中方案。

抓取方式稳定性拿到的是否源数据
模拟悬停读 tooltip
读图表实例变量
拦截接口响应

总的来说,遇到网页图表先别急着写鼠标移动逻辑,按 F12 看看全局对象和图表实例,往往一行 getOption 就解决了。把这套思路做成内部工具,能省掉大量脆弱的爬虫维护时间。

web_scrapingJavaScript_variablechart_data修改时间:2026-08-08 01:00:32

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。