结论先说:例外情况不能写成“其他情况照常处理”,而要写成可判定的条件、触发后的动作和动作失败时的退路。人工操作时,你看到页面结构异常会凭经验绕开;脚本没有这种判断力,所以必须把“什么算异常、异常时停还是降级、降级后谁来接手”写进需求。否则脚本会把本该跳过的页面当成正常页处理,批量产出错误改动,反而让后续排查更困难。
把人工经验转成脚本需求时,最常见的失误是把所有偏离预期的页面都归为“特殊情况”。实际至少可以分为三类,处理方式完全不同。
分类的意义在于:不同类型对应不同退路。数据缺失可以跳过,结构冲突可以按优先级选,业务规则例外则必须完全不碰。如果需求里只写一句“异常页面不处理”,脚本作者只能自行猜测,实现结果大概率和你的预期不一致。
可执行的例外描述,结构上接近一段伪代码的判断逻辑。你不需要会写代码,但需要用同样的严谨度表达。假设你要批量调整页面的正文摘要区,可以这样描述一条例外:
当正文容器内匹配到的段落数少于 2 时,不生成摘要,保留原内容,并写入日志“段落不足”。
这条描述包含了三个必要成分:触发条件(段落数少于 2)、动作(不生成、保留原内容)、记录(写日志)。三者缺一,脚本就无法在事后被验证。
对比一下常见的模糊写法:“内容太短的页面不要改”。问题在于“太短”没有阈值,脚本作者只能自己定,可能是 50 字,也可能是 200 字。改动上线后你发现某些页面被误改,却无法判断是阈值定错了,还是判断逻辑本身有漏洞。
还有一个容易被忽略的点:例外判断应该发生在动作之前,而不是之后。如果脚本先改了页面再检查是否异常,等于已经产生了错误改动,回滚成本远高于提前跳过。
上面的结论有一个明确的反例。如果例外条件写得过宽,脚本会频繁触发跳过,最终只处理了少量“最规整”的页面。这种情况下,脚本看起来运行正常、日志也没有报错,但实际覆盖范围和人工处理相比没有明显优势,反而多了一层维护成本。
判断是否过宽,可以看一个假设例子。假设你有 100 个目标页面,脚本跳过了其中 40 个,理由是“结构不符合预期”。这时不要急着认为脚本保守是好事,而要抽样检查这 40 个页面:如果其中大部分只是模板略有差异、人工本来也能处理,那说明例外条件定得太严,应该把这类差异转成优先级规则,而不是一律跳过。反过来,如果这 40 个页面确实涉及不同业务规则,跳过就是正确的。
换句话说,例外条件的宽严不是风格问题,而是取决于被跳过的那部分页面,人工是否真的会处理。这个判断只能靠抽样,不能靠感觉。
在正式批量执行前,选一小批页面跑一次,重点不是看“改了多少”,而是看“跳过了哪些、为什么跳过”。具体动作可以是:让脚本输出每个页面的判定结果和触发原因,然后逐条对照你的人工判断。
如果发现某条例外被频繁触发,先回头检查是条件写错了,还是这批页面本身就不适合这次改动。两种情况的下一步完全不同:前者要改需求描述,后者要缩小目标范围。这个动作的价值在于,它把“例外描述是否准确”从主观争论变成了可对照的证据。
需要提醒的是,试运行前后的对比不能只看单次数据波动。搜索需求本身会随季节和热点变化,采集时间不同也会带来差异。所以试运行的目的是验证脚本判断逻辑,而不是验证排名效果。把这两件事混在一起,很容易把正常波动误读成改动生效或失效。
例外情况很少一次写全。上线后你大概率会发现新的边界情况,这时如果需求文档没有预留变更位置,每次调整都会变成口头补充,时间一长就没人说得清当前规则是什么。
一个实用的做法是:在需求里单独列一节“已知例外”,每条标注发现时间和处理方式。新增例外时,明确它是替换旧规则还是叠加在旧规则之上。这个习惯不解决技术问题,但能避免同一类页面在不同批次里被反复用不同逻辑处理,那才是批量操作里最难排查的混乱来源。