做爬虫的同行最喜欢晒“我把速度提升了三倍”这种话。盯错指标了。速度很少是真正的瓶颈,能不能断点重跑才是。
你的爬虫跑了四个小时,中间有个地址挂了,你能不能在三十秒内单独重跑那一条?还是说整个四小时的活都得从头来一遍?如果答案是“从头来”,那你搞的不是爬虫,是一场漫长的祈祷。
一个可以随时重跑的任务,长下面这样。三个条件,缺一不可。
第一,输入是显式声明的、落盘保存的。运行开始前,每一个地址、每一组参数都得写进队列或者数据集里,稍后你能重新读到这份输入清单。第二,输出可以按输入逐条追溯。你得能问“地址X成功了吗”并且得到一个确切的“是”或“否”,而不是“嗯,整个任务跑完了,大概没问题”。第三,失败是最优先级的记录。出错的输入得带着报错原因单独进一个数据集或队列,等着被喂给下一次重跑任务。三点全满足,“重跑所有失败项”就是一行命令的事。少任何一点,故障恢复都变成手工考古。
核心思路很简单:把输入和输出拆成两份独立的数据集。代码骨架大概是这样——先初始化,拿到输入参数,然后把所有要抓的地址推进队列。这个时候用地址做唯一键有个巨大的好处:重复添加同一批地址并不会产生重复任务,天然幂等。接着进入处理循环,从队列里逐个取地址,请求成功就把结果推进“成功”数据集,顺手把队列项标记为已处理;抛出异常就把它推进“失败”数据集,同时记下地址、报错内容和失败时间戳,同样标记为已处理,别让程序盲目重试。队列、成功集、失败集,三件套。
说个真实案例。有个播客转录项目原来是六小时起步的批处理任务。只要有一集转写失败——音频下载超时也好,模型抽风也好——恢复路径是:翻日志找出那个挂了地址,手工拼一个单条任务跑一遍,然后祈祷第二次能成。换成“队列加双数据集”模式之后,所有失败地址都乖乖躺在一个专用数据集里,带着报错信息和时间戳,一目了然。“重跑昨天失败项”就是一个按钮的事:打开失败数据集,把里面的记录作为输入推给新一轮任务。原来成功的那批数据不用再处理,新结果直接追加进去就行。
三十分钟的手动排查,压缩到三十秒就能搞定。同一个爬虫,同样的页面选择器,同样的模型——唯一的区别是运行时的数据结构。别优化错的数字了。
热门跟贴