限流发生后,最危险的动作不是停止抓取,而是让脚本带着空结果重试并覆盖本地文件。保住已有结果的关键是:把“本次抓取是否完整”与“结果文件是否有效”分开判断,未完成批次只写临时区,校验通过后才合并进主结果。
脚本日志里请求量从几百降到个位数,但生成的结果文件行数没有明显减少,甚至某些关键词还多出了记录。直觉会认为限流没有真正发生,或工具在后台补足了数据。更常见的解释有两个。
第一种解释是缓存与重试掩盖了缺口。脚本对失败请求做了重试,重试命中了本地或中间层缓存,于是同一批旧数据被反复写入,行数看似稳定,实际没有新抓取。第二种解释是写入逻辑把“请求失败”也当成一条记录,用空值或上一次的值填充,导致文件结构完整但内容陈旧。
这两种解释指向不同的处理方式:前者要检查缓存命中与重试路径,后者要检查写入前的字段校验。只看文件行数无法区分。
限流出现后,第一步是停止对主结果文件的写入,把它设为只读或复制一份带时间标记的快照。这样后续任何重试都不会污染已经确认可用的数据。快照的作用不是备份洁癖,而是让“已有结果”成为一个可回退的基线。
接着用一个可核对的证据区分上面两种解释:抽取本次运行中状态为失败的请求,逐条对照结果文件里对应关键词的记录。如果失败请求在文件中仍有非空记录,且值与上一轮完全相同,更可能是缓存或旧值回填;如果失败请求对应记录为空或缺失,则更可能是写入校验缺失。
这一步的实际动作是生成一份差异清单,而不是直接重跑脚本。差异清单决定下一步:缓存问题要调整请求头、重试间隔或缓存策略;写入问题要在合并前增加必填字段检查。
把每次运行的结果先写入临时区,是限流场景下最实用的隔离手段。临时区可以是一个独立文件或独立数据表,命名带上运行标识。只有满足以下条件才允许合并进主结果:
校验不通过时,保留临时区并标记为未完成,不合并、不删除。这样即使脚本后续被限流到无法继续,已有主结果仍然可用,未完成部分也不会以“完整文件”的假象进入下游。
假设某次计划查询 200 个关键词,脚本在抓到第 120 个时开始收到限流响应。若直接重试,可能得到 200 行文件,但其中 80 行是旧值。若先写临时区,会看到有效新记录 120 条、失败 80 条,差异清单明确。
此时的选择取决于业务需要:如果这 120 条覆盖了核心词,可以先合并并标注“部分更新”,把剩余 80 条排入下一轮;如果核心词恰好落在失败区间,则应保留快照、放弃本次合并,等待限流窗口过去后重跑缺失部分。这个判断依据是覆盖范围,而不是文件行数。
限流缓解后不要立刻恢复全量抓取,先用差异清单只补失败的关键词。补抓完成后再次校验,通过后再合并。这样做的结果是:主结果始终对应一次可解释的完整运行,而不是多次半成品拼接的混合体。
如果补抓仍然失败,就保留未完成标记并记录失败原因。请求量归零或抓取量下降本身不能证明处理正确,它也可能只是限流更严重或脚本提前退出。真正能说明问题的是差异清单是否缩小、有效记录是否增加、合并校验是否通过。
对具体工具的重试上限、缓存行为和字段校验方式,不同实现差异很大,需要以实际日志和返回内容核对,不能仅凭界面提示判断。