先给结论:不要用“同一个 URL”这一个变量去判断收录问题。你需要在未登录 + 移动端 UA、未登录 + 桌面端 UA、已登录 + 同一 UA 三组条件下分别抓取同一地址,把返回的 HTML 主体、HTTP 状态码和重定向链逐项对照。只有当未登录状态下的返回内容稳定且与页面实际主体一致,这个地址才具备被当作单一收录对象处理的前提;否则你面对的其实是多个版本的页面,收录信号会被拆散。
常见的表现是:你在浏览器里打开某条链接,看到的是完整正文;换一台设备或退出登录后再打开,页面却变成登录引导、地区提示、空壳骨架或跳转到首页。你据此判断“内容明明存在”,但抓取端看到的是另一份东西。
这个矛盾本身不说明谁对谁错,它只说明服务端根据请求特征做了差异化输出。设备类型、登录 Cookie、地区 IP、语言偏好,都可能成为分叉条件。问题不在于内容是否存在,而在于你对照时是否固定了这些条件。
第一种解释是渲染差异。服务端返回的 HTML 骨架基本相同,正文由客户端脚本填充。桌面端脚本执行顺利,移动端因脚本被拦截、接口超时或依赖登录态而填充失败,于是看起来“内容不一样”。这种情况下,原始 HTML 里往往缺少正文文本,差异发生在浏览器执行之后。
第二种解释是服务端分叉。服务端在返回响应之前就根据 UA、Cookie 或 IP 决定了输出哪一版 HTML。这种情况下,用 curl 直接取源码就能看到差异,不依赖任何脚本执行。移动端和桌面端拿到的可能是两套模板、两套 canonical,甚至两套 robots 指令。
两者的处理方向完全不同:渲染差异要解决脚本可执行性,服务端分叉要统一输出或明确版本归属。判断错方向,后续所有动作都会落空。
最直接的证据是关闭脚本后的原始响应。用同一地址、同一 UA,分别带与不带登录 Cookie 请求,比较返回的 HTML 源码:
第二组证据是响应头与状态码。服务端分叉常伴随 Vary 头(如 Vary: User-Agent, Cookie)、不同的 Cache-Control,或未登录时返回 302 跳转。渲染差异通常状态码一致、头部一致,差异只出现在脚本执行之后。
第三组证据是重定向链。已登录访问返回 200 并渲染正文,未登录访问返回 302 到登录页,这属于服务端分叉的典型形态。此时收录对象到底是被重定向的地址,还是登录页,需要单独确认,不能混为一谈。
假设某文章地址在桌面端未登录时返回完整正文,在移动端未登录时返回“请下载客户端查看”的提示页,两者状态码都是 200。你可以这样推进:
这个例子的数字和结论都是假设,用于说明对照方法:先固定条件取原始响应,再根据差异出现的位置判断分叉发生在服务端还是客户端。动作的结果会直接决定下一步——服务端分叉要改输出逻辑或规范声明,渲染差异要排查脚本与接口,两者不能互换处理。
一是缓存层。CDN 或反向代理可能按 UA 缓存了不同版本,导致你看到的差异来自缓存副本而非源站逻辑。绕过缓存再取一次,能排除这一层干扰。
二是地区与语言。按 IP 或 Accept-Language 返回不同语言版本时,差异同样会被误读为设备问题。对照时应把地区变量也固定住。
三是登录态本身的层级。部分站点对“已登录但无权限”和“未登录”返回不同内容,这属于第三、第四种状态,对照表里要单独列一行,不能只分登录与未登录两栏。
把这些条件固定后,你得到的是一张可复查的对照表,而不是一次性的印象。收录处理依赖的是稳定、可复现的返回结果;只要同一地址在不同条件下返回实质不同的主体内容,就应该先解决版本归属,再谈其他。