蜘蛛抓取时拿到的,是服务器在那一瞬间返回的响应。这条响应可能已经被 Cookie、地域判断、设备识别或 A/B 测试改写过。一旦改写后的版本里少了导航、少了列表链接,抓取路径就会随之变窄,URL 发现也会跟着慢下来。
为什么同一个 URL 会返回不同内容
分流本身不是错误,很多站点确实需要它。问题在于分流经常在爬虫不知情的情况下发生,而且是按“有没有带 Cookie”“请求从哪个区域进来”这类蜘蛛不具备的条件来判断。
- Cookie 与同意弹窗:首次访问返回一个带遮罩层的版本,用户点过之后才写入 Cookie。蜘蛛没有这个 Cookie,每次拿到的都是遮罩版,若遮罩把首屏链接一起挡在客户端渲染之外,内链就等于不存在。
- 地域与语言跳转:按 IP 或 Accept-Language 跳到某个语言站。蜘蛛的出口 IP 与用户分布不同,可能被反复送到同一个版本,其他语言站的 URL 就长期缺少入口。
- User-Agent 分流:给移动端和桌面端返回不同的 HTML 骨架。如果其中一版的导航依赖脚本后置注入,蜘蛛从中拿不到可跟随的链接。
- A/B 测试与灰度:随机把访问者分到实验组。蜘蛛每次抓到的可能都是不同分支,链接结构不稳定,抓取结果也就不稳定。
- 登录或会员门槛:未登录状态只给一个极简首页,站内大部分 URL 没有对外暴露的路径。
这些分流怎样影响 URL 发现
URL 发现依赖可见的、可跟随的链接。当蜘蛛拿到的版本是一份“瘦身版”,它并不是抓取失败,而是把这份瘦身版当成了页面的真实样子:里面有多少链接,就顺着多少链接往下走。结果是站点里有内容的页面长期没人访问,日志里只看到首页反复被请求。
判断标准很朴素:在不带 Cookie、不模拟登录、不设置特殊 UA 的情况下请求一次,看看返回的 HTML 里能不能找到通向主要栏目的链接。找不到,就是发现路径被切断了。
缓存层会把问题放大
CDN 和反向代理按缓存键决定返回哪一份内容。如果缓存键里包含 Cookie,同一 URL 会被拆成很多份副本,命中率下降,回源增多;如果响应带上了 Vary: Cookie 或 Vary: User-Agent,缓存会把不同请求者当成不同对象,蜘蛛抓到的那一份可能恰好是某个边缘节点上残留的实验版本。反过来,如果缓存键过于简单,先被缓存的实验版或遮罩版又会被分发给后续所有请求者,包括蜘蛛。
排查与治理的常规做法
- 用全新会话、不带任何 Cookie 的请求抓一次首页和几个栏目页,检查返回 HTML 中的链接数量。
- 分别用桌面与移动 UA 请求同一 URL,比对可跟随链接的差异。
- 检查响应头里的 Vary、Cache-Control 与缓存键设置,确认蜘蛛与普通用户是否可能落到不同副本。
- 把导航、面包屑、主要列表这类关键链接做成服务端直出的静态 HTML,不依赖脚本后置注入。
- 确认弹窗、跳转、同意层不会阻断链接的抓取路径,必要时对爬虫请求返回不含遮罩的基础版本。
- 如果确实需要多语言或多地域版本,确保每个版本都有稳定、独立的 URL,并互相之间有可跟随的链接。
把版本差异控制在无害范围内
理想状态不是消灭所有分流,而是让分流只影响样式、推荐位这类非关键内容。左侧导航、页脚、面包屑、分页链接这些承载发现功能的元素,最好在任何版本里都保持一致,并且是服务端就能输出的。这样即便蜘蛛抓到的是实验分支、地域版本或未登录版本,它依然能顺着同一套链接继续走,URL 发现的节奏不会被局部改动打乱。
做这类检查不需要复杂工具,一份不带 Cookie 的响应加上一次日志比对,通常就能看出问题出在哪一层。