scrapy-redis分布式爬虫总结

一、redis相关

  • 全称为remote dictionary server。国内使用到的公司也很多。
其关键字可以归纳为:
  • 1.开源
    并以实际应用驱动。
  • 2.key-value
    这种KV特性将其与关系型数据库本质的区别开来。这也是redis流行的关键因素所在。
  • 3.内存数据库
    这种将数据存储在内存中是redis性能(主要针对存取速度)优于硬盘数据库的根本。为了便于数据长久保存,其也支持持久化操作。

二. redis特性

  • 1.丰富的数据类型与相应操作。每种数据类型都有特定的应用场景。
  • 2.性能极高。
  • 3.功能丰富。提供的进阶特性能够满足数据库使用需求。

三. redis数据结构

包括string/hash/list/set/zset,针对每种结构的使用情景可见文章:redis之其中武器。
hash特点就是在string基础上可实现单个key的多个filed的存储;list就不用说了,就是双向链表的特性。set就是集合,它和list的区别体现在value的有序性和唯一性上,操作上更贴近集合的概念,提供交集、并集等。zset相比set多了一个score项,并利用其实现value的排序。

四. redis进阶特性

  • 1.事物。一组命令的集合。
  • 2.生存时间。可用来实现缓存。
  • 3.排序。
  • 4.任务队列。
  • 5.管道。

五. redis客户端

可以在redis官网上查看针对各个语言的客户端,所谓的客户端就是通过相应的语言接口实现对redis的操作。我是用的python,官方推荐redis-py,可通过pip install redis-py安装。

六.scrapy-redis实现scrapy分布式爬取分析的原理

scrapy-redis原理:
  • 1.spider解析下载器下载下来的response,返回item或者是links
  • 2.item或者links经过spidermiddleware的process_spider_out()方法,交给engine。
  • 3.engine将item交给itempipeline,将links交给调度器
  • 4.在调度器中,先将request对象利用scrapy内置的指纹函数,生成一个指纹对象
  • 5.如果request对象中的dont_filter参数设置为False,并且该request对象的指纹不在信息指纹的队列中,那么就把该request对象放到优先级的队列中
  • 6.从优先级队列中获取request对象,交给engine
  • 7.engine将request对象交给下载器下载,期间会通过downloadmiddleware的process_request()方法
  • 8.下载器完成下载,获得response对象,将该对象交给engine,期间会通过downloadmiddleware的process_response()方法
  • 9.engine将获得的response对象交给spider进行解析,期间会经过spidermiddleware的process_spider_input()方法
  • 10.从第一步开始循环

上面的十个步骤就是scrapy-redis的整体框架,与scrapy相差无几。本质的区别就是,将scrapy的内置的去重的队列和待抓取的request队列换成了redis的集合。就这一个小小的改动,就使得了scrapy-redis支持了分布式抓取。

七、需要用到的模块

  • scrapy
  • scrapy-redis
  • redis
  • mysql :收集来的数据存放到mysql中
  • python的mysqldb模块
  • python的redis模块
    后面俩个库:python不能直接操作数据库,需要通过库来支持。而这2个就是相应数据库的支持库。
    结构化数据可以使用mysql节省空间,非结构化、文本等数据可以采用mongodb等非关系型数据提高访问速度。

scrapy框架运行流程图

在这里插入图片描述

流程说明

- 一、Scrapy框架
  • Scrapy是一个为了爬取网站数据,提取结构性数据而编写的应用框架。可以应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。其最初是为了页面抓取 (更确切来说, 网络抓取 )所设计的, 也可以应用在获取API所返回的数据(例如 Amazon Associates Web Services ) 或者通用的网络爬虫。Scrapy用途广泛,可以用于数据挖掘、监测和自动化测试。
二、Scrapy主要组件
  • 1、引擎(Scrapy): 用来处理整个系统的数据流处理, 触发事务(框架核心)。
  • 2、调度器(Scheduler): 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址。
  • 3、下载器(Downloader): 用于下载网页内容, 并将网页内容返回给蜘蛛(Scrapy下载器是建立在twisted这个高效的异步模型上的)。
  • 4、爬虫(Spiders): 爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。
  • 5、项目管道(Pipeline): 负责处理爬虫从网页中抽取的实体,主要的功能是持久化实体、验证实体的有效性、清除不需要的信息。当页面被爬虫解析后,将被发送到项目管道,并经过几个特定的次序处理数据。
  • 6、下载器中间件(Downloader Middlewares): 位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。
  • 7、爬虫中间件(Spider Middlewares): 介于Scrapy引擎和爬虫之间的框架,主要工作是处理蜘蛛的响应输入和请求输出。
  • 8、调度中间件(Scheduler Middewares): 介于Scrapy引擎和调度之间的中间件,从Scrapy引擎发送到调度的请求和响应。

scrapy-redis分布式

在这里插入图片描述

总结

- 1、Scheduler:

scrapy改造了python本来的collection.deque(双向队列)形成了自己的Scrapy queue,但是Scrapy多个spider不能共享待爬取队列Scrapy
queue,即Scrapy本身不支持爬虫分布式,scrapy-redis 的解决是把这个Scrapy queue换成redis数据库(也是指redis队列),从同一个redis-server存放要爬取的request,便能让多个spider去同一个数据库里读取。Scrapy中跟“待爬队列”直接相关的就是调度器Scheduler,它负责对新的request进行入列操作(加入Scrapy queue),取出下一个要爬取的request(从Scrapy queue中取出)等操作。它把待爬队列按照优先级建立了一个字典结构,然后根据request中的优先级,来决定该入哪个队列,出列时则按优先级较小的优先出列。为了管理这个比较高级的队列字典,Scheduler需要提供一系列的方法。但是原来的Scheduler已经无法使用,所以使用Scrapy-redis的scheduler组件。

- 2、Duplication Filter

Scrapy中用集合实现这个request去重功能,Scrapy中把已经发送的request指纹放入到一个集合中,把下一个request的指纹拿到集合中比对,如果该指纹存在于集合中,说明这个request发送过了,如果没有则继续操作
在scrapy-redis中去重是由Duplication Filter组件来实现的,它通过redis的set不重复的特性,巧妙的实现了DuplicationFilter去重。scrapy-redis调度器从引擎接受request,将request的指纹存入redis的set检查是否重复,并将不重复的request push写入redis的 request queue。
引擎请求request(Spider发出的)时,调度器从redis的request queue队列里根据优先级pop 出⼀个request 返回给引擎,引擎将此request发给spider处理。

- 3、Item Pipeline:

引擎将(Spider返回的)爬取到的Item给Item Pipeline,scrapy-redis 的Item Pipeline将爬取到的 Item 存入redis的 items queue。修改过Item Pipeline可以很方便的根据 key 从 items queue 提取item,从而实现 items processes集群。

- 4、Base Spider

不在使用scrapy原有的Spider类,重写的RedisSpider继承了Spider和RedisMixin这两个类,RedisMixin是用来从redis读取url的类。
当我们生成一个Spider继承RedisSpider时,调用setup_redis函数,这个函数会去连接redis数据库,然后会设置signals(信号):一个是当spider空闲时候的signal,会调用spider_idle函数,这个函数调用schedule_next_request函数,保证spider是一直活着的状态,并且抛出DontCloseSpider异常。一个是当抓到一个item时的signal,会调用item_scraped函数,这个函数会调用schedule_next_request函数,获取下一个request。

总结一下scrapy-redis的总体思路:

这套组件通过重写scheduler和spider类,实现了调度、spider启动和redis的交互;
实现新的dupefilter和queue类,达到了判重和调度容器和redis的交互,因为每个主机上的爬虫进程都访问同一个redis数据库,所以调度和判重都统一进行统一管理,达到了分布式爬虫的目的;当spider被初始化时,同时会初始化一个对应的scheduler对象,这个调度器对象通过读取settings,配置好自己的调度容器queue和判重工具dupefilter;
每当一个spider产出一个request的时候,scrapy引擎会把这个reuqest递交给这个spider对应的scheduler对象进行调度,scheduler对象通过访问redis对request进行判重,如果不重复就把他添加进redis中的调度器队列里。当调度条件满足时,scheduler对象就从redis的调度器队列中取出一个request发送给spider,让他爬取;
当spider爬取的所有暂时可用url之后,scheduler发现这个spider对应的redis的调度器队列空了,于是触发信号spider_idle,spider收到这个信号之后,直接连接redis读取strart_url池,拿去新的一批url入口,然后再次重复上边的工作。

Scrapy-redis的源码解析

- connection.py + spider.py

spider.py文件是分布式爬虫的入口代码:
  1、通过connection接口,spider初始化时,通过setup_redis()函数初始化好和redis的连接。
  2、通过next_requests函数从redis中取出strat url,spider使用少量的start url + LinkExtractor,可以发展出很多新的url,这些url会进入scheduler进行判重和调度。直到spider跑到调度池内没有url的时候,会触发spider_idle信号,从而触发spider的next_requests函数。
  3、再次从redis的start url池中读取一些url。

- scheduler.py

这个文件重写了scheduler类,用来代替scrapy.core.scheduler的原有调度器。实现原理是使用指定的一个redis内存作为数据存储的媒介,以达到各个爬虫之间的统一调度。
  1、scheduler负责调度各个spider的request请求,scheduler初始化时,通过settings文件读取queue和dupefilters(url去重)的类型,配置queue和dupefilters使用的key(一般就是spider name加上queue或者dupefilters,这样对于同一种spider的不同实例,就会使用相同的数据块了)。
  2、每当一个request要被调度时,enqueue_request被调用,scheduler使用dupefilters来判断这个url是否重复,如果不重复,就添加到queue的容器中(三种队列方式:先进先出,先进后出和优先级都可以,可以在settings中配置)。
  3、当调度完成时,next_request被调用,scheduler就通过queue容器的接口,取出一个request,把他发送给相应的spider,让spider进行爬取工作。

- dupefilter.py

分布式爬虫url去重原理:
  通过分析可以知道self.server为redis实例,使用一个key来向redis的一个set中插入fingerprint(这个key对于同一个spider是相同的,redis是一个key-value的数据库,如果key是相同的,访问到的值就是相同的,默认使用spider名字 + fingerpoint的key就是为了区分在不同主机上的不同spider实例,只要数据是同一个spider,就会访问到redis中的同一个spider-set而这个set就是url的判重池)。

- request.py

request_fingerprint接口:
通过request_fingerprint接口,通过sha1算法来判断两个url请求地址是否相同(注意,这里面不完全是我们之前理解的hash了,如果两个url的地址相同,请求方式和参数都相同,但是请求参数的前后顺序不同的话也别判定为同一个url地址,从而达到url的去重功能。

- queue.py

这是个队列类,它会作为scheduler调度request的容器来维护一个秩序:
  1、 scheduler在每个主机上都会实例化一个,并且和spider一一对应,所以分布式运行时会有一个spider的多个实例和一个scheduler的多个实例存在于不同的主机上。
  2、因为scheduler都是用相同的容器,而这些容器都连接同一个 redis服务器,又都使用spider名 + queue来作为key 读写数据,所以不同主机上的不同爬虫实例公用一个request调度池,实现了分布式爬虫之间的统一调度。

- picklecompat.py

这里实现了loads和dumps两个函数,其实就是实现了一个serializer:
  1、因为redis数据库不能存储复杂对象(value部分只能是字符串,字符串列表,字符串集合和hash,key部分只能是字符串),所以我们存啥都要先串行化成文本才行。这里使用的就是python的pickle模块,一个兼容py2和py3的串行化工具。

- pipelines.py

pipelines.py中类的作用:
  pipeline.py文件用来实现数据分布式处理。它通过从settings中拿到我们配置的REDIS_ITEMS_KEY作为key,把item串行化之后存入redis数据库对应的value中(这个value可以看出是个list,我们的每个item是这个list中的一个结点),这个pipeline把提取出的item存起来,主要是为了方便我们延后处理数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值