很多站点谈抓取时,注意力都在“蜘蛛有没有来”“来了多少次”,却很少算另一笔账:每来一次,蜘蛛要下载多少字节。抓取本身是有带宽和时间成本的,同样一段时间里,响应越轻,能走完的 URL 越多;响应越重,能覆盖的页面就越少。
一次抓取的下载成本由什么构成
一次抓取大致包含三部分:请求头、响应头、响应体。前两者通常只有几百字节,真正的大头是响应体。响应体既包括 HTML 本身,也包括服务端直接内联进去的内容,这部分最容易被忽略。
HTML 体积常见的几个膨胀点
内联的 base64 图片与字体
把图片转成 base64 直接写进 HTML 或 CSS,页面看起来“少了一个请求”,代价是体积增加约三分之一,而且这段内容无法被浏览器单独缓存,每次抓取都要重下。
直接塞进页面的数据
一些框架会把整份状态数据内联进 HTML,再由前端渲染。用户只看到页面的一部分,蜘蛛下载的却是全部数据。当数据量随列表长度线性增长时,抓取成本也跟着涨。
冗余结构与注释
深层嵌套的 DOM、重复的样式声明、大段调试注释,都会进入响应体。单独看都不大,叠加在模板里就很可观。
压缩:性价比最高的一步
HTML、CSS、JS、JSON 都是文本,压缩率通常很高。开启 gzip 或 brotli 后,几百 KB 的 HTML 常常能压到几十 KB。这通常是成本最低、最好验证的一步。
- 确认服务器确实返回了 Content-Encoding: gzip 或 br,而不只是配置里写了开启。
- 不要把已经压缩过的格式(图片、视频、woff2、zip)再压一遍,收益极小,还多耗 CPU。
- 如果经过 CDN,要分清压缩在源站还是边缘节点完成,避免“源站压了、CDN 又压一次”,或者两头都没压。
- 压缩会增加服务器 CPU 开销,抓取高峰时可以配合缓存,把压缩结果缓存下来,而不是每次现压。
减少重复下载:Last-Modified、ETag 与 304
蜘蛛再次访问同一个 URL 时,通常会带上 If-Modified-Since 或 If-None-Match。如果页面没变,服务器返回 304,响应体为空,这次抓取几乎不消耗传输成本。要让它真正生效,有几个前提:
- 这两个头要稳定:同一份内容不要每次请求都生成一个新的 ETag。
- 页面有实质更新时要让 Last-Modified 随之变化,否则蜘蛛会一直拿到 304,看不到新内容。
- 304 省掉的是响应体,请求本身仍然会发生,所以它不能替代对 URL 数量本身的控制。
渲染型页面的额外成本
如果页面靠 JS 渲染,蜘蛛除了下载 HTML,还要下载并执行 JS、CSS。这时体积的影响不止在文档本身:一个体积庞大的脚本包,会同时抬高下载、解析和执行的时间。把首屏不需要的脚本延迟加载、适当拆分打包,对抓取的稳定性会有帮助。
怎么量一遍
- 用命令行看压缩前后的大小差异:curl -H "Accept-Encoding: gzip" -s -o /dev/null -w "%{size_download}" URL,再对比不带压缩头的结果。
- 在浏览器开发者工具里查看文档请求的“传输大小”和“资源大小”,两者的差距就是压缩带来的收益。
- 在页面源码中搜索 base64, 与内联 JSON,估算它们占了多少字节。
- 如果能看到抓取日志,留意下载字节数,找出明显偏大的页面,这类问题通常集中在列表页和详情页模板。
有些体积不必省
压缩和内联都需要权衡:把正文拆成异步加载、只让用户看到,对抓取没有好处;为了减小 HTML 把关键内容全部改成前端拼接,也可能让内容抓不到。目标不是把页面做到最小,而是在内容完整的前提下,把不必要的传输去掉。
在抓取优化里,减少“每次抓取要传的东西”和增加“被抓的入口”同样重要,前者往往更容易测量和验证。
可以挑一两个模板页面先做测试:看压缩前后的大小、看 304 的命中情况,再决定改哪里,比全站铺开容易收场。