笔记-爬虫-scrapy-srcapy-redis组件

笔记-爬虫-scrapy-srcapy-redis组件

 

1.      简介

scrapy是一个爬虫框架,但不支持分布式,scrapy-redis是为了更方便的实现scrapy分布式爬虫的组件。

可以在pypi上找到:https://pypi.org/project/scrapy-redis/

 

1.1.    安装

可以使用pip安装

pip install scrapy-redis

pip show scrapy-redis

目前最新版是0.6.8。

 

2.      使用

Scrapy-redis提供了下面四种组件(components):(意味着原始scrapy爬虫这四个部分都要做相应的修改)

Scheduler

Duplication Filter

Item Pipeline

Base Spider

先不管那么多,先跑一个案例;

 

-----实测------

3.      scrapy-redis实测

3.1.    环境准备

主:虚拟机 centos6.5

从:物理机win8

3.2.    redis安装配置

见笔记-redis安装

 

3.3.    centos python环境安装

centos下已有python环境,安装参考文档:笔记-python3环境安装-centos6.5

安装相关包:

pip3 install redis,scrapy,scrapy-redis,lxml

包括redis-py,scrapy-redis,

通过pip安装就可以了,比较简单。

 

3.4.    代码

与scrapy爬虫代码大同小异,主要是spider类和settings中设置调度器,去重功能:

 

3.4.1.   item

完全一样;

 

3.4.2.   spiders/sina_news.py

spider类的基类改为RedisSpider

from scrapy_redis.spiders import RedisSpider

 

注释掉start_urls。

新增属性:

redis_key = ‘sinanewsspider:start_urls’

这个属性是给redis中建组用的,:作为组名和key名的间隔。

 

3.4.3.   settings.py

需要设置以下内容:

#使用scrapy_redis调度器

SCHEDULER = "scrapy_redis.scheduler.Scheduler"

 

#使用scrapy_redis的去重处理器

DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"

 

#不清理Redis队列

SCHEDULER_PERSIST = True

如果这一项为True,那么在Redis中的URL不会被Scrapy_redis清理掉,这样的好处是:爬虫停止了再重新启动,它会从上次暂停的地方开始继续爬取。但是它的弊端也很明显,如果有多个爬虫都要从这里读取URL,需要另外写一段代码来防止重复爬取。

如果设置成了False,那么Scrapy_redis每一次读取了URL以后,就会把这个URL给删除。这样的好处是:多个服务器的爬虫不会拿到同一个URL,也就不会重复爬取。但弊端是:爬虫暂停以后再重新启动,它会重新开始爬。

 

#redis服务器地址,主机写本地,从机写远程IP

REDIS_HOST = "192.168.199.129"

#redis端口

REDIS_PORT = 6379

 

其他设置(可选)

爬虫请求的调度算法

爬虫的请求调度算法,有三种情况可供选择:

1.队列

SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderQueue'

如果不配置调度算法,默认就会使用这种方式。它实现了一个先入先出的队列,先放进Redis的请求会优先爬取。

2.栈

SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderStack'

这种方式,后放入到Redis的请求会优先爬取。

3.优先级队列

SCHEDULER_QUEUE_CLASS='scrapy_redis.queue.SpiderPriorityQueue'

这种方式,会根据一个优先级算法来计算哪些请求先爬取,哪些请求后爬取。这个优先级算法比较复杂,会综合考虑请求的深度等各个因素。

 

3.4.4.   pipeline

本来就是一个分离的组件,想改就改,不改也没问题。

scrapy-redis自带的pipeline是将items写入redis数据库中的items中。

前面声明的redis_key = ‘sinanewsspider:start_urls’

提供了组名,完整的key名为sinanewsspider:items

 

3.5.    运行

  1. 在主从机上都运行爬虫,爬虫进入等待状态,

因为都要去redis数据库的sina_news:start_urls中取链接,但现在没有该KEY,所以都在等待;

  1. 在redis数据库中添加初始爬取地址:

lpush sinanewsspider:start_urls http://news.sina.com.cn/guide/

  1. 然后就开始爬取了,在案例中设定了一个全局参数pages_max_num限制二级解析的YIELD次数,以此来限定总爬取页面数;
  2. 爬取完成,主从爬虫都进入等待状态。

 

3.6.    理解

scrapy-redis在数据库中新增三个key,dupefilter,items,requests:

  1. items:很好理解,存放item数据;
  2. dupefilter:
  3. requests:请求,

 

个人理解:为了实现分布式爬虫,需要一个跨平台的信息传递,目前是通过redis的远程访问满足这一点,至于为什么是redis而不是其它数据库或中间件那是另外一个问题了;

下一个问题是传递什么信息,最简单也是数据量最小的是传递url地址,但这样功能不够丰富,scrapy-redis放进去的是requests;

总之具体实现就是scrapy-redis把请求放到redis数据库中,爬虫去数据库中拿到请求,爬取,再把结果放到items中。

关于dupefilter,它是用于去重,看上去是hash的结果,类似于指纹。

 

4.      scrapy-redis原理理解

4.1.    scrapy-redis分布式爬取实现原理

因为官方文档没什么内容,下面的内容取自网络及个人理解。

从爬虫实现的过程来讲,爬虫分布式最容易实现的方式是共享请求,也就是“待爬队列”;

从爬虫整体的合理设计来讲,爬虫要做的事就是得到请求,去重,采集,存储数据四部分,下面一一解释scrapy-redis的实现方法。

 

  1. 请求发起/获取

怎么发起就不废话了,scrapy是从爬取队列中获取请求的,它具体的数据结构就是python自带的collection.deque,当然是改造过后的啦(后面所说到的deque均是指scrapy改造之后的队列)。

scrapy-redis提供了一个分布式解决方法,把deque换成redis数据库,在同一个redis服务器写/读要爬取的request,这样就让多个spider共享了请求。

问题来了,换了redis来存放队列,怎么去发起/获取请求?

scrapy中做这个事的是调度器“Scheduler”,它负责对新request入列(加入deque),取出待爬取的request(从deque中出殡)等操作。

另外,在scrapy中,Scheduler为deque提供了一个比较高级的组织方法,它把待爬队列按照优先级建立了一个字典结构,比如:

{

priority0:队列0

priority1:队列2

priority2:队列2

}

然后根据request中的priority属性,来决定该入哪个队列。而出列时,则按priority较小的优先出列。为了管理这个比较高级的队列字典,Scheduler需要提供一系列的方法。

这样做的结果就是如果换了redis做队列,scrapy下的Scheduler就用不了,需要重写。

待爬队列+调度器解决了,分布式爬虫也就基本可以运行了。

 

2.去重

爬虫有一个重要的功能是去重,scrapy中用集合解决;scrapy-redis用dupefilter存放请求的指纹,在进行调度前做对比。

 

3.采集

请求的格式与接口不变,这一部分也不需要变化,与scrapy没什么不同。

 

4数据存储

分布式爬取带来的一个问题是数据在不同的主机上,那么理论上有两种方法:

  1. 各存各的,master提供url,slave采集后存在本地,把title或指纹汇总,这样好处是开销小,问题是数据汇总麻烦;
  2. slave把采集到的数据实时发送到master或其它主机,这样数据存放在一起,slave仅做采集。

实际中 一般使用第二种方法,另备一台mongodb/mysql服务器,用于采集数据存储。

scrapy-redis的RedisPipeline将数据存入master的redis数据库中。

 

5.      源码分析

scrapy-redis官方文档内容有限,要想理解其实现过程,还是得看看源码。

 

5.1.    connection.py

连接redis

有一个问题是没有提供password参数的连接模式

在源码中对于这一部分的处理如下:

url = kwargs.pop('url', None)

    if url:

        return redis_cls.from_url(url, **kwargs)

    else:

        return redis_cls(**kwargs)

它还是调用redis模块的函数去连接,可以参照redis模块中的格式redis://[:password]@localhost:6379/0

注意,在settings中的变量名需要做一个转换:REDIS_URL

 

5.2.    pipelines.py

主要是将内容推进数据库的items中,用于解析结果的共享,一般可以写入其它服务器数据库,减少master的压力。

 

    # 最主要的方法

    def process_item(self, item, spider):

        # 调用了一个线程方法

        return deferToThread(self._process_item, item, spider)

 

    def _process_item(self, item, spider):

        key = self.item_key(item, spider)

        # 序列化item

        data = self.serialize(item)

        # 将item同意添加到redis队列里面, 存到主机

        self.server.rpush(key, data)

        return item

 

5.3.    queue.py

实现了三种队列,

SpiderQueue = FifoQueue

SpiderStack = LifoQueue

SpiderPriorityQueue = PriorityQueue

LIFO,FIFO很好看懂,优先级队列的实现没看懂,调用redis的函数,需要详细去看redis接口代码。

 

5.4.    dupefilter.py

主要是实现了判重,根据源代码来看,scrapy-redis在计算特征码时使用了scrapy本身的一个fingerprint接request_fingerprint。

特征码保存在redis的组名:dupefilter中。

 

5.5.    scheduler.py

爬取队列管理,核心部分如下:

def enqueue_request(self, request):

        if not request.dont_filter and self.df.request_seen(request):

            self.df.log(request, self.spider)

            return False

        if self.stats:

            self.stats.inc_value('scheduler/enqueued/redis', spider=self.spider)

        self.queue.push(request)

        return True

 

    def next_request(self):

        block_pop_timeout = self.idle_before_close

        request = self.queue.pop(block_pop_timeout)

        if request and self.stats:

            self.stats.inc_value('scheduler/dequeued/redis', spider=self.spider)

        return request

 

5.6.    spider.py

不在使用scrapy原有的Spider类,重写的RedisSpider继承了Spider和RedisMixin这两个类,RedisMixin是用来从redis读取url的类。

 

转载于:https://www.cnblogs.com/wodeboke-y/p/9922619.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值