scrapy源码阅读笔记(2) -- scheduler
2016-12-17 本文已影响540人
troy_ld
数据流向
关于Scheduler
Scheduler主要负责scrapy请求队列的管理,即进队与出队。进一步来说,会涉及到队列的选择,队列去重,序列化。
属性/方法 | 功能 | 描述 |
---|---|---|
df | 去重模块 | 默认利用set在内存去重 |
dqdir | 磁盘队列路径 | 持久化队列至硬盘 |
pqclass | 带优先级队列 | 默认来自queuelib |
dqclass | 磁盘队列 | 持久化队列至硬盘 |
mqclass | 内存队列 | 默认来自queuelib |
stats | 状态记录 | 状态记录通用模块 |
from_crawler | 实例化入口 | scrapy风格的实例化入口 |
has_pending_requests | 检查队列数 | 指向len |
open | 初始化队列 | scrapy模块的初始化入口 |
close | 安全退出接口 | scrapy模块的安全入口 |
enqueue_request | 进队api | 调度进队 |
next_request | 出队api | 调度出队 |
另外,enqueue_request next_request 封装了一些内部函数,指向queue。
去重 scrapy.dupefilters.RFPDupeFilter
class RFPDupeFilter(BaseDupeFilter):
def request_seen(self, request):
fp = self.request_fingerprint(request)
if fp in self.fingerprints:
return True
self.fingerprints.add(fp)
if self.file:
self.file.write(fp + os.linesep)
def request_fingerprint(self, request):
return request_fingerprint(request)
scrapy默认去重方案:
- 利用request生成fingerprint, 存入set
- 每次利用set判断
- 如果用了 disk queue 追加至文件
request_fingerprint默认采用的hashlib.sha1,利用headers/method/url/body生成
队列 queuelib
包含了一些基本的队列FIFO/LIFO, 以及带有优先级的PriorityQueue。Github 上有功能用法介绍
爬取策略
针对不同的场景可能会选择不同的队列。
- FIFO 先进先出, 队列针对时间顺排
- LIFO 后进先出, 队列针对时间倒排
- DFS 深度优先, 针对depth(按照referer建立树的深度)倒排
- BFS 广度优先, 针对depth(按照referer建立树的深度)顺排
在scrapy的文档中有介绍如何设置各种队列。在frontera中也是以这几种策略为基础,生成scoring log
值得一提的是,在网站数目较多的时候(200~500)个, 这些策略的稳定性都不是很好,(控制单个域名访问频率)亲测都有一定概率遭遇队列阻塞。刚开始下载量可以到1200 page/min, 渐渐降低到300~500, 谈不上慢,但总有些损耗。
域名队列
为了解决域名阻塞的问题,chamilto设计了域名队列去改进, 基本思路
- 发现新增域名时即建立域名-队列映射/域名队列/request队列
- 索取新request之前, 先访问域名队列, 再根据映射表访问该域名下的requet
代码实现上,需要修改schduler初始化参数,重载进队出队相关的内部函数。对于内存队列, 性能不是问题, 对于持久化队列(硬盘/数据库)IO压力会增大
序列化
在做队列(对象)持久化/传输时会用到.常见的方案如下
- cpickle/json
- 逐个字段转换,存入数据库
对于scrapy的disk queue来说
scrapy.utils.reqser.request_to_dict 将request转成dict
scrapy.squeues.PickleFifoDiskQueue 做序列化队列