目录
一、背景:单机爬虫的五大瓶颈
在千万级数据抓取需求下,传统单机爬虫面临严峻挑战:
- IP封锁阈值:单个IP日均请求上限普遍低于5万次
- 存储性能瓶颈:内存型去重库(如set)突破百万级后性能骤降
- 网络带宽限制:单机下载带宽利用率峰值仅达60%
- 故障恢复困难:断点续爬需要手动维护复杂状态
- 扩展成本高昂:垂直扩展(升级硬件)成本呈指数增长
分布式爬虫优势:
- 横向扩展:线性提升抓取吞吐量
- 动态伸缩:根据负载自动增减节点
- 高可用性:单节点故障不影响整体任务
二、Scrapy-Redis架构深度解析
1. 架构拓扑图
[分布式节点集群]
├─ Node1: Spider1 → Redis调度队列
├─ Node2: Spider2 → Redis数据管道
├─ Node3: Spider3 → BloomFilter去重
└─ ...
↓
[统一存储层]
├─ MySQL/ClickHouse(结构化存储)
└─ HDFS/MinIO(非结构化存储)
2. 核心组件对比
模块 | 原生Scrapy | Scrapy-Redis |
---|---|---|
调度器 | 内存队列 | Redis优先队列 |
去重机制 | 内存set | RedisSet/BloomFilter |
请求分发 | 单机处理 | 多节点协同消费 |
状态持久化 | 手动维护 | 自动持久化 |
扩展性 | 垂直扩展 | 水平扩展 |
三、环境搭建与核心配置
1. 基础环境部署
# 安装Scrapy-Redis
pip install scrapy-redis redis
# 启动Redis集群(Docker版)
docker run -d --name redis-master -p 6379:6379 redis
docker run -d --name redis-replica1 -p 6380:6379 redis replicaof redis-master 6379
2. Scrapy项目配置
# settings.py核心配置
SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 启用Redis调度
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # Redis去重
REDIS_URL = 'redis://:password@192.168.1.100:6379' # Redis连接
SCHEDULER_PERSIST = True # 保持爬虫状态
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue' # 优先级队列
# 数据管道配置
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
}
四、分布式爬虫核心实现
1. 改造原生Spider
from scrapy_redis.spiders import RedisSpider
class DistributedSpider(RedisSpider):
name = 'cluster_spider'
redis_key = 'myspider:start_urls' # Redis起始键
def parse(self, response):
# 提取详情页链接
detail_links = response.css('a.detail::attr(href)').getall()
for link in detail_links:
yield scrapy.Request(link, callback=self.parse_detail)
# 自动翻页逻辑
next_page = response.css('li.next-page a::attr(href)').get()
if next_page:
yield self.make_next_request(next_page)
def make_next_request(self, url):
# 携带优先级参数
return scrapy.Request(url,
priority=100, # 优先级权重
meta={'retry_times': 0} # 重试次数记录
)
2. 布隆过滤器集成
from pybloom_live import ScalableBloomFilter
class BloomDupeFilter(RFPDupeFilter):
def __init__(self, server, key):
super().__init__(server, key)
self.bf = ScalableBloomFilter(
initial_capacity=1000000,
error_rate=0.001
)
def request_seen(self, request):
fp = self.request_fingerprint(request)
if fp in self.bf:
return True
self.bf.add(fp)
self.server.sadd(self.key, fp)
return False
五、五大性能优化策略
1. 动态优先级调整
# 根据URL深度自动调整优先级
def adjust_priority(request):
depth = request.meta.get('depth', 0)
return 1000 // (depth + 1)
2. 智能限速策略
AUTOTHROTTLE_ENABLED = True
AUTOTHROTTLE_START_DELAY = 1.0
AUTOTHROTTLE_MAX_DELAY = 60.0
AUTOTHROTTLE_TARGET_CONCURRENCY = 50.0
3. 连接池优化
# 自定义Redis连接池
import redis
pool = redis.ConnectionPool(
host='cluster.example.com',
port=6379,
max_connections=100,
socket_timeout=10
)
REDIS_PARAMS = {'connection_pool': pool}
4. 数据分片存储
class ShardingPipeline:
def process_item(self, item, spider):
# 根据时间分片存储
ts = int(time.time() * 1000)
shard_id = ts % 16 # 16个分片
self.conn.hset(f"data:shard:{shard_id}", ts, json.dumps(item))
5. 心跳监控系统
# 节点健康检查脚本
import redis
r = redis.StrictRedis()
def node_heartbeat():
while True:
r.zadd('nodes:alive', {NODE_ID: time.time()})
time.sleep(30)
六、实战:新闻聚合平台数据抓取
1. 集群架构
-
规模:10节点Docker集群(8核16G/节点)
-
数据目标:日均抓取100万新闻条目
-
技术栈:
- Scrapy-Redis调度中心
- RedisBloom去重过滤
- Kafka实时数据管道
- Prometheus+Granfana监控
2. 性能指标
指标 | 单机模式 | 分布式模式 |
---|---|---|
请求处理QPS | 120 | 8500 |
去重效率 | 98.5% | 99.99% |
故障恢复时间 | 15分钟+ | <30秒 |
日均抓取量 | 20万 | 120万+ |
七、总结
1. 核心收获
- 吞吐量飞跃:线性扩展提升40倍+处理能力
- 成本优化:硬件利用率提升至85%以上
- 系统健壮性:实现7×24小时稳定运行