谈抓取预算时,讨论常常停留在 URL 数量上:有多少页面、有多少内链、有多少参数组合。但蜘蛛的时间并不是按页面数量平均分配的,而是按一次请求从建立连接到读完响应体所消耗的时长来分配。同样是一千个 URL,每个返回 30KB 的 HTML 与每个返回 500KB 的 HTML,能走完的轮次差别很大。
一次抓取的成本由什么构成
把一次抓取拆开看,大致包含几段开销:
- 连接建立:DNS 解析、TCP 握手,使用 HTTPS 时还有 TLS 协商;
- 服务端处理与首字节时间(TTFB);
- 响应体传输:字节数、压缩方式、可用带宽;
- 客户端解析与渲染:构建 DOM、执行必要的脚本、提取链接。
其中传输和解析这两段最容易在改版中被悄悄放大。模板里多塞几百 KB 的内容,成本会平摊到之后的每一次抓取上,而且很难从抓取数量上直接看出原因。
HTML 体积是从哪里膨胀起来的
把几类常见情况列出来,通常能对上号:
- 把接口返回的 JSON 直接序列化进页面,作为初始状态;
- 图标、小图、字体用 base64 内联在 HTML 或 CSS 中;
- 模板注释、调试代码、已经下线的模块没有清理;
- 统计、客服、实验类脚本在模板里同步加载;
- 同一套 SVG 图标在列表中被反复内联。
这些内容对蜘蛛发现 URL 几乎没有帮助,却要和正文、链接一起传输和解析。它们不会直接导致页面不被抓取,但会让同一份抓取能力覆盖的页面变少。
压缩与传输方式的影响
开启 gzip 或 brotli 之后,HTML 的传输体积通常可以降到原来的三分之一甚至更低,这是投入产出比很高的一项调整。需要注意的是压缩要覆盖 HTML 本身,而不只是静态资源;有些配置只对 css、js 文件生效,动态页面反而没有压缩。
分块传输本身不是问题,但如果页面是边查数据库边输出的流式渲染,首字节时间会被拉长,蜘蛛需要更久才能拿到完整响应。缓存命中率高的页面通常首字节更快,这也是列表页、分类页值得做缓存的直接原因。
DOM 深度与链接提取
拿到 HTML 之后,蜘蛛还要解析结构才能提取链接。链接埋在十几层容器之下,或者必须等脚本执行后才插入,提取成本都会上升。这不是要求把所有结构压平,而是导航、分类、分页这类承担 URL 发现职责的链接,应该尽量放在稳定、浅层、服务端就已存在的结构里。
瘦身的优先顺序
- 先统计响应体大小分布,找出体积最大的那批页面模板,通常集中在列表页和详情页;
- 确认 HTML 响应是否开启压缩,静态资源是否设置了合理的缓存头;
- 把内联的脚本、样式、图标改为可缓存的外部文件,减少重复传输;
- 清理模板中的注释、调试代码和已经下线的组件;
- 对首屏不需要的第三方脚本做延迟加载,避免阻塞页面输出。
调整之后不必期待抓取量立刻变化,可以对照服务器日志里的响应字节数和响应时间,看一段时间内的趋势是否稳定下降。
抓取吞吐大致是时间与字节数的乘积关系:让每个页面更轻,等于在同一段抓取时间里腾出了更多 URL 的位置。
URL 发现、内链结构、Sitemap 决定了蜘蛛能被带到哪里,而页面本身的重量决定了它在一段时间内能走多远。两者不是替代关系,但后者常常因为不显眼而被长期忽略。