先给结论:不要直接比较两次抓取到的 HTML,而要先固定一个“基准请求”,再分别记录同一地址在桌面、移动、登录、未登录条件下的 canonical 声明与可见主内容,最后判断差异是内容分叉还是渲染分叉。若不同条件返回的 canonical 指向不同地址,应优先处理声明冲突;若 canonical 相同但正文不同,则要看差异是否足以构成两个独立意图的页面。
你手中的资料通常是一个页面地址,以及若干次抓取快照。第一步不是打开浏览器看页面,而是确定一个可重复的基准请求:同一 URL、同一协议、同一路径、不带追踪参数、不携带登录 Cookie。把这个请求返回的 HTML 保存为基准副本,记录状态码、响应头中的 Content-Location 或 Link(如有)、HTML 中的 <link rel="canonical"> 以及首屏可见主内容。
接下来只改变一个变量:设备类型或登录状态。设备差异可以通过自定义 User-Agent 模拟,但要注意,服务端可能据此返回不同模板,而不仅是 CSS 变化。登录状态则需要两组真实会话:一组已登录,一组未登录。每个条件都保存完整 HTML 和渲染后可见文本,而不是只截图。
这样做的影响是:你得到一张对照表,而不是一堆互相矛盾的印象。下一步判断才有依据。
把基准副本与各条件副本并排比较,顺序不能颠倒。先比较 <link rel="canonical"> 的 href 是否一致。可能出现三种情况:
/a,移动版指向 /m/a。这是明确的声明冲突,需要决定保留哪一套,或者改为自引用并做响应式适配。可见内容比较要落到具体片段:标题、主段落、价格或状态文本、主要操作按钮。不要因为侧边栏推荐位不同就判定为两个页面。只有主内容意图发生实质变化,才需要重新考虑 canonical 策略。
假设你面对一个商品页:未登录时显示公开价格和简介,登录后显示会员价和库存。你可以选择统一 canonical 到公开地址,也可以选择为登录状态单独声明 canonical。两种做法成立的条件不同。
统一 canonical 成立的条件: 登录态内容只是同一意图的增强展示,核心主题、标题和主要信息一致,且登录态页面不需要被独立检索。代价是登录态下的差异化内容不会被单独索引,但避免了重复页判断。
按条件拆分 canonical 成立的条件: 登录态和未登录态对应不同用户意图,例如一个面向普通访客,一个面向已认证用户且内容结构差异大。代价是维护两套声明,且必须确保每套都能稳定返回,否则容易产生冲突。
一个可执行的判断动作是:把两种条件下的主内容各取前 200 个非空白字符,人工判断它们是否在回答同一个问题。如果答案相同,统一 canonical 更省事;如果答案不同,才考虑拆分。这个动作的结果直接决定下一步是修改模板还是保持现状。
假设某页面在未登录时返回的 canonical 是 https://example.com/p/1,在登录后返回的 canonical 也是同一个地址,但登录后主内容多出一段“仅会员可见”的说明。此时不要立刻改 canonical。先确认这段说明是否出现在 HTML 源码中,还是由 JavaScript 在登录后注入。如果只在渲染后出现,而源码中 canonical 仍一致,那么抓取工具看到的仍是同一页,差异主要影响用户体验,不影响规范判断。
如果登录后源码中的 canonical 变成了 https://example.com/p/1?member=1,则说明模板在登录条件下生成了带参数的规范地址。此时需要检查该参数是否有实际独立内容。如果没有,应把 canonical 改回不带参数的公开地址。这个动作的结果是消除声明冲突,后续再抓取时才能得到稳定对照。
对照的终点不是“发现不同”,而是决定改什么、不改什么。如果 canonical 声明一致且主内容意图一致,不需要为了设备或登录差异做额外处理,只需确认移动端可正常渲染。如果 canonical 声明冲突,优先修复模板中的条件输出,再重新抓取验证。
需要提醒的是,抓取量或索引量在修改后短期波动,不能单独证明处理正确,因为缓存、抓取预算和重新评估都需要时间。更可靠的验证是:固定基准请求,重复对照流程,确认 canonical 声明在所有条件下是否回到一致或按预期分流。只有声明稳定,后续的索引判断才有意义。