笔记-爬虫-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. 运行
- 在主从机上都运行爬虫,爬虫进入等待状态,
因为都要去redis数据库的sina_news:start_urls中取链接,但现在没有该KEY,所以都在等待;
- 在redis数据库中添加初始爬取地址:
lpush sinanewsspider:start_urls http://news.sina.com.cn/guide/
- 然后就开始爬取了,在案例中设定了一个全局参数pages_max_num限制二级解析的YIELD次数,以此来限定总爬取页面数;
- 爬取完成,主从爬虫都进入等待状态。
3.6. 理解
scrapy-redis在数据库中新增三个key,dupefilter,items,requests:
- items:很好理解,存放item数据;
- dupefilter:
- requests:请求,
个人理解:为了实现分布式爬虫,需要一个跨平台的信息传递,目前是通过redis的远程访问满足这一点,至于为什么是redis而不是其它数据库或中间件那是另外一个问题了;
下一个问题是传递什么信息,最简单也是数据量最小的是传递url地址,但这样功能不够丰富,scrapy-redis放进去的是requests;
总之具体实现就是scrapy-redis把请求放到redis数据库中,爬虫去数据库中拿到请求,爬取,再把结果放到items中。
关于dupefilter,它是用于去重,看上去是hash的结果,类似于指纹。
4. scrapy-redis原理理解
4.1. scrapy-redis分布式爬取实现原理
因为官方文档没什么内容,下面的内容取自网络及个人理解。
从爬虫实现的过程来讲,爬虫分布式最容易实现的方式是共享请求,也就是“待爬队列”;
从爬虫整体的合理设计来讲,爬虫要做的事就是得到请求,去重,采集,存储数据四部分,下面一一解释scrapy-redis的实现方法。
- 请求发起/获取
怎么发起就不废话了,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数据存储
分布式爬取带来的一个问题是数据在不同的主机上,那么理论上有两种方法:
- 各存各的,master提供url,slave采集后存在本地,把title或指纹汇总,这样好处是开销小,问题是数据汇总麻烦;
- 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组件的更多相关文章
- Python分布式爬虫打造搜索引擎完整版-基于Scrapy、Redis、elasticsearch和django打造一个完整的搜索引擎网站
Python分布式爬虫打造搜索引擎 基于Scrapy.Redis.elasticsearch和django打造一个完整的搜索引擎网站 https://github.com/mtianyan/Artic ...
- 爬虫 scrapy 笔记
scrapy 基础 1. 创建一个spider项目 a) Scrapy startproject project_name [project_dir] b) Cd p ...
- 爬虫Ⅱ:scrapy框架
爬虫Ⅱ:scrapy框架 step5: Scrapy框架初识 Scrapy框架的使用 pySpider 什么是框架: 就是一个具有很强通用性且集成了很多功能的项目模板(可以被应用在各种需求中) scr ...
- Scrapy、Scrapy-redis组件
目录 Scrapy 一.安装 二.基本使用 1. 基本命令 2.项目结构以及爬虫应用简介 3. 小试牛刀 4. 选择器 5. 格式化处理 6.中间件 7. 自定制命令 8. 自定义扩展 9. 避免重复 ...
- 自己动手实现爬虫scrapy框架思路汇总
这里先简要温习下爬虫实际操作: cd ~/Desktop/spider scrapy startproject lastspider # 创建爬虫工程 cd lastspider/ # 进入工程 sc ...
- 笔记-爬虫-去重/bloomfilter
笔记-爬虫-去重/bloomfilter 1. 去重 为什么要去重? 页面重复:爬的多了,总会有重复的页面,对已爬过的页面肯定不愿意再爬一次. 页面更新:很多页面是会更新的,爬取这种页面时就 ...
- 网页爬虫--scrapy入门
本篇从实际出发,展示如何用网页爬虫.并介绍一个流行的爬虫框架~ 1. 网页爬虫的过程 所谓网页爬虫,就是模拟浏览器的行为访问网站,从而获得网页信息的程序.正因为是程序,所以获得网页的速度可以轻易超过单 ...
- WebMagic的设计参考了业界最优秀的爬虫Scrapy
http://webmagic.io/docs/zh/posts/ch1-overview/thinking.html https://github.com/psvehla/liferay-sprin ...
- python爬虫scrapy项目详解(关注、持续更新)
python爬虫scrapy项目(一) 爬取目标:腾讯招聘网站(起始url:https://hr.tencent.com/position.php?keywords=&tid=0&st ...
随机推荐
- jQueryMobile(一)
一].jQuery Mobile 页面 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta ...
- 将pugixml库编译成动态库的做法
作者:朱金灿 来源:http://blog.csdn.net/clever101 pugixml库默认是编译成静态库的.要把pugixml库编译成一个动态库,需要对代码做一些修改,具体是将 // If ...
- dojo query 基本用法
1. 常用的 dojo.query 用法 dojo.query("#header > h1") //ID 为 header 的元素的直接子节点中的 h3 元素 dojo. ...
- 修改Android系统关机动画
文件路径:frameworks\base\services\core\java\com\android\server\power\ShutdownThread.java 在beginShutdownS ...
- Android开发基础
一.Android开发环境搭建 1.下载安卓SDK 官方下载地址:http://developer.android.com/sdk/index.html 2.下载安装JDK 官方下载地址:JDK6 h ...
- Unity利用AnimationCurve做物体的各种运动
之前一直都是自己学习Unity各种做Demo,最近开始正式使用Unity来做一个款2d的游戏. 其中在做一个类似小球弹跳运动的时候遇到了点问题,查找了很多资料,无意间发现AnimationCurve ...
- Eclipse Push出现rejected - non-fast-forward错误
在 Push到服务器时有时会出现 rejected - non-fast-forward 错误,这是由于pull的代码而远端发生改变,此时再提交之前你需要将远端的改变合并到本地上 参考:https:/ ...
- 进程—内存描述符(mm_struct)
http://blog.csdn.net/qq_26768741/article/details/54375524 前言 上一篇我们谈论了task_struct这个结构体,它被叫做进程描述符,内部成员 ...
- 【洛谷4459】[BJOI2018] 双人猜数游戏(动态规划)
点此看题面 大致题意: 一直有两个数\(m,n\),已知\(s\le m\le n\),且\(Alice\)和\(Bob\)二个"最强大佬"各知道\(mn\)和\(m+n\).每轮 ...
- LA 3902 网络
题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...