限流发生后,最该做的不是继续重试,而是先把已经拿到的结果落盘并标记完整性,再决定是否续跑。判断依据很简单:如果脚本把结果只放在内存里、等全部关键词查完才统一写文件,那么限流一来,前面查到的名次很可能一起丢掉;如果每查完一个就追加写入并记录状态,损失就被限制在未完成的那一段。
限流通常有两种可观察表现。一种是请求被明确拒绝,返回错误码或提示频率过高;另一种是请求看似成功,但返回空内容、重复内容或明显异常的数据。两者的处理方式不同。
如果遇到的是明确拒绝,说明调用通道还在,只是节奏被约束。此时应停止继续发请求,把内存中尚未写入的结果立即刷到磁盘,然后进入等待。如果遇到的是静默异常,风险更大,因为脚本可能把空结果当成“没有排名”写进文件,污染已有数据。此时应暂停写入,先对这批返回做抽样核对,确认是限流还是关键词本身没有结果,再决定是否保留。
一个可区分的证据是:同一批关键词里,如果前面若干条返回正常、后面突然连续为空,且空结果集中在短时间内,更像是被限流;如果空结果分散出现、与关键词难度或生僻程度相关,更可能是这些词本身查询不到。这个判断只是概率性的,不能单凭空结果数量就下结论。
当限流只是暂时的、稍后可以继续调用时,优先保护已有结果并支持续跑。具体动作是维护一个进度文件,记录每个关键词的处理状态:已完成、已失败、待处理。每查完一个关键词,就把它追加写入结果文件,同时更新进度文件。
这样做的直接结果是:下次启动脚本时,可以先读进度文件,跳过已完成的关键词,只处理待处理和失败的。相比重跑全量,续跑节省调用次数,也降低再次触发限流的概率。代价是需要处理文件写入的原子性——如果写入过程中脚本被中断,可能出现结果文件和进度文件不一致。一个务实做法是让进度文件最后写入,结果先落盘,这样即使不一致,也只会表现为“结果已存在但进度未更新”,续跑时重复查询少量关键词,而不是丢结果。
假设一次批量查询有100个关键词,查到第40个时被限流。如果每查完一个就落盘,已完成的39条结果安全;如果脚本每20条统一写一次,那么第21到39条可能丢失。这个例子只用于说明落盘频率与损失范围的关系,实际频率应根据单次调用成本和可接受的重复量来定。
如果限流持续时间不确定,或者调用配额有限、续跑代价高,那么保护已有结果的优先动作是冻结快照:把当前结果文件复制一份,加上时间标记,作为本次查询的存档。之后所有分析和决策都基于这份快照,而不是基于一个还在变动、可能被后续写入覆盖的文件。
冻结快照之后,下一步是核对完整性。可以统计已完成关键词数量、失败数量、空结果数量,并与预期总量对比。如果缺口集中在尾部,说明限流发生在查询后期;如果缺口分散,说明限流可能间歇出现,已有结果的代表性更弱。这个核对结果会影响下一步:缺口小且集中在尾部时,可以等限流解除后补齐;缺口大或分散时,更稳妥的做法是放弃本次数据,改用更小批量重新查询,而不是在残缺数据上做排名判断。
需要注意,快照只能保护“已经拿到的结果”,不能证明这些结果本身正确。如果限流期间混入了空结果或异常数据,快照也会把它们一起保存。因此冻结快照的同时,应记录本次查询的时间、关键词范围和失败标记,方便后续区分哪些条目可信、哪些需要复核。
实际工作中,脚本维护者、数据使用者和决策者常对同一份限流后的结果有不同理解。维护者认为“大部分查到了”,使用者发现“有几个词是空的”,决策者关心“能不能据此调整”。分歧的根源往往是大家看的是不同版本的文件,或者对“完成”的定义不同。
把分歧转成可核对的项目,可以列出三列:关键词、状态、证据。状态分为正常返回、明确失败、空结果、未处理;证据记录该条结果来自哪个文件、哪个时间点。这样讨论时不再争论“结果准不准”,而是核对具体条目属于哪一类,以及缺失部分是否影响当前决策。如果缺失的关键词恰好是决策重点,就应该补齐后再判断;如果缺失的是长尾词,且已有结果覆盖了主要目标词,则可以先基于现有数据做初步判断,并标注局限。
一个可立即执行的动作是:在脚本里加入“先写结果、后写进度、失败也记录”的逻辑,并在每次运行开始时检查是否存在未完成的进度文件。这样限流发生时,已有结果不会因为进程退出而丢失,下一次运行也能从断点继续。
例外情况是:如果工具本身不允许保存中间结果,或者调用条款限制结果留存,那么保护已有结果的方式就要改为即时导出到允许的位置,并避免在脚本内长期缓存。此时应优先遵守工具的使用约束,而不是单纯追求数据完整。具体工具是否允许中间结果留存、是否有调用频率说明,需要以该工具的当前文档和条款为准,不能凭通用经验假定。