笔记-爬虫-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的类。

笔记-爬虫-scrapy-srcapy-redis组件的更多相关文章

  1. Python分布式爬虫打造搜索引擎完整版-基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站

    Python分布式爬虫打造搜索引擎 基于Scrapy.Redis.elasticsearch和django打造一个完整的搜索引擎网站 https://github.com/mtianyan/Artic ...

  2. 爬虫 scrapy 笔记

    scrapy 基础 1.  创建一个spider项目 a)         Scrapy startproject project_name [project_dir] b)         Cd p ...

  3. 爬虫Ⅱ:scrapy框架

    爬虫Ⅱ:scrapy框架 step5: Scrapy框架初识 Scrapy框架的使用 pySpider 什么是框架: 就是一个具有很强通用性且集成了很多功能的项目模板(可以被应用在各种需求中) scr ...

  4. Scrapy、Scrapy-redis组件

    目录 Scrapy 一.安装 二.基本使用 1. 基本命令 2.项目结构以及爬虫应用简介 3. 小试牛刀 4. 选择器 5. 格式化处理 6.中间件 7. 自定制命令 8. 自定义扩展 9. 避免重复 ...

  5. 自己动手实现爬虫scrapy框架思路汇总

    这里先简要温习下爬虫实际操作: cd ~/Desktop/spider scrapy startproject lastspider # 创建爬虫工程 cd lastspider/ # 进入工程 sc ...

  6. 笔记-爬虫-去重/bloomfilter

    笔记-爬虫-去重/bloomfilter 1.      去重 为什么要去重? 页面重复:爬的多了,总会有重复的页面,对已爬过的页面肯定不愿意再爬一次. 页面更新:很多页面是会更新的,爬取这种页面时就 ...

  7. 网页爬虫--scrapy入门

    本篇从实际出发,展示如何用网页爬虫.并介绍一个流行的爬虫框架~ 1. 网页爬虫的过程 所谓网页爬虫,就是模拟浏览器的行为访问网站,从而获得网页信息的程序.正因为是程序,所以获得网页的速度可以轻易超过单 ...

  8. WebMagic的设计参考了业界最优秀的爬虫Scrapy

    http://webmagic.io/docs/zh/posts/ch1-overview/thinking.html https://github.com/psvehla/liferay-sprin ...

  9. python爬虫scrapy项目详解(关注、持续更新)

    python爬虫scrapy项目(一) 爬取目标:腾讯招聘网站(起始url:https://hr.tencent.com/position.php?keywords=&tid=0&st ...

随机推荐

  1. 回归JDK源代码(2)Enumeration<E>接口

    现在的Java程序员习惯使用Iterator<E>接口或者增强for循环来遍历集合.如果回到JDK 1.0,Enumeration接口则是遍历向量.哈希表的不二之选.本节就解读和翻译一下E ...

  2. 笨办法学Python(三十五)

    习题 35: 分支和函数 你已经学会了 if 语句.函数.还有列表.现在你要练习扭转一下思维了.把下面的代码写下来,看你是否能弄懂它实现的是什么功能. from sys import exit def ...

  3. 华硕主板开启intel virtual technology以便支持虚拟机

  4. 广搜最短路径变形,(POJ3414)

    题目链接:http://poj.org/problem?id=3414 解题报告: 1.每个节点都是一个独立的状态 2.这里的状态转移就是有几种出路,4种:1.倒掉a中的水,2.把a中的水倒到b中去, ...

  5. 并查集,是否成树,Poj(1308)

    思路: 对于每一条新的边的两个端点,是否是属于一颗树,要是的话,就不是一颗树.否则,就合并. 这里要注意的是,不能是森林,我这里WA了两次了.只不过在最后,查看每个节点的祖先是否是同一个就可以了. # ...

  6. 用蒙特卡洛方法计算派-python和R语言

    用蒙特卡洛方法算pi-基于python和R语言 最近follow了MOOC上一门python课,开始学Python.同时,买来了概率论与数理统计,准备自学一下统计.(因为被鄙视过不是统计专业却想搞数据 ...

  7. iframe的Dom操作

    我最近遇到这样一个需求, 抛开业务相关不谈,但从技术上说:页面中选择公司中的页面,在iframe中展示被选的页面,并且要对页面做一些Dom相关的处理.也就是说我们需要在父级页面中操作子页面(ifram ...

  8. open cv & vs

    原来基于vs和msdn一起读视频,结果发现现在的函数不能用了.找不到合适的解码器了,只好转战opencv. 具体怎么用,网上查吧,不过opencv读视频的例子,可以见这个. http://blog.c ...

  9. linux下jdk的安装配置

    1.下载jdk:地址 选中你选择的版本,下载linux版本对应你系统的32位或64位. 我这里选择的是64位. 2.使用你的ssh直连工具把安装包丢到/usr/local/目录下 3.解压安装jdk ...

  10. java多线程同步以及线程间通信详解&消费者生产者模式&死锁&Thread.join()(多线程编程之二)

    本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: package com.zejian.test; /** * @author ...