笔记-scrapy-深入学习-sheduler

1.      scheduler.py

source code:scrapy/core/scheduler.py:

1.1.    初始化的开始

在分析engine的open_spider函数时,讲过scheduler对象是通过类的from_cralwer方法生成的,代码如下:

@classmethod

def from_crawler(cls, crawler):

settings = crawler.settings

dupefilter_cls = load_object(settings['DUPEFILTER_CLASS'])

dupefilter = dupefilter_cls.from_settings(settings)

pqclass = load_object(settings['SCHEDULER_PRIORITY_QUEUE'])

dqclass = load_object(settings['SCHEDULER_DISK_QUEUE'])

mqclass = load_object(settings['SCHEDULER_MEMORY_QUEUE'])

logunser = settings.getbool('LOG_UNSERIALIZABLE_REQUESTS')

return cls(dupefilter, jobdir=job_dir(settings), logunser=logunser,

stats=crawler.stats, pqclass=pqclass, dqclass=dqclass, mqclass=mqclass)

在最后实际还是调用类的__init__()。

class Scheduler(object):

def __init__(self, dupefilter, jobdir=None, dqclass=None, mqclass=None,

logunser=False, stats=None, pqclass=None):

self.df = dupefilter

self.dqdir = self._dqdir(jobdir)

self.pqclass = pqclass

self.dqclass = dqclass

self.mqclass = mqclass

self.logunser = logunser

self.stats = stats

在这里指定了各种队列类型,但并没有构造队列,在下面会讲到。

实际上调度器默认只定义了2种队列类型,但并不是在这里构造的:

dqs:基于磁盘的任务队列:在配置文件可配置存储路径,每次执行后会把队列任务保存到磁盘上;

mqs:基于内存的任务队列:每次都在内存中执行,下次启动则消失;

1.2.    核心对象

前面共创建了4个对象,分别是dupefilter,pqclass,dqclass,mqclass.

1.dupefilter:

DUPEFILTER_CLASS = 'scrapy.dupefilters.RFPDupeFilter'

这个类的含义是"Request Fingerprint duplicates filter",请求指纹重复过滤。

它对每个request请求生成指纹,对指纹重复的请求进行过滤。具体实现见文档:scrapy-去重

2.pqclass

SCHEDULER_PRIORITY_QUEUE = 'queuelib.PriorityQueue'

从名字上也可以看出这个一个优先级队列,使用的是queuelib.它的作用应该不说也明白就是对request请求按优先级进行排序。

如何指定优先级?

前面讲spider时,讲述过可以在spider中定义Rule规则来过滤我们需要跟进的链接形式,我们只要定义规则时指定一个process_request关键字参数即可,这个参数是一个函数,会传递给我们将要继续跟进的Request,我们直接对其设置priority属性即可。

优先级是一个整数,虽然queuelib使用小的数做为高优化级,但是由于scheduler再入队列时取了负值,所以实际上数值越大优先级越高,代码如下:

def _dqpush(self, request):

if self.dqs is None:

return

try:

reqd = request_to_dict(request, self.spider)

self.dqs.push(reqd, -request.priority)

3.dqclass

SCHEDULER_DISK_QUEUE = 'scrapy.squeues.PickleLifoDiskQueue'

从名字上看,这是一个支持序列化的后进先出的磁盘队列。主要用来帮助我们在停止爬虫后可以接着上一次继续开始爬虫。

序列化要指定一个目录,用于存储序列化文件。这个目录在命令行上通过'-s JOBDIR=XXX'来指定。scheduler会在这个目录下创建active.json文件,用来序列化队列的优先级。

def _dq(self):

activef = join(self.dqdir, 'active.json')

if exists(activef):

with open(activef) as f:

prios = json.load(f)

else:

prios = ()

q = self.pqclass(self._newdq, startprios=prios)

if q:

logger.info("Resuming crawl (%(queuesize)d requests scheduled)",

{'queuesize': len(q)}, extra={'spider': self.spider})

return q

_dq在engine open_spider时调用scheduler的open时调用,可以看到如果命令指定了JOBDIR参数,则从目录下寻找active.json,这个文件存储的上一次指定的优先级集合,然后用它和_newdq一起构造磁盘队列,这样就可以接着上次停止时的状态继续爬取了。

其中_newdq会使用JOBDIR和优先级作为参数初始化磁盘队列对象。

def _newdq(self, priority):

return self.dqclass(join(self.dqdir, 'p%s' % priority))

最后在scheduler关闭时会将优化级存入文件active.json文件,用于下次反序列化。

def close(self, reason):

if self.dqs:

prios = self.dqs.close()

with open(join(self.dqdir, 'active.json'), 'w') as f:

json.dump(prios, f)

return self.df.close(reason)

4.mqclass

SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.LifoMemoryQueue'

从名字上看,是后进先出的内存队列。这个队列是为了构造优先级队列而存在的,在构造优先级队列时,需要传递一个队列工厂类,用它来构造每个不同的优先级队列,构造时会向这个队列工厂类传递优先级作为唯一的参数。

默认情况下实际上就是queuelib.LifoMemoryQueue.

1.3.    入列及出列

前面了解了内存队列和磁盘队列,下面看下scheduler怎样出列入列:

请求的获取和存入流程:

def next_request(self):

request = self.mqs.pop()

if request:

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

else:

request = self._dqpop()

if request:

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

if request:

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

return request

取请求时优先使用内存队列,如果内存队列没有请求再使用磁盘队列。

在请求入队列时,优先存入磁盘队列,如果没有磁盘队列再存入内存队列。

def enqueue_request(self, request):

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

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

return False

dqok = self._dqpush(request)

if dqok:

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

else:

self._mqpush(request)

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

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

return True

Scheduler在入列前调用过滤器进行过滤,如果request对象没有定义dont_filter选项,则用df来过滤,关于去重方法,见文档:scrapy-去重。.

调度器的核心基本就这些了。

另外还有一个LOG_UNSERIALIZABLE_REQUESTS参数,它是用来指定如果一个请求序列化失败,是否要记录日志。

1.4.    队列实现

前面定义了队列,但并没有实例化,

def open(self, spider):

self.spider = spider

self.mqs = self.pqclass(self._newmq)

self.dqs = self._dq() if self.dqdir else None

return self.df.open()

使用self.pqclass(self._newmq),默认情况下等效于PriorityQueue(scrapy.squeues.LifoMemoryQueue)

class PriorityQueue(object):

"""A priority queue implemented using multiple internal queues (typically,

FIFO queues). The internal queue must implement the following methods:

"""

def __init__(self, qfactory, startprios=()):

self.queues = {}

self.qfactory = qfactory

for p in startprios:

self.queues[p] = self.qfactory(p)

self.curprio = min(startprios) if startprios else None

def push(self, obj, priority=0):

if priority not in self.queues:

self.queues[priority] = self.qfactory(priority)

q = self.queues[priority]

q.push(obj) # this may fail (eg. serialization error)

if self.curprio is None or priority < self.curprio:

self.curprio = priority

def pop(self):

if self.curprio is None:

return

q = self.queues[self.curprio]

m = q.pop()

if len(q) == 0:

del self.queues[self.curprio]

q.close()

prios = [p for p, q in self.queues.items() if len(q) > 0]

self.curprio = min(prios) if prios else None

return m

这里是优先级队列的初始化代码,queues是一个dict,dict元素是priority:qfactory形式。

结合起来的结果就是scrapy待爬队列初始化都是优先级队列,如果以后所有request的优先级相同(默认0),那么待爬队列就时一个FIFO/LIFO。如果使用不同的优先级,那么就是一个优先级队列了。

qfactory指定二级队列类型,它来自SCHEDULER_MEMORY_QUEUE = 'scrapy.squeues.LifoMemoryQueue',

修改它有两种结果:

  1. 所有请求优先级为0或相同,那么待爬队列就是一个LIFO或FIFO;
  2. 请求的优先级不同,那么待爬队列就是一个优先级队列,优等级队列的元素值也是队列(LIFO或FIFO)。

1.5.    爬取方式

从上一章节可以看出scrapy默认使用LIFO,那么它的爬取方式是深度优先,如果想使用广度优先,把SCHEDULER_MEMORY_QUEUE 改为FIFO就可以了。

笔记-scrapy-深入学习-sheduler的更多相关文章

  1. HTML+CSS学习笔记 (6) - 开始学习CSS

    HTML+CSS学习笔记 (6) - 开始学习CSS 认识CSS样式 CSS全称为"层叠样式表 (Cascading Style Sheets)",它主要是用于定义HTML内容在浏 ...

  2. scrapy爬虫学习系列五:图片的抓取和下载

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  3. scrapy爬虫学习系列四:portia的学习入门

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  4. scrapy爬虫学习系列二:scrapy简单爬虫样例学习

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  5. scrapy爬虫学习系列一:scrapy爬虫环境的准备

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  6. scrapy爬虫学习系列三:scrapy部署到scrapyhub上

    系列文章列表: scrapy爬虫学习系列一:scrapy爬虫环境的准备:      http://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_python_00 ...

  7. scrapy再学习与第二个实例

    这周对于Scrapy进一步学习,知识比较零散,需要爬取的网站因为封禁策略账号还被封了/(ㄒoㄒ)/~~ 一.信息存储 1.log存储命令:scrapy crawl Test --logfile=tes ...

  8. 【笔记】MySQL学习之索引

    [笔记]MySQL学习之索引 一 索引简单介绍 索引,是数据库中专门用于帮助用户快速查询数据的一种数据结构.类似于字典中的目录,查找字典内容时可以根据目录查找到数据的存放位置,然后直接获取即可. 普通 ...

  9. 笔记-redis深入学习-1

    笔记-redis深入学习-1 redis的基本使用已经会了,但存储和读取只是数据库系统最基础的功能: 数据库系统还得为可靠实现这两者提供一系列保证: 数据.操作备份和恢复,主要是持久化: 高可用:主要 ...

随机推荐

  1. es6-正则

    RegExp构造函数 在ES5中,RegExp构造函数的参数有两种情况. 第一种情况是,参数是字符串,这时第二个参数表示正则表达式的修饰符(flag). var regex = new RegExp( ...

  2. 零基础逆向工程34_Win32_08_线程控制_CONTEXT结构

    线程控制 实验 挂起线程 ::SuspendThread(hThread); 恢复线程 ::ResumeThread(hThread); 终止线程 (这里讲了同步调用与异步调用) 方式一: 此方法结束 ...

  3. Struts2_HelloWorld_5

    1.设置开发模式: 更换配置文件中的 action name="hello_struts",重新访问会出错,因为配置没有立刻响应文件的即时修改. 需要加上配置 <consta ...

  4. [总结]SHAREPOINT - CAML列表查询(上)

    首先要了解的是CAML(Collaboration Application Markup Language)不仅仅是用在对列表.文档库的查询,字段的定义,站点定义等处处使用的都是CAML. 简单的提一 ...

  5. dell omsa管理工具

    dell服务器raid管理工具 lsiutil dell sas6i/r MegaCli dell prec omsa管理raid分区 显示物理硬盘信息 omreport storage pdisk ...

  6. ASP.NET设置母版页

    母版页允许开发人员创建具有指定的可编辑区域的站点级模板.随后,此模板可应用到网站中的 ASP.NET 页面上.这些 ASP.NET 页面只需为母版页中指定的可编辑区域提供相应内容 – 在使用母版页的所 ...

  7. [转]Windows 下常用盗版软件的替代免费软件列表

    当您看完这篇文章,我相信您完全可以把您 Windows 系统里安装的盗版软件清理干净而不影响您的任何工作.如果您仍然希望并且喜欢.享受做一个盗版软件用户的话,那也没有办法,但是请您记住,非常非常重要的 ...

  8. 酷狗音乐PC端怎么使用听歌识曲功能?

    生活中很多时候会听到一些美妙的音乐,耳熟或者动听却不知道它的名字.就像第一眼看到你心动的那个她却不知她叫什么.移动端有酷狗音乐的听歌识曲.现在PC端也有了相同的功能,每当我们看到一部精彩影视剧听到美妙 ...

  9. Linux教程之:Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use)

    Nginx [emerg]: bind() to 0.0.0.0:80 failed (98: Address already in use) 使用命令关闭占用80端口的程序 sudo fuser - ...

  10. nodejs使用MYSQL连接池,断线重连

    两种方式解决1.你可以配置mysql的连接池 var mysql = require('mysql'); var pool = mysql.createPool({ host: 'localhost' ...