Python分布式爬虫完全教程:从基础原理到Scrapy-Redis实战(附完整源码)
1. 为什么需要分布式爬虫?
作为爬虫工程师,我们经常面临这样的挑战:
- 单机爬虫的瓶颈:当需要爬取百万级页面时,单台机器的CPU、内存和网络带宽成为限制
- 反爬机制:目标网站可能对单一IP的频繁请求进行封禁
- 效率需求:希望爬虫能够24小时不间断工作,充分利用多台机器的资源
分布式爬虫解决方案:
通过多台机器协同工作,将爬取任务分散到不同节点,实现任务分配、数据存储、结果汇总的分布式处理
2. 分布式爬虫核心原理
(1) 基本架构
scss
[主节点] (任务调度)
│
├──[爬虫节点1] → 爬取URL1 → 存储数据
├──[爬虫节点2] → 爬取URL2 → 存储数据
└──[爬虫节点N] → 爬取URLN → 存储数据
关键组件:
- 任务队列:存储待爬取的URL(Redis/MQ)
- 去重集合:记录已爬URL避免重复(Redis Set/Bloom Filter)
- 结果存储:MongoDB/MySQL存储爬取数据
(2) 分布式核心技术点
ini
# 伪代码展示核心逻辑
while True:
# 1. 从分布式队列获取任务
url = redis.lpop('task_queue')
# 2. 检查是否已爬取(去重)
if not redis.sismember('visited_urls', url):
# 3. 执行爬取
html = download(url)
data = parse(html)
# 4. 存储结果
mongo.insert(data)
redis.sadd('visited_urls', url) # 标记已爬
3. 实战:Scrapy-Redis分布式爬虫
(1) 环境准备
pip install scrapy scrapy-redis redis pymongo
(2) 项目结构
bash
distributed_spider/
├── spiders/
│ └── example_spider.py # 爬虫核心逻辑
├── settings.py # 分布式配置
└── requirements.txt
(3) 核心配置(settings.py)
ini
# 启用Scrapy-Redis调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 确保所有爬虫共享相同的去重过滤
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# Redis连接配置
REDIS_HOST = 'your_redis_server' # 可以是云Redis或本地
REDIS_PORT = 6379
REDIS_PASSWORD = 'your_password' # 如果有密码
# 保持爬取状态(可选)
SCHEDULER_PERSIST = True
(4) 爬虫实现(example_spider.py)
python
import scrapy
from scrapy_redis.spiders import RedisSpider
class ExampleSpider(RedisSpider):
name = 'example'
# 注意:这里不定义start_urls,而是从Redis获取
redis_key = 'example:start_urls' # Redis中的起始URL键名
def parse(self, response):
# 示例:提取页面标题和链接
yield {
'url': response.url,
'title': response.css('title::text').get(),
'content_length': len(response.text)
}
# 提取新链接并加入队列(分布式自动处理)
for link in response.css('a::attr(href)').getall():
yield response.follow(link, callback=self.parse)
4. 分布式部署实战
(1) 启动Redis服务
bash
# 本地启动(测试用)
redis-server
# 或使用云Redis(如阿里云Redis)
(2) 添加初始任务
java
import redis
r = redis.Redis(host='localhost', port=6379)
r.lpush('example:start_urls', 'https://example.com') # 添加种子URL
(3) 启动多个爬虫节点
bash
# 在多台机器/终端执行(相同代码)
scrapy crawl example
关键说明:
- 所有节点共享同一个Redis任务队列
- 每个URL只会被一个节点爬取(自动去重)
- 爬取结果可以统一存储到MongoDB
5. 进阶优化技巧
(1) 动态负载均衡
ini
# 在settings.py中调整并发参数
CONCURRENT_REQUESTS = 32 # 每个节点并发数
DOWNLOAD_DELAY = 0.5 # 下载延迟(秒)
# 按域名限制并发
AUTOTHROTTLE_ENABLED = True
(2) 断点续爬
shell
# SCHEDULER_PERSIST = True # 已在配置中启用
# 重启爬虫时会自动继续未完成的任务
(3) 结果存储优化(MongoDB示例)
python
# pipelines.py
import pymongo
class MongoPipeline:
def __init__(self, mongo_uri):
self.mongo_uri = mongo_uri
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client['crawler_db']
def process_item(self, item, spider):
self.db['pages'].insert_one(dict(item))
return item
6. 完整项目源码结构
bash
distributed_spider/
├── scrapy.cfg
├── distributed_spider/
│ ├── __init__.py
│ ├── items.py # 数据结构定义
│ ├── middlewares.py # 中间件配置
│ ├── pipelines.py # 数据存储管道
│ ├── settings.py # 核心配置
│ └── spiders/
│ ├── __init__.py
│ └── example_spider.py # 主爬虫逻辑
└── requirements.txt
requirements.txt内容:
ini
scrapy==2.11.0
scrapy-redis==0.7.2
redis==4.5.5
pymongo==4.3.3
7. 常见问题解决方案
(1) 反爬应对策略
ruby
# 在middlewares.py中添加
class RandomUserAgentMiddleware:
def process_request(self, request, spider):
request.headers['User-Agent'] = random.choice(USER_AGENTS)
(2) 分布式监控
bash
# 使用Redis CLI监控任务队列
redis-cli llen example:start_urls # 查看剩余任务数
redis-cli scard example:dupefilter # 查看过滤的URL数
(3) 性能调优建议
- 根据目标网站响应速度调整
DOWNLOAD_DELAY
- 对重要域名设置单独的并发限制
- 使用Redis集群提高任务队列可靠性
8. 总结:分布式爬虫开发要点
核心要素 | 实现方案 | 注意事项 |
---|---|---|
任务分发 | Redis队列 | 确保所有节点连接同一Redis |
去重控制 | Redis Set/Bloom Filter | 大规模爬取考虑Bloom Filter |
容错处理 | 持久化调度状态 | SCHEDULER_PERSIST=True |
扩展性 | 动态添加爬虫节点 | 无状态设计,随时扩容 |
下一步学习建议:
- 尝试结合Selenium处理JavaScript渲染页面
- 研究Kafka替代Redis作为任务队列
- 学习Docker部署分布式爬虫集群
通过这套方案,你可以轻松构建一个每小时处理数万页面的分布式爬虫系统! 🚀