跳转到主内容
极星编程网:以代码为星,赴技术山海!

Python爬虫怎么做增量爬取_Redis记录已爬URL的MD5哈希值实现数据去重防重复

用Redis存URL的MD5而非原始URL,因MD5长度固定、可标准化去重;但需先归一化URL(去斜杠、排序并解码query),再用SET的sismember+sadd原子操作判断插入,配合连接池防并发问题。 为什么用 Redis 存 URL 的 MD5 而不是原始 URL? 因为原始 URL 长度不固定,有的带动态参数、乱序 query、session_id,直接比对极易误判重复;MD5 固定 32 字符,标准化后去重更稳。但注意:
hashlib.md5(url.encode()).hexdigest()
前必须统一处理 URL:去掉末尾斜杠、排序 query 参数、解码 URL 编码(
urllib.parse.unquote
),否则相同语义的 URL 会算出不同哈希。 常见错误现象:
redis.exceptions.ConnectionError
或去重失效——多半是没做 URL 归一化,或 Redis 连接未复用(每次新建连接导致性能崩)。 归一化示例:
https://a.com/?b=1&a=2
和
https://a.com/?a=2&b=1
必须转成同一串 MD5 别用
url.strip('/') + '?' + '&'.join(sorted(query_pairs))
这种手写拼接,改用
urllib.parse.urlunparse
+
urllib.parse.parse_qs
安全重组 Redis 连接建议用单例或连接池(
redis.ConnectionPool
),避免 爬虫 并发高时连接耗尽 如何用 Redis 的 SET 实现高效去重判断? 用
SET
类型存 MD5,靠
redis_client.sismember('urls_seen', md5)
判断是否已存在,再用
redis_client.sadd('urls_seen', md5)
尝试插入——原子操作,天然防竞态。别用 GET/SET 组合,那是自己造 race condition。 性能影响明显:单次判断 + 插入平均 redis_client.expire('urls_seen', 604800),否则集合无限增长。 立即学习 “ Python免费学习笔记(深入) ”; Python 3.14.3 微软官方的 Python 扩展,是 VS Code 安装量最高的扩展(209M+)。集成 IntelliSense(通过 Pylance)、调试(通过 Python Debugger)、代码检查、格式化、重构和单元测试等功能。支持 Jupyter Notebook、虚拟环境管理和多 Python 版本切换。 下载 别把整个 URL 当 key(如
redis_client.get(url)
),key 长度超限且无法批量查 避免用
HSET
存 URL→状态映射,纯属浪费内存和 CPU 如果要支持清理旧数据,改用
zset
+ 时间戳分值,但增量爬取通常不需要这么复杂 增量爬取时怎么保证“新 URL 不漏、旧 URL 不重”? 核心逻辑就一句: 先查 MD5 是否存在,不存在才请求 + 解析 + 入库 + 写入 Redis 。关键在“解析”环节——新页面里提取的子链接,必须同样走一遍归一化 + MD5 +
sismember
流程,不能跳过。 容易踩的坑是“只对种子 URL 做去重,子链接放行”。结果就是首页抓 10 次,每次都把全站链接又扫一遍。还有人把去重逻辑写在入库后,导致重复请求已失效的 URL(比如 404 页面反复抓)。 所有待爬 URL(无论来源)必须在
requests.get()
前完成去重校验 如果用 Scrapy,把去重逻辑塞进
start_requests
和
parse
里的
response.follow()
之前 Redis 写失败(如网络抖动)不能静默吞掉,得 fallback 到内存 set() 或抛异常中断,否则漏去重 Redis 挂了或网络超时怎么办? 别让整个爬虫停摆。加一层简单容错:
try/except redis.ConnectionError
,捕获后降级到本地
set()
缓存本次运行的 MD5,同时打日志告警。但注意:本地 set 只能保本次进程内去重,重启即丢,所以必须标清楚“降级模式”,避免误以为长期可靠。 另一个现实问题是 Redis 内存爆了——如果爬的站点 URL 量极大(千万级),
SET
内存占用线性增长,这时得考虑布隆过滤器(
pyreBloom
或 RedisBloom 模块),但会引入误判率(
false positive
)。普通中小规模爬取,坚持用
SET
最省心。 别在 except 里 sleep(1) 后重试,可能拖慢整个爬虫节奏 本地降级的
seen_urls = set()
必须是模块级变量,不能每次函数调用都新建 如果用 Docker 部署,检查 Redis 容器内存 limit,
docker stats
看实际 RSS 占用 事情说清了就结束。真正难的不是写那几行
sismember
,而是 URL 归一化的边界 case——比如带 fragment 的 URL 是否该去掉
#xxx
,比如大小写敏感的域名要不要强制小写。这些细节不抠准,Redis 再快也白搭。

相关文章