灰度只放行少量路径时,真正被检验的是规则例外是否被完整表达。小流量本身不会制造问题,它只是把全量发布时被流量掩盖的例外条件提前放大。判断该保留、改写还是退出灰度,关键看例外是路径级、参数级还是爬虫级,以及你能否在不影响已放行流量的前提下把例外补回去。
全量发布时,robots.txt 通常只写一条总规则,例如 Disallow: /search。灰度阶段为了让测试爬虫能抓到部分页面,有人会临时加一条更宽松的规则,或者把总规则改成带通配符的版本。问题就出在这里:灰度只验证了被放行的几个 URL,没有验证被排除的那一类。等全量上线,例外条件仍然缺失,原本该被限制的路径反而因为规则顺序或通配符匹配被放行。
可区分的原因至少有三类。第一类是路径级例外,比如 Disallow: /tmp/ 后面又写了 Allow: /tmp/public/,但灰度只测了 /tmp/public/,没测 /tmp/private/ 是否仍被挡。第二类是参数级例外,灰度放行的是不带参数的干净 URL,全量后带跟踪参数的 URL 才大量出现,而规则里没有针对参数的匹配。第三类是爬虫级例外,灰度用的测试 UA 与正式爬虫不同,规则里针对特定 UA 的分组在灰度中根本没被触发。这三类的证据不同:路径级看请求日志中 200 与 403 的分布,参数级看同一路径带参和不带参的抓取差异,爬虫级看不同 UA 命中的规则分组。把这三类混在一起,就会误以为灰度通过等于全量安全。
保留适用于例外本身是临时的。比如灰度期间需要让测试爬虫访问 /preview/,而全量后这个目录会整体下线。此时保留灰度规则的前提是:你有明确的删除动作和删除时间点,并且删除后重新验证一次全量规则。如果没有删除动作,保留就等于把临时例外永久写进了公开文件。
改写适用于例外是真实且长期存在的。比如站内搜索既有需要限制的 /search?q=,又有需要放行的 /search/help。这时不该保留两条互相覆盖的规则,而应把它们合并成一组顺序明确、前缀不重叠的规则。改写的验证动作是:用灰度中已经抓过的 URL 和灰度中没抓过的同类 URL 各取一组,分别请求,确认结果符合预期。只有两组都符合,改写才算完成。
退出适用于灰度规则已经污染了全量语义,且无法通过局部修改恢复。典型情况是灰度期间把 Disallow: / 临时改成 Allow: /,再靠后续规则逐条排除。这种写法在全量后极难维护,因为任何新增路径都默认放行。退出的动作是回到上一版全量规则,重新只补必要的例外,而不是在灰度版本上继续打补丁。退出的代价是灰度期间积累的抓取数据可能失效,需要重新观察一轮。
假设某站点灰度时只放行 /docs/a 到 /docs/j 十个 URL,规则写成 Allow: /docs/ 并放在 Disallow: /docs/ 之前。灰度期间这十个 URL 都能被抓到,看起来通过。全量后 /docs/ 下还有 /docs/archive/ 和 /docs/draft/,它们被同一条 Allow 放行,而原本的 Disallow 被顺序覆盖。这里的关键证据不是抓取量变化,而是规则匹配结果:把全量路径列表与规则逐条比对,看哪些路径命中了 Allow 而不是 Disallow。抓取量下降或上升都不能单独证明规则正确,因为抓取量还受站点地图提交、内链变化和服务器响应速度影响。
此时合理的下一步是改写而不是退出:把 Allow: /docs/ 收窄为 Allow: /docs/a 到 /docs/j 对应的实际前缀,或者把 Disallow 放到更靠前的位置并补上 /docs/archive/ 与 /docs/draft/。改写后重新用灰度中未覆盖的同类路径验证一次,确认它们命中限制规则,再考虑全量。
第一,robots.txt 的抓取限制不等于索引移除。即使某条路径被 Disallow 挡住,如果它已经被其他页面链接或已被收录,搜索结果中仍可能出现该 URL。灰度验证只能证明抓取行为符合规则,不能证明索引状态同步变化。第二,不同搜索引擎对通配符和规则顺序的支持并不完全一致,灰度中使用的测试方式如果只覆盖一种爬虫,全量后另一种爬虫可能命中不同结果。必要时应分别核查目标搜索引擎的规则说明,而不是假设一套规则通用。
站点地图也不保证收录,把灰度放行的 URL 写进站点地图,只能说明你希望它们被抓取,不能说明它们一定被处理。灰度暴露例外之后,真正要决定的是规则本身是否表达了完整意图;如果规则表达不完整,增加流量或提交更多 URL 只会扩大偏差。