分布式爬虫的去重与任务调度:URL指纹、布隆过滤器与幂等落库
发布时间: 2026-10-09 10:19:37
阅读量: 26 人次
爬虫最贵的浪费,是同一页抓了三遍
单机爬虫跑得好好的逻辑,一放到多节点就出问题:同一个页面被不同节点重复抓取、任务队列里积压着大量重复 URL、数据库里同一份数据写进去两次。这些问题的成本是双份的——既浪费了带宽和代理 IP 的调用量,也让下游的数据校验和清洗白做一遍。本文把分布式爬虫里去重与调度的四个环节讲清楚:URL 规范化、指纹分层、布隆过滤器与精确去重的分工、任务领取与幂等落库。
一、去重之前,先做 URL 规范化
同一个页面在站内往往有多种 URL 写法,不做规范化,任何去重都会失效。常见的差异包括:查询参数顺序不同、大小写不一致、默认端口显式写出、末尾斜杠有无、URL 编码方式不同、锚点片段被带上、以及各类跟踪参数。
规范化规则要满足两个要求:写在一处、被所有节点共用。规则一旦散落在各个节点的代码里,很快就会因为改动不同步而出现"这台机器认为重复、那台机器认为不重复"的诡异现象,排查成本极高。规则本身建议保持保守——只合并确定等价的写法,拿不准的宁可不去重,也不要错误合并把不同页面当成一个。
规范化规则要满足两个要求:写在一处、被所有节点共用。规则一旦散落在各个节点的代码里,很快就会因为改动不同步而出现"这台机器认为重复、那台机器认为不重复"的诡异现象,排查成本极高。规则本身建议保持保守——只合并确定等价的写法,拿不准的宁可不去重,也不要错误合并把不同页面当成一个。
二、指纹要分层,一层管不了一件事
1、URL 指纹:防重复抓
对规范化后的 URL 取哈希,得到 URL 指纹,用来判断"这个地址抓过没有"。它解决的是抓取动作的重复,粒度是地址。
2、内容指纹:防重复存
同一份内容可能有多个地址(列表页分页、参数不同但结果相同、移动版与桌面版),只用 URL 指纹挡不住。对正文主体取哈希作为内容指纹,可以在写入前识别出"地址不同、内容相同"的情况。注意取哈希前要先剥离时间戳、随机推荐位这类每次都变的噪声,否则指纹每次都不同。
3、业务主键:防重复入库
最终落到数据库时,用业务上的唯一标识(如站点加内容 ID)做唯一约束,这是最后一道闸。前两层是优化,这一层是底线——它保证即使前面全部失效,数据也不会真的写重。
对规范化后的 URL 取哈希,得到 URL 指纹,用来判断"这个地址抓过没有"。它解决的是抓取动作的重复,粒度是地址。
2、内容指纹:防重复存
同一份内容可能有多个地址(列表页分页、参数不同但结果相同、移动版与桌面版),只用 URL 指纹挡不住。对正文主体取哈希作为内容指纹,可以在写入前识别出"地址不同、内容相同"的情况。注意取哈希前要先剥离时间戳、随机推荐位这类每次都变的噪声,否则指纹每次都不同。
3、业务主键:防重复入库
最终落到数据库时,用业务上的唯一标识(如站点加内容 ID)做唯一约束,这是最后一道闸。前两层是优化,这一层是底线——它保证即使前面全部失效,数据也不会真的写重。
三、布隆过滤器与精确去重怎么分工
1、布隆过滤器适合做第一道闸
它的优势是内存占用极小、查询极快,适合在入队前快速过滤掉绝大多数已抓地址。代价是存在假阳性:没抓过的地址可能被判为抓过。这个方向是"漏抓",而不是"重复抓"。
2、精确集合做确认层
布隆过滤器判为"可能抓过"之后,再用精确集合或数据库唯一索引确认一次。这样既保住了内存优势,又不会因为误判丢掉重要页面。判断标准很直接:这个页面漏抓的代价,是否值得多花一次精确查询。
3、规模上来之后要分片
数据量增长后,单实例的过滤器会逐渐吃满内存,精确集合的查询压力也会上升。按站点或按指纹前缀分片,是成本最低的扩展方式,同时天然降低了单点故障的影响范围。
它的优势是内存占用极小、查询极快,适合在入队前快速过滤掉绝大多数已抓地址。代价是存在假阳性:没抓过的地址可能被判为抓过。这个方向是"漏抓",而不是"重复抓"。
2、精确集合做确认层
布隆过滤器判为"可能抓过"之后,再用精确集合或数据库唯一索引确认一次。这样既保住了内存优势,又不会因为误判丢掉重要页面。判断标准很直接:这个页面漏抓的代价,是否值得多花一次精确查询。
3、规模上来之后要分片
数据量增长后,单实例的过滤器会逐渐吃满内存,精确集合的查询压力也会上升。按站点或按指纹前缀分片,是成本最低的扩展方式,同时天然降低了单点故障的影响范围。
四、任务领取、超时与重入
多节点场景下,任务从队列到节点的过程必须保证"一个任务只被一个节点领走",这依赖队列本身的原子领取能力,而不是靠应用层的先查后改。任务被领走后要有租约和心跳:节点正常就持续续期,节点崩溃或卡死,租约到期后任务自动回到队列,避免因为一个节点掉线让整批任务永久卡住。
重入意味着同一个任务可能被执行两次,所以抓取与写入都必须按"会被重复执行"来设计。另外要专门处理代理出口切换引起的重复:出口变化导致的连接失败会触发重试,同一个地址可能在短时间内被重新抓取,如果没有幂等兜底,重复数据就会从这条路径进来。
重入意味着同一个任务可能被执行两次,所以抓取与写入都必须按"会被重复执行"来设计。另外要专门处理代理出口切换引起的重复:出口变化导致的连接失败会触发重试,同一个地址可能在短时间内被重新抓取,如果没有幂等兜底,重复数据就会从这条路径进来。
五、幂等落库的三种常见做法
一是唯一索引配合 upsert:写入冲突时更新而不是报错,让重复写入自然收敛。二是版本号或更新时间比较:只有新数据确实更新时才覆盖,避免旧任务迟到把新数据覆盖回去。三是批量写入时逐条处理冲突:批量接口遇到部分冲突容易整批回滚,需要明确冲突策略,否则会出现"明明抓到了却没写进去"。
还要注意抓取状态与数据写入的一致性:先落库再标记完成,还是先标记再落库,决定了崩溃时会漏数据还是多数据。通常选择先落库、后标记,配合幂等写入,崩溃恢复后最多重复执行一次,不会丢数据。
还要注意抓取状态与数据写入的一致性:先落库再标记完成,还是先标记再落库,决定了崩溃时会漏数据还是多数据。通常选择先落库、后标记,配合幂等写入,崩溃恢复后最多重复执行一次,不会丢数据。
总结
分布式爬虫的去重不是一个开关,而是一条链:先做 URL 规范化,让等价地址长得一样;再分层做指纹,URL 指纹防重复抓、内容指纹防重复存、业务主键防重复入库;布隆过滤器负责用极小的内存挡住绝大多数重复,精确集合兜住误判;任务队列用原子领取加租约心跳保证不丢不卡;最后用幂等写入收口,包括代理出口切换引起的重试重复。链条上每一环都不复杂,但缺一环,重复数据就会从那一环漏进来。
关于山水代理
山水代理 私密代理提供按时、按量、按流量三种计费方式,去重做好之后,代理调用量会明显下降,按量计费更划算。
隧道代理 提供固定入口与自动 IP 轮换,出口切换对任务透明,重试重复交给幂等逻辑兜底即可。
欢迎随时 免费试用,先在小规模队列上验证去重链路。
隧道代理 提供固定入口与自动 IP 轮换,出口切换对任务透明,重试重复交给幂等逻辑兜底即可。
欢迎随时 免费试用,先在小规模队列上验证去重链路。


黑公网安备 23100002000084号