scrapy-redis 分布式爬虫
为什么要学?
Scrapy_redis在scrapy的基础上实现了更多,更强大的功能。
有哪些功能体现?
request去重、爬虫持久化、实现分布式爬虫、断点续爬(带爬取的request存在redis中)、增量式爬虫(爬取过的生成指纹)
工作流程
先来看看之前的爬虫流程

再来看看scrapy_redis的爬虫流程

安装:
pip install scrapy-redis
源码包安装:
git clone git://github.com/rolando/scrapy-redis
官方文档在:https://scrapy-redis.readthedocs.io/en/stable/index.html#running-the-example-project
scrapy_redis 的源码在github:https://github.com/rmax/scrapy-redis
它提供了三个demo在example-projec/example中

三个案例有

先来看第一个案例:
dmoz.py
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule class DmozSpider(CrawlSpider):
"""Follow categories and extract links."""
name = 'dmoz'
allowed_domains = ['dmoz.org']
start_urls = ['http://www.dmoz.org/'] rules = [
Rule(LinkExtractor(
restrict_css=('.top-cat', '.sub-cat', '.cat-item')
), callback='parse_directory', follow=True),
] def parse_directory(self, response):
for div in response.css('.title-and-desc'):
yield {
'name': div.css('.site-title::text').extract_first(),
'description': div.css('.site-descr::text').extract_first().strip(),
'link': div.css('a::attr(href)').extract_first(),
}
这个案例很像我们自己写的crawlspider什么区别,所以接下来就要进行配置操作
先来看看官方的 Use the following settings in your project:
# 指定schedule队列
# Enables scheduling storing requests queue in redis.
SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 指定哪个去重方法给request对象去重
# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # Default requests serializer is pickle, but it can be changed to any module
# with loads and dumps functions. Note that pickle is not compatible between
# python versions.
# Caveat: In python 3.x, the serializer must return strings keys and support
# bytes as values. Because of this reason the json or msgpack module will not
# work by default. In python 2.x there is no such issue and you can use
# 'json' or 'msgpack' as serializers.
#SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat" # 队列中的内容是否持久保存,False:在关闭redis的时候清空redis
# Don't cleanup redis queues, allows to pause/resume crawls.
#SCHEDULER_PERSIST = True # Schedule requests using a priority queue. (default)
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue' # Alternative queues.
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
#SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue' # Max idle time to prevent the spider from being closed when distributed crawling.
# This only works if queue class is SpiderQueue or SpiderStack,
# and may also block the same time when your spider start at the first time (because the queue is empty).
#SCHEDULER_IDLE_BEFORE_CLOSE = 10 # scrapy_redis实现的items保存到redis的pipeline
# Store scraped item in redis for post-processing.
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
} # The item pipeline serializes and stores the items in this redis key.
#REDIS_ITEMS_KEY = '%(spider)s:items' # The items serializer is by default ScrapyJSONEncoder. You can use any
# importable path to a callable object.
#REDIS_ITEMS_SERIALIZER = 'json.dumps' # 指定redis的地址
# Specify the host and port to use when connecting to Redis (optional).
#REDIS_HOST = 'localhost'
#REDIS_PORT = 6379 # 指定redis的地址
# Specify the full Redis URL for connecting (optional).
# If set, this takes precedence over the REDIS_HOST and REDIS_PORT settings.
#REDIS_URL = 'redis://user:pass@hostname:9001' # Custom redis client parameters (i.e.: socket timeout, etc.)
#REDIS_PARAMS = {}
# Use custom redis client class.
#REDIS_PARAMS['redis_cls'] = 'myproject.RedisClient' # If True, it uses redis' ``spop`` operation. This could be useful if you
# want to avoid duplicates in your start urls list. In this cases, urls must
# be added via ``sadd`` command or you will get a type error from redis.
#REDIS_START_URLS_AS_SET = False # Default start urls key for RedisSpider and RedisCrawlSpider.
#REDIS_START_URLS_KEY = '%(name)s:start_urls' # Use other encoding than utf-8 for redis.
#REDIS_ENCODING = 'latin1'
所以我们需要做的就是在配置文件中添加这几行
# 去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 调度器持久化
SCHEDULER_PERSIST = True # 指定redis地址
REDIS_URL = "redis://192.168.226.150:6379"
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400, # 保存数据到redis
}
在这里也贴上github上的settings:
# Scrapy settings for example project
#
# For simplicity, this file contains only the most important settings by
# default. All the other settings are documented here:
#
# http://doc.scrapy.org/topics/settings.html
#
SPIDER_MODULES = ['example.spiders']
NEWSPIDER_MODULE = 'example.spiders' USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)' DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack" ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400,
} LOG_LEVEL = 'DEBUG' # Introduce an artifical delay to make use of parallelism. to speed up the
# crawl.
DOWNLOAD_DELAY = 1
git hub
接下来我们就运行,跟往常的运行命令一样,切换到example 下,执行命令
\example>scrapy crawl dmoz
然后就去redis中查看

可以看到多了三个键
dmoz:items :存放获取到的item信息,在pipeline中开启RedisPipeline才会存入
dmoz:dupefilter :指纹集合,存放的是已经进入 scheduler 队列的 request 对象的指纹,指纹默认由请求方法,url和请求体组成
dmoz:requests :Scheduler队列,存放着待请求的 request 对象,获取的过程是pop操作,即获取一个会去除一个
三个键的类型:

以及数据
items

dupefilter

requests

如果我们不想把数据存放到redis,而是放到其他的地方,应该怎么做?
先来测试一下redispiipeline 关闭的情况
#ITEM_PIPELINES = {
# 'example.pipelines.ExamplePipeline': 300,
#'scrapy_redis.pipelines.RedisPipeline': 400, # 保存数据到redis
#}
再看看redis数据库中这三个键如何变化,变化结果:
dmoz:requests 有变化(变多或者变少或者不变)
dmoz:dupefilter 变多
dmoz:items 不变
所以 redispipeline中仅仅实现了item数据存储到redis的过程,我们可以新建一个pipeline(或者修改默认的ExamplePipeline),让数据存储到其他地方。
接下来看一下 RedisPipeline 的源码段
from scrapy_redis.pipelines import RedisPipeline
# 调用这个方法,实现数据的保存
def process_item(self, item, spider):
# 调用一个异步线程去处理这个item
return deferToThread(self._process_item, item, spider) def _process_item(self, item, spider):
key = self.item_key(item, spider)
data = self.serialize(item)
# 向dmoz:items中添加item
self.server.rpush(key, data)
return item
去重的方法,通过生成指纹识别:指纹默认由请求方法,url和请求体组成
- 使用sha1加密request得到指纹
- 把指纹存在redis的集合中
- 下一次新来一个request,同样的方式生成指纹,判断指纹是否存在reids的集合中
- fp = hashlib.sha1()
- fp.update(request.method)
- fp.update(request.body or b"")
- fp.update(url)
- fp.hexdigest()
判断数据是否存在redis的集合中,不存在插入
added = self.server.sadd(self.key, fp)
return added != 0
去重的的类 RFPDupeFilter :


主要是这三个方法:
request_seen:判断requests对象是否已经存在,如果没有就添加到“dmoz:dupefilter”
request_fingerprint:调用函数request_fingerprint
request_fingerprint:主要是对请求进行加密生成指纹 下面来看看调度器:

总结:
domz案例相当于之前的spider多了两个内容:
1、持久化
2、request去重的功能
通过源码以及setting中的配置来看,我们可以重写 去重 和 调度器的方法 ,还有存储数据的 pipeline 。在配置中改成我们重写的类就行。
Scrapy_redis 之 RedisSpider
这是scrapy_redis 的第二个案例
先来看试一下初始源码
from scrapy_redis.spiders import RedisSpider class MySpider(RedisSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'myspider_redis'
redis_key = 'myspider:start_urls' def __init__(self, *args, **kwargs):
# Dynamically define the allowed domains list.
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MySpider, self).__init__(*args, **kwargs) def parse(self, response):
return {
'name': response.css('title::text').extract_first(),
'url': response.url,
}
myspider_redis.py
解析源码

配置文件设置:

# 去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 调度器持久化
SCHEDULER_PERSIST = True # 指定redis地址
REDIS_URL = "redis://192.168.226.150:6379"
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400, # 保存数据到redis
}

start_url 只能由一台机器爬取,其他机器刚开始处于等待状态,这是随机的
但是这里的start_url 改为redis_key='自定义键名' 例如('dangdang')
那么执行的启动命令就是在redis中执行这条语句:lpush 健名 起始url
lpush dangdang http://www.dangdang.com
Scrapy_redis 之 RedisCrawlSpider
这是scrapy_redis 的第三个案例
先来看初始源码
from scrapy.spiders import Rule
from scrapy.linkextractors import LinkExtractor from scrapy_redis.spiders import RedisCrawlSpider class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
redis_key = 'mycrawler:start_urls' rules = (
# follow all links
Rule(LinkExtractor(), callback='parse_page', follow=True),
) def __init__(self, *args, **kwargs):
# Dynamically define the allowed domains list.
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MyCrawler, self).__init__(*args, **kwargs) def parse_page(self, response):
return {
'name': response.css('title::text').extract_first(),
'url': response.url,
}
mycrawl_redis.py
解析源码

配置文件

# 去重
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 调度器持久化
SCHEDULER_PERSIST = True # 指定redis地址
REDIS_URL = "redis://192.168.226.150:6379"
ITEM_PIPELINES = {
'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400, # 保存数据到redis
}

这个案例跟crawlspider的使用差不多,也是没有start_url 改为了 redis_key
启动方式跟案例二一样。爬虫准备就绪,redis数据库中
lpush 健名 起始url
scrapy-redis 分布式爬虫的更多相关文章
- Scrapy 框架 分布式 爬虫
分布式 爬虫 scrapy-redis 实现 原生scrapy 无法实现 分布式 调度器和管道无法被分布式机群共享 环境安装 - pip install scrapy_redis 导包:from sc ...
- scrapy简单分布式爬虫
经过一段时间的折腾,终于整明白scrapy分布式是怎么个搞法了,特记录一点心得. 虽然scrapy能做的事情很多,但是要做到大规模的分布式应用则捉襟见肘.有能人改变了scrapy的队列调度,将起始的网 ...
- scrapy进行分布式爬虫
今天,参照崔庆才老师的爬虫实战课程,实践了一下分布式爬虫,并没有之前想象的那么神秘,其实非常的简单,相信你看过这篇文章后,不出一小时,便可以动手完成一个分布式爬虫! 1.分布式爬虫原理 首先我们来看一 ...
- 16 Scrapy之分布式爬虫
redis分布式部署 1.scrapy框架是否可以自己实现分布式? - 不可以.原因有二. 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls ...
- scrapy补充-分布式爬虫
spiders 介绍:在项目中是创建爬虫程序的py文件 #1.Spiders是由一系列类(定义了一个网址或一组网址将被爬取)组成,具体包括如何执行爬取任务并且如何从页面中提取结构化的数据. #2.换句 ...
- 【Python3爬虫】爬取美女图新姿势--Redis分布式爬虫初体验
一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...
- 【Python3爬虫】学习分布式爬虫第一步--Redis分布式爬虫初体验
一.写在前面 之前写的爬虫都是单机爬虫,还没有尝试过分布式爬虫,这次就是一个分布式爬虫的初体验.所谓分布式爬虫,就是要用多台电脑同时爬取数据,相比于单机爬虫,分布式爬虫的爬取速度更快,也能更好地应对I ...
- 爬虫--scrapy+redis分布式爬取58同城北京全站租房数据
作业需求: 1.基于Spider或者CrawlSpider进行租房信息的爬取 2.本机搭建分布式环境对租房信息进行爬取 3.搭建多台机器的分布式环境,多台机器同时进行租房数据爬取 建议:用Pychar ...
- 基于scrapy的分布式爬虫抓取新浪微博个人信息和微博内容存入MySQL
为了学习机器学习深度学习和文本挖掘方面的知识,需要获取一定的数据,新浪微博的大量数据可以作为此次研究历程的对象 一.环境准备 python 2.7 scrapy框架的部署(可以查看上一篇博客的简 ...
- 第三百六十五节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)的基本查询
第三百六十五节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)的基本查询 1.elasticsearch(搜索引擎)的查询 elasticsearch是功能 ...
随机推荐
- Open-Source Service Discovery
Service discovery is a key component of most distributed systems and service oriented architectures. ...
- CSS布局十八般武艺都在这里了
CSS布局十八般武艺都在这里了 Shelley Lee 4 个月前 布局是CSS中一个重要部分,本文总结了CSS布局中的常用技巧,包括常用的水平居中.垂直居中方法,以及单列布局.多列布局的多种实现方式 ...
- javascript 事件编程之事件(流,处理,对象,类型)
1. 事件处理 1.1. 绑定事件方式 1)行内绑定 语法: //最常用的使用方式 <元素 事件="事件处理程序"> 2)动态绑定 //结构+样式+行为分离的页面(ht ...
- js定时器让动画隔秒运动
现有一个需求,宝箱隔几秒动一次,抓住用户眼球,自己写了个 doem.
- Spring IOC知识点一网打尽!
前言 只有光头才能变强 回顾前面: 给女朋友讲解什么是代理模式 包装模式就是这么简单啦 单例模式你会几种写法? 工厂模式理解了没有? 在刷Spring书籍的时候花了点时间去学习了单例模式和工厂模式,总 ...
- config.go
package blog4go import ( "encoding/xml" "errors" "io/ioutil" "os& ...
- 【LOJ #6094. 「Codeforces Round #418」归乡迷途】
题目大意: 传送门. lca说的很明白就不重复了. 题解: 先膜一发lca. 大体读完题以后我们可以知道对于第i个节点最短路一定是连向1到i-1中的某个点. 然后我们考虑将到1距离(这里及以下均是最短 ...
- BZOJ_1316_树上的询问_点分治
BZOJ_1316_树上的询问_点分治 Description 一棵n个点的带权有根树,有p个询问,每次询问树中是否存在一条长度为Len的路径,如果是,输出Yes否输出No. Input 第一行两个整 ...
- K短路 (A*算法) [Usaco2008 Mar]牛跑步&[Sdoi2010]魔法猪学院
A*属于搜索的一种,启发式搜索,即:每次搜索时加一个估价函数 这个算法可以用来解决K短路问题,常用的估价函数是:已经走过的距离+期望上最短的距离 通常和Dijkstra一起解决K短路 BZOJ1598 ...
- #利用openCV裁脸
#利用openCV裁脸import cv2 def draw_rects(img, rects): for x, y, w, h in rects: cv2.rectangle(img, (x, y) ...