去年接了一个数据采集任务,目标是一个报表系统里的趋势图。一开始我想得很简单:requests 拿接口 JSON,不行就 selenium 模拟登录慢慢翻。结果打开开发者工具一看,整个人愣住了——整个页面只有一个 <canvas> 标签,图表里的数字、坐标轴标签、图例说明,全都"画"在了画布上,DOM 树里一个都找不到。之前的经验全部作废。这就是典型的 Canvas 渲染数据图表:前端用绘图引擎把数据变成像素,浏览器里看得见摸得着,爬虫这边却像隔了一层毛玻璃。
这篇文章就专门解决这个问题。我会先讲明白 Canvas 图表为什么难爬,再列出几条切实可行的路线,最后给出一套用 Selenium 注入钩子截获绘图数据的完整方案,包含我踩过的坑和排查思路。适合正在做数据采集、报表自动化、竞品数据监控的朋友参考。不管你是 Python 新手还是有一定经验的爬虫工程师,看完都能直接落地。
1. 为什么 Canvas 图表是爬虫的"黑盒"
1.1 Canvas 渲染的底层机制与 DOM 爬虫的盲区
先说清楚一个问题:普通网页里,数据展示靠的是 HTML 元素。一个表格、一段文字、一张图片,都有对应的 DOM 节点,爬虫解析 HTML 就能拿到内容。但 Canvas 完全不同。
如果说 DOM 像一份印刷好的报纸,文字、图片、位置都写在版面上,爬虫只要读报就能抄到内容;那 Canvas 就更像一块电子画板。页面在运行时通过 JavaScript 调用绘图 API,把点、线、面、文字一笔一笔画上去。画完之后,画板上只有最终的像素颜色,至于刚才画了哪条线、写了哪个字,画板自己并不保存。
用代码拆开看,Canvas 绘制的核心是这样的:
javascript复制const canvas = document.getElementById('chart');
const ctx = canvas.getContext('2d');
ctx.fillText('2024年Q1销售额', 100, 50);
ctx.beginPath();
ctx.moveTo(20, 200);
ctx.lineTo(180, 60);
ctx.stroke();
爬虫解析 HTML 时,看到的就是:
html复制<canvas id="chart" width="800" height="400"></canvas>
没有任何数据信息。真正的数据流转发生在 JavaScript 运行时,而 Selenium 这类自动化工具获取的是渲染后的 DOM,同样拿不到 canvas 内部的绘图调用记录。这就是最核心的盲区。
对比一下常见图表方案的爬虫友好度:
| 图表方案 | 数据呈现方式 | 能否直接解析 | 典型库 |
|---|---|---|---|
| SVG 图表 | 图形元素是独立 DOM 节点,circle、text 都可定位 | 能,用 XPath/CSS 选择器 | 老版本 Highcharts |
| Canvas 图表 | 渲染为位图,无 DOM 节点 | 不能,需要绕道 | ECharts canvas 模式、Chart.js |
| WebGL 图表 | GPU 渲染,更底层 | 更难,甚至没有绘图文本回调 | Three.js 数据可视化 |
所以遇到 Canvas 图表,第一反应应该是:别在 DOM 里找数据,要找数据进入 canvas 之前的源头。
1.2 反爬者为什么偏爱 Canvas,以及常见的组合拳
很多数据平台宁可牺牲一部分用户体验,也要把图表从 SVG 换成 Canvas,原因很直接:数据不可见性。图表上的数值不进入 DOM,普通的 requests、Scrapy、Selenium 解析器全部失效,这就在第一层就挡住了大部分爬虫。
更狠的是,Canvas 渲染经常和反爬手段组合使用。我见过比较典型的配置是:
- 数据接口加密:前端拿到的数据本身就是密文,要经过一波解密运算才能画到画布上。接口层就挡住了 90% 的请求型爬虫。
- 接口响应不包含完整数据:服务端只返回最近一屏的增量数据,历史数据靠前端本地聚合,想直接从接口拿全量数据根本不可能。
- Canvas 加 WebDriver 检测:页面加载时检测
navigator.webdriver属性,如果发现是自动化环境,画布上会随机少画几个柱状条,或者把数值渲染成错误数字,让人防不胜防。 - 关键信息不画在图上:坐标轴标签、图例数值用 Canvas 画,但 tooltip 里的详细数据却由另一个动态 DOM 生成,形成"页面可见、源码不可见、接口又加密"的三角结构。
这套组合拳打下来,单纯靠 Selenium 点击、截图已经不够用了,必须从多个层面同时下手。这就是为什么我接下来要讲的方案是"组合路线"而不是单点突破。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 四条可行的破局路线,怎么选才算高效
2.1 接口优先:Hook 网络请求才是最优解
先记住一个原则:页面不管渲染得多花哨,要把数据展示出来,数据一定得先传到前端。所以遇到任何 Canvas 图表,第一个动作永远是打开控制台的 Network 面板,看看到底哪个请求在返回原始数据。
有些站点接口是明文的,直接拿来用就行。但很多报表系统会对接口做签名、加密,这时候就需要让浏览器帮我们完成"请求 + 解密 + 渲染"的完整流程,而我们只负责在中间偷听。
推荐用 Selenium 注入 JavaScript 钩子来监听网络请求。以 fetch 为例,在页面加载前重写 window.fetch:
javascript复制const originalFetch = window.fetch;
window.__fetchLog = [];
window.fetch = function (...args) {
const url = typeof args[0] === 'string' ? args[0] : args[0].url;
const options = args[1] || {};
return originalFetch.apply(this, args).then(response => {
const cloned = response.clone();
cloned.text().then(body => {
windo
