分布式爬虫去重:Python + Redis实现高效URL去重

简介: 分布式爬虫去重:Python + Redis实现高效URL去重
  1. 引言
    在互联网数据采集(爬虫)过程中,URL去重是一个关键问题。如果不对URL进行去重,爬虫可能会重复抓取相同页面,导致资源浪费、数据冗余,甚至触发目标网站的反爬机制。
    对于单机爬虫,可以使用Python内置的set()或dict进行去重,但在分布式爬虫环境下,多个爬虫节点同时工作时,内存级的去重方式不再适用。此时,需要一个共享存储来管理已爬取的URL,而Redis凭借其高性能、低延迟和分布式支持,成为理想选择。
  2. URL去重的常见方法
    2.1 基于内存的去重(单机适用)
    Python set()
    最简单的去重方式,适用于小规模数据,但无法持久化,重启后数据丢失。
    visited_urls = set()
    if url not in visited_urls:
    visited_urls.add(url)

    抓取逻辑

    ● Bloom Filter(布隆过滤器)
    节省内存,但有一定误判率(可能误判未访问的URL为已访问),适用于海量URL去重。
    2.2 基于数据库的去重(分布式适用)
    ● Redis Set / Redis HyperLogLog
    ○ SET 结构存储URL,精确去重(100%准确)。
    ○ HyperLogLog 适用于统计不重复元素数量(有一定误差,但占用内存极小)。
    ● 关系型数据库(MySQL, PostgreSQL)
    通过UNIQUE约束去重,但性能较低,不适合高并发爬虫。
    ● 分布式键值存储(如Memcached)
    类似Redis,但功能较少,通常仅用于缓存。
  3. Redis 在分布式爬虫去重中的优势
    Redis 是一个高性能的内存数据库,支持多种数据结构,适用于分布式爬虫去重,主要优势包括:
  4. 高性能:数据存储在内存中,读写速度极快(10万+ QPS)。
  5. 持久化:支持RDB/AOF持久化,避免数据丢失。
  6. 分布式支持:可通过集群模式扩展,支持多爬虫节点共享数据。
  7. 丰富的数据结构:SET(精确去重)、HyperLogLog(近似去重)、Bitmap(位图去重)等。
  8. Python + Redis 实现分布式URL去重
    4.1 方案1:使用 Redis Set 精确去重
    import redis

class RedisUrlDedupe:
def init(self, redis_host='localhost', redis_port=6379, redis_db=0):
self.redis = redis.StrictRedis(
host=redis_host, port=redis_port, db=redis_db
)
self.key = "visited_urls"

def is_visited(self, url):
    """检查URL是否已访问"""
    return self.redis.sismember(self.key, url)

def mark_visited(self, url):
    """标记URL为已访问"""
    self.redis.sadd(self.key, url)

示例用法

deduper = RedisUrlDedupe()
url = "https://example.com/page1"

if not deduper.is_visited(url):
deduper.mark_visited(url)
print(f"抓取: {url}")
else:
print(f"已访问: {url}")
优点:
● 100% 准确,无误差。
● 适用于中小规模爬虫(百万级URL)。
缺点:
● 存储所有URL,内存占用较高。
4.2 方案2:使用 Redis HyperLogLog 近似去重
如果允许少量误差(~0.8%),可使用HyperLogLog节省内存:
class RedisHyperLogLogDedupe:
def init(self, redis_host='localhost', redis_port=6379, redis_db=0):
self.redis = redis.StrictRedis(
host=redis_host, port=redis_port, db=redis_db
)
self.key = "hll_visited_urls"

def is_visited(self, url):
    """检查URL是否可能已访问(可能有误判)"""
    before = self.redis.pfcount(self.key)
    after = self.redis.pfadd(self.key, url)
    return after == 0  # 如果添加后计数未变,说明可能已存在

示例用法

hll_deduper = RedisHyperLogLogDedupe()
url = "https://example.com/page1"

if not hll_deduper.is_visited(url):
print(f"抓取: {url}")
else:
print(f"可能已访问: {url}")
优点:
● 内存占用极低(12KB可存储数亿URL)。
● 适用于超大规模爬虫(如全网爬取)。
缺点:
● 有少量误判(可能将未访问的URL误判为已访问)。
4.3 方案3:使用 Redis Bloom Filter(需安装RedisBloom模块)
Redis 官方提供 RedisBloom 模块,支持布隆过滤器(需额外安装):

需确保Redis服务器加载了RedisBloom模块

class RedisBloomFilterDedupe:
def init(self, redis_host='localhost', redis_port=6379, redis_db=0):
self.redis = redis.StrictRedis(
host=redis_host, port=redis_port, db=redis_db
)
self.key = "bloom_visited_urls"

def is_visited(self, url):
    """检查URL是否可能已访问(可能有误判)"""
    return self.redis.execute_command("BF.EXISTS", self.key, url)

def mark_visited(self, url):
    """标记URL为已访问"""
    self.redis.execute_command("BF.ADD", self.key, url)

示例用法

bloom_deduper = RedisBloomFilterDedupe()
url = "https://example.com/page1"

if not bloom_deduper.is_visited(url):
bloom_deduper.mark_visited(url)
print(f"抓取: {url}")
else:
print(f"可能已访问: {url}")
优点:
● 内存占用低,误判率可控。
● 适用于海量URL去重。
缺点:
● 需要额外安装RedisBloom模块。

  1. 性能优化与对比
    方法 准确率 内存占用 适用场景
    Redis Set 100% 高 中小规模爬虫(<1000万URL)
    Redis HyperLogLog ~99.2% 极低 超大规模爬虫(允许少量误判)
    Redis Bloom Filter 可调 中 海量URL(需额外模块)
    优化建议:
  2. 短URL优化:存储URL的MD5或SHA1哈希值(减少内存占用)。
  3. 分片存储:按域名或哈希分片,避免单个Key过大。
  4. TTL过期:设置过期时间,避免长期累积无用URL。
  5. 结论
    在分布式爬虫中,Redis 是URL去重的理想选择,支持多种数据结构:
    ● 精确去重 → Redis Set
    ● 低内存消耗 → HyperLogLog
    ● 可控误判率 → Bloom Filter
    通过合理选择方案,可以显著提升爬虫效率,避免重复抓取。本文提供的Python代码可直接集成到Scrapy或其他爬虫框架中,助力高效数据采集。
相关文章
|
4月前
|
Java 数据处理 索引
(Pandas)Python做数据处理必选框架之一!(二):附带案例分析;刨析DataFrame结构和其属性;学会访问具体元素;判断元素是否存在;元素求和、求标准值、方差、去重、删除、排序...
DataFrame结构 每一列都属于Series类型,不同列之间数据类型可以不一样,但同一列的值类型必须一致。 DataFrame拥有一个总的 idx记录列,该列记录了每一行的索引 在DataFrame中,若列之间的元素个数不匹配,且使用Series填充时,在DataFrame里空值会显示为NaN;当列之间元素个数不匹配,并且不使用Series填充,会报错。在指定了index 属性显示情况下,会按照index的位置进行排序,默认是 [0,1,2,3,...] 从0索引开始正序排序行。
387 0
|
4月前
|
Java 数据挖掘 数据处理
(Pandas)Python做数据处理必选框架之一!(一):介绍Pandas中的两个数据结构;刨析Series:如何访问数据;数据去重、取众数、总和、标准差、方差、平均值等;判断缺失值、获取索引...
Pandas 是一个开源的数据分析和数据处理库,它是基于 Python 编程语言的。 Pandas 提供了易于使用的数据结构和数据分析工具,特别适用于处理结构化数据,如表格型数据(类似于Excel表格)。 Pandas 是数据科学和分析领域中常用的工具之一,它使得用户能够轻松地从各种数据源中导入数据,并对数据进行高效的操作和分析。 Pandas 主要引入了两种新的数据结构:Series 和 DataFrame。
578 0
|
6月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
468 2
|
6月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
417 6
|
7月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
5月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
443 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
5月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
9月前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
562 67
|
7月前
|
NoSQL Redis
Lua脚本协助Redis分布式锁实现命令的原子性
利用Lua脚本确保Redis操作的原子性是分布式锁安全性的关键所在,可以大幅减少由于网络分区、客户端故障等导致的锁无法正确释放的情况,从而在分布式系统中保证数据操作的安全性和一致性。在将这些概念应用于生产环境前,建议深入理解Redis事务与Lua脚本的工作原理以及分布式锁的可能问题和解决方案。
282 8
|
8月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
2317 7

推荐镜像

更多