go爬虫增量采集核心在于分层去重与状态持久化:url级去重用内存映射,内容指纹用sha256提取正文后计算,断点需分离存储success/failure状态并强制周期刷新。

Go语言爬虫做增量采集,核心不是“要不要去重”,而是“在哪一层去重、用什么粒度去重、状态存哪、断在哪”。不区分场景硬套 sync.Map 或 Redis,轻则重复抓取浪费带宽,重则漏更新、丢断点。
URL级去重 vs 内容级指纹:别混用同一套逻辑
URL去重解决的是“同一个地址不重复请求”,适合种子稳定、结构清晰的站点(如新闻列表页分页);内容指纹解决的是“页面URL变了但正文没变”,适合 SPA、参数扰动或 CDN 缓存导致 URL 不同但内容雷同的场景。
-
sync.Map或map[string]struct{}仅适用于内存级 URL 去重,进程重启即失效,不能当断点依据 - 内容指纹必须基于稳定字段:推荐用
sha256.Sum256(content)而非md5,避免哈希碰撞;若页面含动态时间戳或广告位,需先用goquery剔除无关 DOM 节点再计算 - 不要对整个 HTML 做指纹——提取正文文本后去空格/换行再哈希,体积小、抗噪强
断点续爬必须持久化状态,且区分 success/failure
Pholcus 的 success.go 和 failure.go 分离设计很务实:成功记录要存 URL + 指纹 + 时间戳,失败记录则需额外存错误类型(net/http: request canceled、i/o timeout)、重试次数、最后尝试时间。否则续爬时无法判断该跳过还是重试。
- 文件存储建议用追加写 + 行格式(每行 JSON),避免全量加载;不要用 SQLite 或 BoltDB——高并发写入易成瓶颈
- Redis 存指纹时,用
SET类型存 URL,用HASH存指纹(HSET page_fingerprint <url><sha256></sha256></url>),方便原子比对 - 每次成功抓取后,必须先落盘指纹,再更新 URL 状态;顺序颠倒会导致“已存指纹但任务未标记完成”,下次启动误判为失败
增量更新策略:什么时候该重抓,什么时候只比对
全量重抓成本高,但盲目依赖指纹也会出问题——比如某电商详情页价格字段被 JS 动态注入,静态 HTML 指纹不变,实际数据已更新。
- 对高价值字段(价格、库存、状态),必须单独提取并参与指纹计算,或另建轻量级校验字段(如
price_selector对应的文本) - 设置强制刷新周期:即使指纹一致,超过 24 小时也触发一次重抓,防静态缓存欺骗
- 失败重试不参与增量逻辑——连续 3 次超时就进 failure 队列,由后台定时扫描,而非混在主爬取流里反复塞回 channel
真正难的不是算指纹或写断点,而是把“URL 是否见过”“内容是否真变了”“这次失败是临时还是永久”三个判断拆开、落库、可查。少一个环节,增量就变成伪增量。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











