21天搞定Python分布爬虫

90 阅读4分钟

Python 分布式爬虫完全教程:从基础原理到 Scrapy-Redis 实战,附完整源码

1. 为什么需要分布式爬虫?

作为爬虫工程师,我们经常面临这样的挑战:

  • 单机爬取速度有限,无法满足大规模数据采集需求
  • 目标网站有反爬机制,单机IP容易被封禁
  • 需要24小时不间断爬取,单机稳定性不足

​分布式爬虫解决方案​​: ✅ 将爬虫任务分散到多台机器,​​成倍提升爬取速度​​ ✅ 通过IP代理池和请求间隔控制,​​降低被封风险​​ ✅ 实现​​任务队列共享​​,多机协同工作不停歇

2. 分布式爬虫核心原理

2.1 基本架构

[主节点] (任务调度)
  │
  ├─[任务队列] (Redis/RabbitMQ)
  │     │
  │     ├─[爬虫节点1] → 抓取 → [数据存储]
  │     ├─[爬虫节点2] → 抓取 → [数据存储]
  │     └─[爬虫节点N] → 抓取 → [数据存储]

2.2 关键技术点

  1. ​任务队列​​:存储待抓取的URL
  2. ​去重机制​​:避免重复抓取
  3. ​结果存储​​:集中存储爬取的数据
  4. ​节点通信​​:协调多个爬虫节点工作

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服务器设置

  1. 安装Redis:sudo apt-get install redis-server(Ubuntu)
  2. 启动Redis:redis-server
  3. 测试连接: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 关键要点

  1. ​Redis是核心​​:作为分布式任务队列和去重存储
  2. ​继承RedisSpider​​:简化分布式爬虫开发
  3. ​合理设置延迟​​:避免对目标网站造成过大压力
  4. ​监控必不可少​​:实时了解爬虫运行状态

9.2 最佳实践

✅ ​​渐进式扩展​​:先单机测试,再逐步增加节点 ✅ ​​异常处理​​:完善重试机制和错误日志 ✅ ​​数据校验​​:确保爬取数据的完整性和准确性 ✅ ​​定期维护​​:清理Redis中的过期数据

10. 源码获取

完整项目源码可通过以下方式获取:

  1. GitHub仓库:[github.com/yourusername/scrapy-redis-demo]
  2. 项目文档:包含详细部署说明和扩展指南

​现在就开始构建你的分布式爬虫集群吧!​​ 🚀 通过这套完整的Scrapy-Redis解决方案,你可以轻松实现:

  • 每天百万级页面的稳定抓取
  • 多地域、多IP的分布式部署
  • 24/7不间断的数据采集服务

记住:​​分布式爬虫的核心是协作,而不是单机性能的极限​​。合理设计你的爬虫架构,让多台机器协同工作,才能发挥最大的采集效率!