Scrapy+Rredis实现分布式爬虫入门与优化

分布式爬虫是解决单台机器爬虫效率低下、爬取范围有限、容易被反爬限制等问题的核心方案,而Scrapy+Redis的组合凭借其轻量、高效、易扩展的特性,成为分布式爬虫开发的主流选择。本文将从入门基础到实战优化,全面讲解如何基于 Scrapy 和 Redis 搭建稳定、高效的分布式爬虫系统。

一、核心原理:为什么 Scrapy+Redis 能实现分布式?

Scrapy 本身是一个强大的单机爬虫框架,但它的调度器、队列存储都运行在单台机器上,无法直接支持分布式协作。而 Redis 作为一款高性能的键值对数据库,具备分布式共享、高并发读写、数据持久化的特性,恰好能弥补 Scrapy 的分布式短板,二者的协作核心围绕以下 3 点展开:

  1. 共享请求队列(Request Queue):打破单机 Scrapy 的本地请求队列限制,将所有待爬取的 URL 请求统一存储在 Redis 的队列(通常使用 List 或 ZSet 数据结构)中,所有分布式节点都可以从这个公共队列中获取待爬取任务。
  2. 共享去重集合(Duplication Filter) :Scrapy 单机的去重依赖本地内存的RFPDupeFilter,分布式环境下无法跨节点共享去重信息。借助 Redis 的 Set 数据结构,将所有已爬取或已入队的 URL 进行哈希存储,实现跨节点的全局去重,避免重复爬取。
  3. 分布式节点协作:多个 Scrapy 爬虫节点(可以是同一台机器的多个进程,也可以是不同服务器)共用 Redis 的请求队列和去重集合,各自独立完成 "取任务→爬取数据→解析响应→生成新请求→存入公共队列" 的流程,实现任务的分布式分发和并行执行。

简单来说:Redis 充当了分布式爬虫的 "中央调度中心",而多个 Scrapy 节点则是 "执行工人",二者配合实现高效的分布式数据爬取。

二、入门准备:环境搭建与依赖安装

在开始编码之前,需要完成基础环境的搭建和相关依赖的安装,确保整个分布式系统能够正常运行。

1. 基础环境要求

  • Python 3.7+(推荐 3.8~3.10,兼容性更好)
  • Redis 服务(本地或远程,推荐 5.0+,开启持久化避免任务丢失)
  • 操作系统:Windows/Linux/macOS(生产环境优先 Linux)

2. 核心依赖安装

需要安装 Scrapy 框架和 Scrapy-Redis 扩展(该扩展已封装好 Redis 与 Scrapy 的协作逻辑,无需重复造轮子),执行以下 pip 命令即可:

bash

运行

复制代码
# 安装Scrapy框架
pip install scrapy

# 安装Scrapy-Redis扩展(核心分布式依赖)
pip install scrapy-redis

3. Redis 服务配置(关键)

  1. 安装 Redis 后,启动 Redis 服务(本地测试可直接默认配置,生产环境需优化配置):
    • Linux/macOS:redis-server
    • Windows:双击redis-server.exe(或通过命令行启动)
  2. 验证 Redis 服务是否可用:执行redis-cli进入客户端,输入ping,返回PONG即表示服务正常。
  3. 生产环境额外配置:开启 Redis 持久化(RDB+AOF 混合持久化)、设置密码、限制 IP 访问,避免任务数据丢失和安全风险。

三、实战入门:搭建第一个分布式爬虫

本部分将以爬取公开测试站点(示例站点:http://quotes.toscrape.com)为例,一步步搭建分布式爬虫,核心步骤包括创建 Scrapy 项目、修改配置文件、编写爬虫逻辑、启动分布式节点。

步骤 1:创建 Scrapy 项目和爬虫

首先通过 Scrapy 命令创建项目和基础爬虫,执行以下命令:

bash

运行

复制代码
# 创建Scrapy项目(项目名:distributed_spider)
scrapy startproject distributed_spider

# 进入项目目录
cd distributed_spider

# 创建爬虫(爬虫名:quotes_spider,目标站点:http://quotes.toscrape.com)
scrapy genspider quotes_spider quotes.toscrape.com

步骤 2:修改 Scrapy 配置文件(settings.py

这是实现分布式的核心配置步骤,需要替换 Scrapy 的默认调度器、去重器,配置 Redis 连接信息,具体修改如下:

python

运行

复制代码
# 1. 开启项目的爬虫中间件和下载中间件(默认已开启,确保注释未打开)
SPIDER_MIDDLEWARES = {
    'scrapy.spidermiddlewares.httperror.HttpErrorMiddleware': 500,
}
DOWNLOADER_MIDDLEWARES = {
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
}

# 2. 替换默认调度器为Scrapy-Redis的分布式调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"

# 3. 替换默认去重器为Scrapy-Redis的分布式去重器
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

# 4. 配置Redis连接信息(本地Redis可默认,远程Redis需填写IP、端口、密码)
REDIS_URL = None  # 优先使用该配置,格式:redis://:password@host:port/db
REDIS_HOST = 'localhost'  # Redis服务器IP
REDIS_PORT = 6379  # Redis服务器端口
REDIS_PASSWORD = ''  # Redis服务器密码(无密码则留空)
REDIS_DB = 0  # 使用的Redis数据库编号(默认0)

# 5. 关键配置:爬虫关闭后是否保留Redis中的请求队列和去重集合
# True:保留,支持断点续爬(分布式场景推荐开启)
# False:不保留,爬虫关闭后清空Redis中的相关数据
SCHEDULER_PERSIST = True

# 6. 配置Scrapy-Redis的请求队列类型(可选,默认使用List)
# 可选值:scrapy_redis.queue.SpiderQueue(先进先出)、scrapy_redis.queue.SpiderStack(后进先出)、scrapy_redis.queue.SpiderPriorityQueue(优先级队列)
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"

# 7. 关闭Robots协议(测试场景开启,生产环境需谨慎)
ROBOTSTXT_OBEY = False

# 8. 配置下载延迟(避免给目标站点造成压力,防止被反爬)
DOWNLOAD_DELAY = 1

# 9. 配置并发数(根据目标站点抗压能力和自身服务器性能调整)
CONCURRENT_REQUESTS = 16
CONCURRENT_REQUESTS_PER_DOMAIN = 8

步骤 3:编写爬虫逻辑(quotes_spider.py)

修改自动生成的爬虫文件,实现数据爬取、解析和新请求生成,核心是让爬虫从 Redis 队列中获取任务,并将新生成的请求存入 Redis 队列,具体代码如下:

python

运行

复制代码
import scrapy
from scrapy_redis.spiders import RedisSpider  # 替换默认Spider为RedisSpider

# 注意:继承的是RedisSpider,而非默认的scrapy.Spider
class QuotesSpider(RedisSpider):
    name = 'quotes_spider'  # 爬虫名称,必须唯一
    allowed_domains = ['quotes.toscrape.com']  # 允许爬取的域名
    # 关键修改:移除start_urls,添加redis_key(分布式爬虫的任务入口,从Redis的该key对应的队列中获取起始URL)
    redis_key = 'quotes:start_urls'  # 格式:爬虫名:start_urls(自定义,需与后续入队命令对应)

    def parse(self, response):
        """
        解析响应数据,提取目标信息,生成新请求
        """
        # 1. 提取页面中的名言数据(示例:提取文本和作者)
        quotes = response.xpath('//div[@class="quote"]')
        for quote in quotes:
            yield {
                'text': quote.xpath('.//span[@class="text"]/text()').extract_first(),
                'author': quote.xpath('.//small[@class="author"]/text()').extract_first(),
            }

        # 2. 提取下一页URL,生成新的请求(自动存入Redis公共队列)
        next_page = response.xpath('//li[@class="next"]/a/@href').extract_first()
        if next_page:
            # 拼接完整URL
            next_page_url = response.urljoin(next_page)
            # 生成新请求,交由Scrapy调度(自动存入Redis队列,实现分布式分发)
            yield scrapy.Request(
                url=next_page_url,
                callback=self.parse,
                dont_filter=False  # 不跳过去重(默认False,正常参与分布式去重)
            )

步骤 4:启动分布式爬虫(核心操作)

分布式爬虫的启动分为两步:向 Redis 中存入起始 URL启动多个 Scrapy 爬虫节点,具体操作如下:

1. 向 Redis 存入起始 URL

通过redis-cli客户端向配置的redis_key(即quotes:start_urls)中存入起始 URL,执行以下命令:

bash

运行

复制代码
# 1. 进入Redis客户端
redis-cli

# 2. 存入起始URL(lpush命令:向List队列左侧添加数据,对应Scrapy-Redis的先进先出队列)
lpush quotes:start_urls http://quotes.toscrape.com

# 3. 验证是否存入成功(可选)
lrange quotes:start_urls 0 -1
2. 启动多个 Scrapy 爬虫节点

打开多个终端(或不同服务器),进入项目目录,执行相同的爬虫启动命令,即可启动多个分布式节点:

bash

运行

复制代码
# 启动爬虫节点(每个终端执行一次,即可启动一个节点,支持无限扩展)
scrapy crawl quotes_spider

此时可以观察到:多个爬虫节点会从 Redis 的公共队列中获取任务,并行爬取数据,且不会出现重复爬取的情况,这就是一个最简单的分布式爬虫系统。

四、进阶优化:提升分布式爬虫的稳定性和效率

入门级的分布式爬虫能够正常运行,但在面对大规模爬取、反爬严格的站点时,还存在效率低下、容易崩溃、被封禁等问题,以下是核心优化方案:

1. 队列优化:使用优先级队列应对复杂爬取场景

默认的 List 队列(先进先出)无法满足有优先级的爬取需求(例如:优先爬取重要页面),Scrapy-Redis 支持优先级队列(SpiderPriorityQueue),只需修改settings.py中的队列配置:

python

运行

复制代码
# 配置优先级队列(基于Redis的ZSet数据结构实现)
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"

在生成请求时,通过priority参数设置优先级(数值越大,优先级越高):

python

运行

复制代码
yield scrapy.Request(
    url=next_page_url,
    callback=self.parse,
    priority=10  # 设置优先级,高优先级任务优先被爬取
)

2. 去重优化:提升去重效率,减少 Redis 内存占用

默认的去重方案是将 URL 直接哈希后存入 Redis 的 Set 中,当 URL 数量巨大时,会占用大量 Redis 内存,且去重效率下降,可通过以下方式优化:

  • 缩短哈希值长度 :Scrapy-Redis 默认使用sha1哈希算法(生成 40 位十六进制字符串),可自定义去重类,使用更精简的哈希算法(如md5,32 位),减少内存占用。

  • 分段去重 + 过期清理 :对于超大规模爬取,可将去重集合按时间或批次分段存储,并为 Redis 中的去重键设置过期时间,避免无效数据堆积:

    python

    运行

    复制代码
    # 在settings.py中配置去重键的过期时间(自定义去重类实现)
    DUPEFILTER_EXPIRE = 86400  # 24小时过期,自动清理无效去重数据
  • 避免无效 URL 入队:在生成新请求前,先对 URL 进行预处理(如去重、过滤无效参数、标准化格式),减少不必要的请求入队,减轻 Redis 压力。

3. 反爬优化:降低被封禁的风险

分布式爬虫并行爬取容易被目标站点识别并封禁 IP,这是实战中最需要解决的问题,核心优化方案如下:

  • 设置随机 User-Agent :替换默认的 User-Agent,模拟不同浏览器请求,可使用fake-useragent库:

    bash

    运行

    复制代码
    # 安装fake-useragent
    pip install fake-useragent

    settings.py中配置:

    python

    运行

    复制代码
    from fake_useragent import UserAgent
    UA = UserAgent()
    USER_AGENT = UA.random  # 随机生成User-Agent
  • 使用代理 IP 池:搭建或接入第三方代理 IP 池(如阿布云、快代理),通过下载中间件实现代理 IP 的随机切换,避免单一 IP 被封禁。

  • 优化爬取频率 :合理调整DOWNLOAD_DELAY(下载延迟)、CONCURRENT_REQUESTS(并发数)、CONCURRENT_REQUESTS_PER_DOMAIN(单域名并发数),模拟人类浏览行为,避免高频请求。

  • 启用 Cookies 池:对于需要登录或验证 Cookies 的站点,搭建 Cookies 池,定期更新 Cookies,避免因 Cookies 失效导致爬取失败。

4. 性能优化:提升爬取效率,降低资源消耗

  • Redis 性能优化
    1. 生产环境使用 Redis 集群或哨兵模式,提高 Redis 的可用性和并发处理能力。
    2. 关闭 Redis 的不必要功能(如持久化中的 AOF 实时同步,改为每秒同步),减少 Redis 的 IO 压力。
    3. 合理选择 Redis 的数据结构,避免大 key(单个 key 存储过多数据),提升读写效率。
  • Scrapy 自身优化
    1. 开启异步下载:Scrapy 默认使用异步框架 Twisted,确保DOWNLOAD_HANDLERS中开启异步处理(默认已开启)。

    2. 关闭无用的日志:在settings.py中设置日志级别为INFOWARNING,减少日志 IO 消耗:

      python

      运行

      复制代码
      LOG_LEVEL = 'INFO'
    3. 使用Item Pipeline批量存储数据:避免每条数据都写入数据库(如 MySQL、MongoDB),而是批量收集后一次性写入,减少数据库连接开销。

  • 节点优化
    1. 合理分配节点数量:根据目标站点的抗压能力和自身服务器带宽、CPU 性能,选择合适的节点数量,避免过度并发导致节点自身崩溃。
    2. 负载均衡:使用云服务器搭建分布式节点时,开启负载均衡,避免单个节点任务过重。

5. 监控与容错优化:保障系统稳定运行

  • Redis 监控 :使用redis-cli info命令或第三方工具(如 Redis Insight、Prometheus+Grafana)监控 Redis 的内存使用、队列长度、读写频率,及时发现异常。

  • 爬虫节点监控:监控每个爬虫节点的运行状态、CPU / 内存占用、爬取速度,当节点崩溃时,自动重启节点。

  • 断点续爬与故障恢复 :依赖SCHEDULER_PERSIST = True配置,保留 Redis 中的请求队列和去重集合,当节点崩溃或服务器宕机后,重启节点即可继续爬取,无需重新开始。

  • 异常处理 :在爬虫的parse方法中添加异常捕获逻辑,处理 404、500 等 HTTP 错误,避免单个请求失败导致整个爬虫节点崩溃:

    python

    运行

    复制代码
    def parse(self, response):
        try:
            # 正常解析逻辑
            pass
        except Exception as e:
            self.logger.error(f"解析失败:{response.url},异常信息:{str(e)}")
            # 可选:将失败的URL重新存入Redis队列,待后续重试
            yield scrapy.Request(url=response.url, callback=self.parse, dont_filter=True, priority=-1)

五、总结

Scrapy+Redis 是实现分布式爬虫的黄金组合,其核心是通过 Redis 实现请求队列和去重集合的分布式共享,让多个 Scrapy 节点协同工作,大幅提升爬取效率。

本文从入门基础出发,讲解了环境搭建、核心配置、实战编码和启动流程,再通过进阶优化方案解决大规模爬取中的稳定性、效率、反爬等问题。需要注意的是,分布式爬虫的开发并非一蹴而就,在实战中需要根据目标站点的特性、爬取需求,灵活调整配置和优化方案。

同时,爬取数据时必须遵守目标站点的robots.txt协议(生产环境),尊重网站的版权和使用条款,避免非法爬取和恶意攻击,做一个合规的爬虫开发者。

相关推荐
深蓝电商API3 小时前
Scrapy中间件实战:自定义请求头和代理池实现
python·scrapy·中间件
回家路上绕了弯3 小时前
定期归档历史数据实战指南:从方案设计到落地优化
分布式·后端
rchmin4 小时前
Distro与Raft协议对比分析
分布式·cap
小辉笔记4 小时前
kafka原理总结
分布式·kafka
实战项目4 小时前
分布式协作入侵检测系统的报警信息管理
分布式
无心水7 小时前
【分布式利器:腾讯TSF】10、TSF故障排查与架构评审实战:Java架构师从救火到防火的生产哲学
java·人工智能·分布式·架构·限流·分布式利器·腾讯tsf
小北方城市网18 小时前
分布式锁实战指南:从选型到落地,避开 90% 的坑
java·数据库·redis·分布式·python·缓存
范桂飓20 小时前
大模型分布式训练框架 Megatron-LM
人工智能·分布式
智航GIS1 天前
10.6 Scrapy:Python 网页爬取框架
python·scrapy·信息可视化