在抓取带有图表的网页时,很多人习惯用爬虫模拟鼠标移动,等 tooltip 弹出后再去读浮层里的文字。这种做法不仅慢,还容易因为动画延迟、图例切换而漏掉数据。实际上,大多数前端图表库在渲染时都会把原始数据集保留在 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.chartData 或 window.__SERIES__。打开开发者工具的 Sources 面板,搜索接口返回的关键字段名,往往能发现一个被赋值的全局变量。这种变量在页面生命周期内都存在,用控制台打印即可拿到未经渲染损耗的数组。
如果代码经过打包压缩,变量名可能变成 a.b.c 这样的结构。此时可以下断点到 echarts.init 或 Highcharts.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