零基础掌握 Scrapy 和 Scrapy-Redis:爬虫分布式部署深度解析

如果你对网络数据抓取充满好奇,想从海量信息中提取有价值的内容,那么 Scrapy 绝对是你不可错过的利器!而当你需要处理大规模数据抓取,或者担心单机性能瓶颈时,Scrapy-Redis 更是你的不二之选,它能让你的爬虫具备分布式、可扩展的能力。

本篇博客将带你从零开始,深入理解 Scrapy 的执行流程,并手把手教你如何使用 Scrapy-Redis 构建一个分布式爬虫系统。即使你是编程小白,也能轻松掌握!

Scrapy 篇:单机爬虫的精妙艺术

首先,我们来认识 Scrapy,它是 Python 领域一个强大而高效的爬虫框架。它的设计理念是高度模块化,让你可以专注于数据提取和业务逻辑,而无需过多关注底层的网络请求、并发处理等细节。

Scrapy 的核心组件

理解 Scrapy 的执行流程,要从它的核心组件说起:

  • Scrapy Engine(引擎):Scrapy 的核心,负责控制所有组件的数据流,并根据请求和响应进行调度。它就像一个指挥官,协调各个部件协同工作。
  • Scheduler(调度器):负责接收引擎发来的请求,并将其放入队列中,等待引擎请求时提供。它会根据优先级对请求进行排序,并负责去重。
  • Downloader(下载器):负责下载网页内容。它接收引擎发来的请求,向目标网站发送 HTTP/HTTPS 请求,并将返回的响应(网页内容)发送给引擎。
  • Spiders(爬虫):这是你编写爬虫逻辑的地方。它负责解析下载器返回的响应,提取所需数据,并生成新的请求(比如页面中的其他链接)。
  • Item Pipelines(项目管道):负责处理爬虫提取到的数据。你可以将数据清洗、验证、持久化(保存到数据库、文件等)等操作放在这里。
  • Downloader Middlewares(下载器中间件):介于引擎和下载器之间,可以在请求发送前和响应接收后进行处理。比如设置代理、User-Agent、处理 cookies 等。
  • Spider Middlewares(爬虫中间件):介于引擎和爬虫之间,可以在请求发送给爬虫前和爬虫处理响应后进行处理。
Scrapy 的执行流程(单机版)

理解了核心组件,Scrapy 的执行流程就变得清晰起来:

  1. 启动爬虫 :当你运行 scrapy crawl your_spider_name 命令时,Scrapy 引擎会启动。
  2. 生成初始请求 :引擎会向你定义的 Spider 发送一个信号,告知其开始爬取。Spider 会生成初始的 Request 对象(通常是爬取入口 URL),并将其发送给引擎。
  3. 请求入队 :引擎接收到 Request 后,会将其转发给 SchedulerScheduler 将请求添加到队列中,并进行去重。
  4. 请求出队,发送下载 :引擎从 Scheduler 请求下一个可用的 RequestSchedulerRequest 返回给引擎。引擎再将 Request 转发给 Downloader Middlewares 进行预处理(比如添加代理)。
  5. 下载页面 :经过 Downloader Middlewares 处理的 Request 会被发送给 DownloaderDownloader 向目标网站发送 HTTP/HTTPS 请求,获取网页内容。
  6. 响应处理Downloader 接收到网站响应后,会将其封装成 Response 对象,并先经过 Downloader Middlewares 进行后处理。
  7. 响应解析 :经过 Downloader Middlewares 处理的 Response 对象被发送回引擎。引擎再将其转发给 Spider Middlewares 进行预处理,最后交给 Spider 进行解析。
  8. 数据提取与新请求生成Spider 接收到 Response 后,会根据你编写的解析规则(通常使用 XPath 或 CSS 选择器)提取所需数据,并封装成 Item 对象。同时,它可能会从当前页面中提取新的链接,并生成新的 Request 对象。
  9. 数据入管道,新请求入队SpiderItem 和新的 Request 都发送给引擎。引擎将 Item 转发给 Item Pipelines 进行后续处理(清洗、存储等)。新的 Request 则再次发送给 Scheduler,重复步骤 3-8。
  10. 爬虫结束 :当 Scheduler 中没有待处理的 Request,并且所有 Item Pipelines 都处理完毕时,Scrapy 引擎会停止,爬虫任务完成。
动手实践:一个简单的 Scrapy 爬虫

为了让你更好地理解,我们来创建一个简单的 Scrapy 爬虫,抓取某个网站的标题和链接。

  1. 安装 Scrapy

    bash 复制代码
    pip install scrapy
  2. 创建 Scrapy 项目

    bash 复制代码
    scrapy startproject myproject
    cd myproject
  3. 创建 Spider

    bash 复制代码
    scrapy genspider example example.com

    这会在 myproject/myproject/spiders/ 目录下生成 example.py 文件。

  4. 编辑 example.py

    python 复制代码
    import scrapy
    
    class ExampleSpider(scrapy.Spider):
        name = "example"
        allowed_domains = ["example.com"] # 限制爬取范围
        start_urls = ["http://example.com"] # 初始请求的URL
    
        def parse(self, response):
            # 提取页面标题
            title = response.css('h1::text').get()
            # 提取所有链接
            links = response.css('a::attr(href)').getall()
    
            yield {
                'title': title,
                'links': links,
            }
    
            # 如果需要继续爬取其他页面,可以生成新的请求
            # for link in links:
            #     yield response.follow(link, callback=self.parse)
  5. 运行爬虫

    bash 复制代码
    scrapy crawl example -o output.json

    这会将抓取到的数据保存到 output.json 文件中。

Scrapy-Redis 篇:分布式爬虫的实现

单机 Scrapy 爬虫在处理大量数据时可能会遇到性能瓶颈,比如内存占用过高、请求频率受限等。这时,Scrapy-Redis 就派上用场了!它能将 Scrapy 爬虫的请求队列和去重指纹存储到 Redis 数据库中,从而实现多个爬虫实例共享队列、协同工作,达到分布式爬取的效果。

为什么需要 Scrapy-Redis?
  • 分布式爬取:多台机器可以同时运行爬虫实例,共享同一个 Redis 队列,提高爬取效率。
  • 断点续爬:请求队列和去重指纹存储在 Redis 中,即使爬虫意外中断,重启后也能从上次中断的地方继续爬取,避免重复抓取和数据丢失。
  • 共享 Request 队列:所有爬虫实例从同一个 Redis 队列中获取待爬取的 URL,避免重复爬取。
  • 共享去重指纹:Redis 存储了已爬取 URL 的指纹,确保不会重复爬取相同页面。
Scrapy-Redis 的核心原理

Scrapy-Redis 的核心在于将 Scrapy 的 SchedulerDupeFilter(去重过滤器)替换掉,转而使用 Redis 来存储和管理请求队列以及去重指纹。

当使用 Scrapy-Redis 时,Scrapy 的执行流程会发生如下变化:

  1. 启动爬虫(Scrapy-Redis 版):当你运行 Scrapy-Redis 爬虫时,它会连接到 Redis 数据库。
  2. 初始请求入 RedisSpider 生成的初始 Request 对象不再直接发送给 Scrapy 内置的 Scheduler,而是被发送到 Redis 队列中。
  3. 请求从 Redis 出队 :Scrapy-Redis 的 Scheduler 会从 Redis 队列中获取待爬取的 Request
  4. 去重在 Redis :在将 Request 放入队列前或从队列取出后,Scrapy-Redis 的 DupeFilter 会检查 Redis 中是否已经存在该请求的指纹,从而实现去重。
  5. 后续流程不变 :一旦 Request 从 Redis 队列中取出并经过去重,后续的下载、响应处理、数据提取等流程与单机 Scrapy 基本一致。
  6. 新请求入 RedisSpider 解析页面后生成的新 Request 也同样发送到 Redis 队列中。
动手实践:构建一个 Scrapy-Redis 分布式爬虫

接下来,我们来改造上面的 Scrapy 爬虫,让它具备分布式能力。

  1. 安装 Scrapy-Redis 和 Redis

    bash 复制代码
    pip install scrapy-redis redis

    你还需要在本地或服务器上安装并运行 Redis 服务。具体安装方法请参考 Redis 官方文档。

  2. 修改 settings.py 文件

    在你的 Scrapy 项目的 settings.py 文件中,添加或修改以下配置:

    python 复制代码
    # 启用 Scrapy-Redis 的调度器和去重过滤器
    SCHEDULER = "scrapy_redis.scheduler.Scheduler"
    DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
    
    # 设置调度器是否持久化。如果为 True,Scrapy 停止后,Redis 中的请求队列和指纹不会被清除。
    SCHEDULER_PERSIST = True
    
    # 设置 Redis 连接信息
    REDIS_HOST = 'localhost' # 你的 Redis 服务器地址
    REDIS_PORT = 6379 # 你的 Redis 服务器端口
    REDIS_DB = 0 # Redis 数据库编号
    
    # 可以设置 Scrapy-Redis 的队列类型,默认是 LIFO(栈),也可以改为 FIFO(队列)
    # SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" # 队列
    # SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" # 栈
    # SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # 优先级队列
    
    # 如果需要爬虫启动时清除 Redis 中之前的请求,可以设置为 False
    # Dont cleanup redis queues, allows to start where was disconnected.
    # SCHEDULER_PERSIST = True
  3. 修改 example.py 文件

    让你的 Spider 继承 scrapy_redis.spiders.RedisSpider
    注意: 继承 RedisSpider 后,你的 start_urls 不再生效,你需要通过 Redis 来提交初始 URL。

    python 复制代码
    import scrapy
    from scrapy_redis.spiders import RedisSpider
    
    class ExampleSpider(RedisSpider): # 继承 RedisSpider
        name = "example_redis" # 更改爬虫名称,避免与之前的冲突
        allowed_domains = ["example.com"]
    
        # start_urls 不再需要,初始 URL 将从 Redis 中获取
        # start_urls = ["http://example.com"]
    
        # Redis 键名,用于存储待爬取的 URL
        redis_key = 'example_redis:start_urls'
    
        def parse(self, response):
            title = response.css('h1::text').get()
            links = response.css('a::attr(href)').getall()
    
            yield {
                'title': title,
                'links': links,
            }
    
            for link in links:
                # 使用 response.follow 生成新的请求,这些请求会自动发送到 Redis
                yield response.follow(link, callback=self.parse)
  4. 启动 Redis

    确保你的 Redis 服务正在运行。

  5. 提交初始 URL 到 Redis

    在运行爬虫之前,你需要手动将初始 URL 提交到 Redis 中。你可以使用 Redis 客户端工具(如 redis-cli)或者 Python 代码来完成。

    使用 redis-cli

    bash 复制代码
    redis-cli LPUSH example_redis:start_urls http://example.com

    这里的 example_redis:start_urls 就是你在 Spider 中定义的 redis_keyLPUSH 命令将 URL 添加到 Redis 列表的左侧。

  6. 运行分布式爬虫

    bash 复制代码
    scrapy crawl example_redis

    现在,你的爬虫实例会从 Redis 中获取 URL 进行爬取。你可以启动多个终端,运行相同的命令,它们会共享同一个 Redis 队列,实现分布式爬取。

分布式部署的关键步骤
  1. 准备多台机器:你需要多台服务器或虚拟机来部署你的爬虫实例。
  2. 安装 Redis:在一台独立的服务器上部署 Redis 服务,确保所有爬虫实例都能访问到它。
  3. 部署代码:将你的 Scrapy-Redis 项目代码部署到每台爬虫机器上。
  4. 启动爬虫 :在每台机器上运行 scrapy crawl your_redis_spider_name 命令。

总结与展望

通过本篇博客,相信你已经对 Scrapy 和 Scrapy-Redis 的执行流程有了清晰的理解。

  • Scrapy 提供了一个高效且模块化的框架,让你能够专注于数据提取和业务逻辑。
  • Scrapy-Redis 则在此基础上,通过利用 Redis 的特性,实现了分布式爬虫的强大功能,包括断点续爬、共享队列、去重等,大大提升了大规模数据抓取的效率和稳定性。
相关推荐
转身後 默落7 小时前
01.Redis 概述
数据库·redis·缓存
gs8014011 小时前
GitLab Docker Compose 迁移后 Redis 权限问题排查与解决
redis·docker·gitlab
华科云商xiao徐14 小时前
Go语言高并发价格监控系统设计
爬虫
转身後 默落14 小时前
06.Redis 配置文件说明
数据库·redis·bootstrap
小满和小晨14 小时前
Redis+Lua的分布式限流器
redis·分布式·lua
阿萨德528号15 小时前
5、生产Redis高并发分布式锁实战
数据库·redis·分布式·缓存
IT小辉同学16 小时前
CentOS 7 编译 Redis 6.x 完整教程(解决 GCC 版本不支持 C11)
linux·redis·centos
掉头发的王富贵16 小时前
Java玩转Redis+Lua脚本:一篇让你从小白到高手的实战入门指南
java·redis·lua
转身後 默落18 小时前
03.一键编译安装Redis脚本
数据库·redis·缓存