Python 分布式爬虫完全教程:从基础原理到 Scrapy-Redis 实战,附完整源码
1. 为什么需要分布式爬虫?
作为爬虫工程师,我们经常面临这样的挑战:
- 单机爬取速度有限,无法满足大规模数据采集需求
- 目标网站有反爬机制,单机IP容易被封禁
- 需要24小时不间断爬取,单机稳定性不足
分布式爬虫解决方案: ✅ 将爬虫任务分散到多台机器,成倍提升爬取速度 ✅ 通过IP代理池和请求间隔控制,降低被封风险 ✅ 实现任务队列共享,多机协同工作不停歇
2. 分布式爬虫核心原理
2.1 基本架构
[主节点] (任务调度)
│
├─[任务队列] (Redis/RabbitMQ)
│ │
│ ├─[爬虫节点1] → 抓取 → [数据存储]
│ ├─[爬虫节点2] → 抓取 → [数据存储]
│ └─[爬虫节点N] → 抓取 → [数据存储]
2.2 关键技术点
- 任务队列:存储待抓取的URL
- 去重机制:避免重复抓取
- 结果存储:集中存储爬取的数据
- 节点通信:协调多个爬虫节点工作
3. 基础准备:Scrapy 框架回顾
3.1 安装必要库
pip install scrapy scrapy-redis redis
3.2 基础Scrapy项目结构
myproject/
├── scrapy.cfg
└── myproject/
├── __init__.py
├── items.py # 数据模型
├── middlewares.py # 中间件
├── pipelines.py # 数据处理
├── settings.py # 配置
└── spiders/ # 爬虫目录
└── __init__.py
4. Scrapy-Redis 分布式爬虫实战
4.1 项目配置 (settings.py)
# 启用Scrapy-Redis调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 启用去重过滤器
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 允许暂停/恢复爬取
SCHEDULER_PERSIST = True
# Redis连接设置
REDIS_HOST = 'localhost' # Redis服务器地址
REDIS_PORT = 6379 # Redis端口
# REDIS_PASSWORD = 'yourpassword' # 如果有密码
# 使用优先级队列
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
# 分布式爬虫类
DEFAULT_REQUEST_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
4.2 分布式爬虫类 (spiders/distributed_spider.py)
import scrapy
from scrapy_redis.spiders import RedisSpider
class MyDistributedSpider(RedisSpider):
name = 'distributed_spider'
redis_key = 'distributed_spider:start_urls' # Redis中的起始URL键名
def parse(self, response):
# 示例:提取页面标题和链接
title = response.css('title::text').get()
links = response.css('a::attr(href)').getall()
yield {
'url': response.url,
'title': title,
'links_count': len(links)
}
# 提取新链接并加入队列
for link in links:
if link and not link.startswith('javascript:'):
yield scrapy.Request(response.urljoin(link), callback=self.parse)
4.3 数据管道 (pipelines.py)
import pymongo
class MongoPipeline:
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'scrapy_data')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
self.db['pages'].insert_one(dict(item))
return item
4.4 项目配置补充 (settings.py)
# MongoDB配置
MONGO_URI = 'mongodb://localhost:27017'
MONGO_DATABASE = 'distributed_crawler'
# 启用Pipeline
ITEM_PIPELINES = {
'myproject.pipelines.MongoPipeline': 300,
}
# 遵守robots.txt规则(根据目标网站调整)
ROBOTSTXT_OBEY = False
# 下载延迟
DOWNLOAD_DELAY = 1
# 并发请求数
CONCURRENT_REQUESTS = 8
5. 部署与运行指南
5.1 Redis服务器设置
- 安装Redis:
sudo apt-get install redis-server(Ubuntu) - 启动Redis:
redis-server - 测试连接:
redis-cli ping(应返回"pong")
5.2 添加起始URL
import redis
r = redis.Redis(host='localhost', port=6379, db=0)
r.lpush('distributed_spider:start_urls', 'https://example.com')
5.3 启动爬虫节点
在一台或多台机器上运行:
scrapy crawl distributed_spider
6. 高级功能扩展
6.1 动态代理中间件
# middlewares.py
import random
class ProxyMiddleware:
def __init__(self, proxies):
self.proxies = proxies
@classmethod
def from_crawler(cls, crawler):
return cls(
proxies=crawler.settings.get('PROXIES')
)
def process_request(self, request, spider):
if self.proxies and not request.meta.get('proxy'):
proxy = random.choice(self.proxies)
request.meta['proxy'] = proxy
# settings.py
PROXIES = [
'http://proxy1.example.com:8080',
'http://proxy2.example.com:8080',
# 添加更多代理...
]
6.2 用户代理轮换
# settings.py
USER_AGENTS = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64)',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)',
'Mozilla/5.0 (X11; Linux x86_64)',
# 添加更多UA...
]
# middlewares.py
class RandomUserAgentMiddleware:
def __init__(self, user_agents):
self.user_agents = user_agents
@classmethod
def from_crawler(cls, crawler):
return cls(
user_agents=crawler.settings.get('USER_AGENTS')
)
def process_request(self, request, spider):
request.headers.setdefault('User-Agent', random.choice(self.user_agents))
7. 监控与管理
7.1 Redis监控命令
# 查看队列中的URL数量
redis-cli llen distributed_spider:start_urls
# 查看已爬取的URL数量
redis-cli scard distributed_spider:dupefilter
# 查看所有爬虫节点状态
redis-cli keys *
7.2 爬虫状态检查
import redis
from scrapy.utils.project import get_project_settings
settings = get_project_settings()
r = redis.Redis(**settings.getdict('REDIS_PARAMS'))
print(f"待爬取URL数量: {r.llen('distributed_spider:start_urls')}")
print(f"已去重URL数量: {r.scard('distributed_spider:dupefilter')}")
8. 完整项目结构
distributed_crawler/
├── scrapy.cfg
├── distributed_crawler/
│ ├── __init__.py
│ ├── items.py
│ ├── middlewares.py
│ ├── pipelines.py
│ ├── settings.py
│ └── spiders/
│ ├── __init__.py
│ └── distributed_spider.py
└── requirements.txt
9. 总结与最佳实践
9.1 关键要点
- Redis是核心:作为分布式任务队列和去重存储
- 继承RedisSpider:简化分布式爬虫开发
- 合理设置延迟:避免对目标网站造成过大压力
- 监控必不可少:实时了解爬虫运行状态
9.2 最佳实践
✅ 渐进式扩展:先单机测试,再逐步增加节点 ✅ 异常处理:完善重试机制和错误日志 ✅ 数据校验:确保爬取数据的完整性和准确性 ✅ 定期维护:清理Redis中的过期数据
10. 源码获取
完整项目源码可通过以下方式获取:
- GitHub仓库:
[github.com/yourusername/scrapy-redis-demo] - 项目文档:包含详细部署说明和扩展指南
现在就开始构建你的分布式爬虫集群吧! 🚀 通过这套完整的Scrapy-Redis解决方案,你可以轻松实现:
- 每天百万级页面的稳定抓取
- 多地域、多IP的分布式部署
- 24/7不间断的数据采集服务
记住:分布式爬虫的核心是协作,而不是单机性能的极限。合理设计你的爬虫架构,让多台机器协同工作,才能发挥最大的采集效率!