爬虫教程( 4 ) --- 分布式爬虫 scrapy-redis、集群

1、分布式爬虫 scrapy - redis

scrapy 分布式爬虫

文档:http://doc.scrapy.org/en/master/topics/practices.html#distributed-crawls

Scrapy 并没有提供内置的机制支持分布式(多服务器)爬取。不过还是有办法进行分布式爬取, 取决于您要怎么分布了。

如果您有很多spider,那分布负载最简单的办法就是启动多个Scrapyd,并分配到不同机器上。

如果想要在多个机器上运行一个单独的spider,那您可以将要爬取的 url 进行分块,并发送给spider。 例如:

首先,准备要爬取的 url 列表,并分配到不同的文件 url 里:

http://somedomain.com/urls-to-crawl/spider1/part1.list
http://somedomain.com/urls-to-crawl/spider1/part2.list
http://somedomain.com/urls-to-crawl/spider1/part3.list

接着在3个不同的 Scrapd 服务器中启动 spider。spider 会接收一个(spider)参数 part , 该参数表示要爬取的分块:

curl http://scrapy1.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=1
curl http://scrapy2.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=2
curl http://scrapy3.mycompany.com:6800/schedule.json -d project=myproject -d spider=spider1 -d part=3

scrapy-redis 分布式爬虫

scrapy-redis 巧妙的利用 redis 队列实现 request queue 和 items queue,利用 redis 的 set 实现 request 的去重,将 scrapy 从单台机器扩展多台机器,实现较大规模的爬虫集群

Scrapy-Redis 架构分析

scrapy 任务调度是基于文件系统,这样只能在单机执行 crawl。

scrapy-redis 将待抓取 request 请求信息数据 items 信息 的存取放到 redis queue 里,使多台服务器可以同时执行 crawl 和 items process,大大提升了数据爬取和处理的效率。

 scrapy-redis 是基于 redis 的 scrapy 组件,主要功能如下:

  • 分布式爬虫。多个爬虫实例分享一个 redis request 队列,非常适合大范围多域名的爬虫集群
  • 分布式后处理。爬虫抓取到的 items push 到一个 redis items 队列,这就意味着可以开启多个 items processes 来处理抓取到的数据,比如存储到 Mongodb、Mysql
  • 基于 scrapy 即插即用组件。Scheduler + Duplication Filter、Item Pipeline、 Base Spiders

scrapy 原生架构

分析 scrapy-redis 的架构之前先回顾一下 scrapy 的架构

  • 调度器(Scheduler):调度器维护 request 队列,每次执行取出一个 request。
  • Spiders:Spider 是 Scrapy 用户编写用于分析 response,提取 item 以及跟进额外的 URL 的类。每个 spider 负责处理一个特定 (或一些) 网站。
  • Item Pipeline:Item Pipeline 负责处理被 spider 提取出来的 item。典型的处理有清理、验证数据及持久化(例如存取到数据库中)。

如上图所示,scrapy-redis 在 scrapy 的架构上增加了 redis,基于 redis 的特性拓展了如下组件:

  • 调度器(Scheduler) 
    scrapy-redis 调度器通过 redis 的 set 不重复的特性,
    巧妙的实现了Duplication Filter去重(DupeFilter set存放爬取过的request)。Spider 新生成的 request,将 request 的指纹到 redis 的 DupeFilter set 检查是否重复,
    并将不重复的request push写入redis的request队列。调度器每次从 redis 的 request 队列里根据优先级 pop 出一个 request, 
    将此 request 发给 spider 处理。
  • Item Pipeline
    将 Spider 爬取到的 Item 给 scrapy-redis 的 Item Pipeline,
    将爬取到的 Item 存入 redis 的 items 队列。可以很方便的从 items 队列中提取 item,
    从而实现 items processes 集群

总结

scrapy-redis 巧妙的利用 redis 实现 request queue 和 items queue,利用 redis 的 set 实现 request 的去重,将 scrapy 从单台机器扩展多台机器,实现较大规模的爬虫集群

scrapy-redis 安装

文档: https://scrapy-redis.readthedocs.org.

  • 安装 scrapy-redis:pip install scrapy-redis

scrapy-redis 源码截图:

可以看到 scrapy-redis 的 spiders.py 模块,导入了 scrapy.spiders 的 Spider、CrawlSpider,然后重新写了两个类 RedisSpiders、RedisCrawlSpider,分别继承 Spider、CrawlSpider,所以如果要想从 redis 读取任务,需要把自己写的 spider 继承 RedisSpiders、RedisCrawlSpider,而不是 scrapy 的 Spider、CrawlSpider。。。

scrapy-redis 使用 项目案例( 抓取校花网图片 )

:http://www.521609.com/daxuexiaohua/

:https://www.51tietu.net/xiaohua/

( scrapy_redis.spiders 下有两个类 RedisSpider RedisCrawlSpider,能够使 spider 从 Redis 读取 start_urls,然后执行爬取,若爬取过程中返回更多的 request url,那么它会继续进行直至所有的 request 完成之后,再从 redis start_urls 中读取下一个 url,循环这个过程 )

创建 scrapy-redis 的工程目录

方法 1:命令行执行:scrapy startproject MyScrapyRedis,然后自己写的 spider 继承 RedisSpider 或者 RedisCrawlSpider ,设置对应的 redis_key ,即队列的在 redis 中的 key。注意:这个需要手动 在 setting.py 里面配置设置。( 参考配置:https://github.com/rmax/scrapy-redis

 方法 2:使用 scrapy-redis 的 example 来修改。先从 github ( https://github.com/rmax/scrapy-redis ) 上拿到 scrapy-redis 的 example,然后将里面的 example-project 目录移到指定的地址。

tree 查看项目目录

 修改 settings.py ( 参考配置:https://github.com/rmax/scrapy-redis

下面列举了修改后的配置文件中与 scrapy-redis 有关的部分,middleware、proxy 等内容在此就省略了。

# 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
#BOT_NAME = 'example'SPIDER_MODULES = ['example.spiders']
NEWSPIDER_MODULE = 'example.spiders'# USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)'# Obey robots.txt rules
ROBOTSTXT_OBEY = FalseDUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# The class used to detect and filter duplicate requests.
# The default (RFPDupeFilter) filters based on request fingerprint using
# the scrapy.utils.request.request_fingerprint function.
# In order to change the way duplicates are checked you could subclass RFPDupeFilter and
# override its request_fingerprint method. This method should accept scrapy Request object
# and return its fingerprint (a string).# By default, RFPDupeFilter only logs the first duplicate request.
# Setting DUPEFILTER_DEBUG to True will make it log all duplicate requests.
DUPEFILTER_DEBUG = True# 指定使用 scrapy-redis 的 Scheduler
SCHEDULER = "scrapy_redis.scheduler.Scheduler"# 在 redis 中保持 scrapy-redis 用到的各个队列,从而允许暂停和暂停后恢复
SCHEDULER_PERSIST = True# 指定排序爬取地址时使用的队列,默认是按照优先级排序
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"# 只在使用 SpiderQueue 或者 SpiderStack 是有效的参数,,指定爬虫关闭的最大空闲时间
SCHEDULER_IDLE_BEFORE_CLOSE = 10ITEM_PIPELINES = {'example.pipelines.ExamplePipeline': 300,'example.pipelines.MyRedisPipeline': 400,# '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# 指定redis的连接参数
# REDIS_PASS是我自己加上的redis连接密码,需要简单修改scrapy-redis的源代码以支持使用密码连接redis
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
# Custom redis client parameters (i.e.: socket timeout, etc.)
REDIS_PARAMS = {}
# REDIS_URL = 'redis://user:pass@hostname:9001'# Override the default request headers:
DEFAULT_REQUEST_HEADERS = {'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8','Accept-Language': 'zh-CN,zh;q=0.8','Connection': 'keep-alive','Accept-Encoding': 'gzip, deflate, sdch',
}

查看 pipeline.py。注意:RedisPipeline 往 redis 写 item 数据时进行了序列化( 可以查看 RedisPipeline 的 _process_item 方法即刻看到进行了序列化),为了看到原始数据的 item,这里自定义了一个 MyRedisPipeline,继承自 RedisPipeline,重写 _process_item 方法,不进行序列化,直接把数据写到 redis 里。

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: http://doc.scrapy.org/topics/item-pipeline.htmlimport json
from datetime import datetime
from scrapy_redis.pipelines import RedisPipelineclass ExamplePipeline(object):def process_item(self, item, spider):item["crawled"] = str(datetime.now().replace(microsecond=0))item["spider"] = spider.namereturn itemclass MyRedisPipeline(RedisPipeline):def _process_item(self, item, spider):key = self.item_key(item, spider)# data = self.serialize(item)self.server.rpush(key, json.dumps(item, ensure_ascii=False))return item

也可以不用重写,通过在 setting.py 里面配置 REDIS_ITEMS_SERIALIZER = 'json.dumps' 即可使用 json 序列化 # scrapy-redis 默认使用 ScrapyJSONEncoder 进行项目序列化 #You can use any importable path to a callable object. #REDIS_ITEMS_SERIALIZER = 'json.dumps',通过查看 scrapy-redis 的 pipelines.py 

参考:https://www.cnblogs.com/Alexephor/p/11446167.html

修改 items.py,增加我们最后要保存的 Profile 项

class Profile(Item):# 提取头像地址header_url = Field()# 提取相册图片地址pic_urls = Field()username = Field()# 提取内心独白monologue = Field()age = Field()# youyuansource = Field()source_url = Field()crawled = Field()spider = Field()

RedisSpider 示例

以 example 下 mycrawler_redis.py 举例

运行:scrapy runspider example/spiders/myspider_redis.py

push urls to redis:redis-cli lpush myspider:start_urls http://baidu.com

RedisCrawlSpider 示例

首先添加任务,push urls to redis:( add_task.py ):

示例代码

import json
from scrapy.utils.project import get_project_settings
from scrapy_redis.connection import get_redis_from_settings
from scrapy_redis import connection
from scrapy_redis.queue import PriorityQueue# def _encode_request(self, request):
#     """Encode a request object"""
#     obj = request_to_dict(request, self.spider)
#     return self.serializer.dumps(obj)
#
#
# def _decode_request(self, encoded_request):
#     """Decode an request previously encoded"""
#     obj = self.serializer.loads(encoded_request)
#     return request_from_dict(obj, self.spider)def add_task_to_redis():redis_key = 'start_urls:yy_spider_request'url_string = 'http://www.youyuan.com/find/beijing/mm18-25/advance-0-0-0-0-0-0-0/p1/'# 方法 1server = get_redis_from_settings(get_project_settings())server.lpush(redis_key, url_string)# server.zadd(redis_key, url_string, 1000)# 方法 2# server = connection.from_settings(get_project_settings())# server.execute_command('ZADD', redis_key, 1000, url_string)if __name__ == '__main__':# temp = 'test json string'# print(json.dumps(temp))add_task_to_redis()pass

添加完任务,可以看到 redis 里面 的 start_urls:yy_spider_request 已经有添加的任务

添加任务到 Redis 有序集合(sorted set)

思考:

  • 现在添加的任务是到 redis 的 list 里面,怎么添加任务到 redis 的 有序集合中 ???
  • 怎么实现队列中添加的任务是 json 格式的字符串 ???
  • 使用布隆去重代替 scrapy_redis (分布式爬虫)自带的 dupefilter ???

防禁封策略 --- 分布式实战

丁香园用药助手( http://drugs.dxy.cn/ ) 项目为例。架构示意图如下:

首先通过药理分类采集一遍,按照drug_id排序,发现:

我们要完成 http://drugs.dxy.cn/drug/[50000-150000].htm

正常采集:

异常数据情况包括如下:

  • 药品不存在

  • 当采集频率过快,弹出验证码

  • 当天采集累计操作次数过多,弹出禁止

这个时候就需要用到代理

项目流程

1. 创建项目

scrapy startproject drugs_dxy# 创建 spider
cd drugs_dxy/
scrapy genspider -t basic Drugs dxy.cn

2. items.py 下添加类 DrugsItem

class DrugsItem(scrapy.Item):# define the fields for your item here like:#药品不存在标记exists = scrapy.Field()#药品iddrugtId = scrapy.Field()#数据data = scrapy.Field()#标记验证码状态msg = scrapy.Field()pass

3. 编辑 spider 下 DrugsSpider 类

# -*- coding: utf-8 -*-# from drugs_dxy.items import DrugsItem
import re
import scrapy
from scrapy.spiders import Spiderclass DrugsSpider(Spider):name = "Drugs"allowed_domains = ["dxy.cn"]size = 60def __init__(self):super(DrugsSpider, self).__init__()self.temp = Nonedef start_requests(self):for i in range(50000, 50000 + self.size, 1):url = f'http://drugs.dxy.cn/drug/{i}.htm'yield scrapy.Request(url=url, callback=self.parse)def parse(self, response, **kwargs):self.temp = None# drug_Item = DrugsItem()drug_item = dict()drug_item["drugId"] = int(re.search(r'(\d+)', response.url).group(1))if drug_item["drugId"] >= 150000:returnurl = f'http://drugs.dxy.cn/drug/{drug_item["drugId"] + self.size}.htm'yield scrapy.Request(url=url, callback=self.parse)if '药品不存在' in response.body:drug_item['exists'] = Falseyield drug_itemreturnif '请填写验证码继续正常访问' in response.body:drug_item["msg"] = '请填写验证码继续正常访问'returndrug_item["data"] = {}details = response.xpath("//dt")for detail in details:detail_name = detail.xpath('./span/text()').extract()[0].split(':')[0]if detail_name == u'药品名称':drug_item['data'][u'药品名称'] = {}try:detail_str = detail.xpath("./following-sibling::*[1]")detail_value = detail_str.xpath('string(.)').extract()[0]detail_value = detail_value.replace('\r', '').replace('\t', '').strip()for item in detail_value.split('\n'):item = item.replace('\r', '').replace('\n', '').replace('\t', '').strip()name = item.split(u':')[0]value = item.split(u':')[1]drug_item['data'][u'药品名称'][name] = valueexcept BaseException as ex:passelse:detail_str = detail.xpath("./following-sibling::*[1]")detail_value = detail_str.xpath('string(.)').extract()[0]detail_value = detail_value.replace('\r', '').replace('\t', '').strip()# print detail_str,detail_valuedrug_item['data'][detail_name] = detail_valueyield drug_itemif __name__ == '__main__':from scrapy import cmdlinecmdline.execute('scrapy crawl Drugs'.split())pass

4. Scrapy代理设置

4.1 在 settings.py 文件里

1)启用 scrapy_redis 组件

# Enables scheduling storing requests queue in redis.
SCHEDULER = "scrapy_redis.scheduler.Scheduler"# Ensure all spiders share same duplicates filter through redis.
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 300
}# Specify the host and port to use when connecting to Redis (optional).
REDIS_HOST = '101.200.170.171'
REDIS_PORT = 6379# Custom redis client parameters (i.e.: socket timeout, etc.)
REDIS_PARAMS  = {}
#REDIS_URL = 'redis://user:pass@hostname:9001'
REDIS_PARAMS['password'] = 'redis_password'

2) 启用 DownLoader 中间件;httpproxy

# Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {'drugs_dxy.middlewares.ProxyMiddleware': 400,'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None,
}

3) 设置禁止跳转(code=301、302),超时时间90s

DOWNLOAD_TIMEOUT = 90
REDIRECT_ENABLED = False

4.2 在 drugs_dxy 目录下创建 middlewares.py 并编辑 (settings.py 同级目录)

# -*- coding: utf-8 -*-
import random
import base64
import Queue
import redisclass ProxyMiddleware(object):def __init__(self, settings):self.queue = 'Proxy:queue'# 初始化代理列表self.r = redis.Redis(host=settings.get('REDIS_HOST'),port=settings.get('REDIS_PORT'),db=1,password=settings.get('REDIS_PARAMS')['password'])@classmethoddef from_crawler(cls, crawler):return cls(crawler.settings)def process_request(self, request, spider):proxy={}source, data = self.r.blpop(self.queue)proxy['ip_port']=dataproxy['user_pass']=Noneif proxy['user_pass'] is not None:#request.meta['proxy'] = "http://YOUR_PROXY_IP:PORT"request.meta['proxy'] = "http://%s" % proxy['ip_port']#proxy_user_pass = "USERNAME:PASSWORD"encoded_user_pass = base64.encodestring(proxy['user_pass'])request.headers['Proxy-Authorization'] = 'Basic ' + encoded_user_passprint "********ProxyMiddleware have pass*****" + proxy['ip_port']else:#ProxyMiddleware no passprint request.url, proxy['ip_port']request.meta['proxy'] = "http://%s" % proxy['ip_port']def process_response(self, request, response, spider):"""检查response.status, 根据status是否在允许的状态码中决定是否切换到下一个proxy, 或者禁用proxy"""print("-------%s %s %s------" % (request.meta["proxy"], response.status, request.url))# status不是正常的200而且不在spider声明的正常爬取过程中可能出现的# status列表中, 则认为代理无效, 切换代理if response.status == 200:print 'rpush',request.meta["proxy"]self.r.rpush(self.queue, request.meta["proxy"].replace('http://','')) return responsedef process_exception(self, request, exception, spider):"""处理由于使用代理导致的连接异常"""proxy={}source, data = self.r.blpop(self.queue)proxy['ip_port']=dataproxy['user_pass']=Nonerequest.meta['proxy'] = "http://%s" % proxy['ip_port']new_request = request.copy()new_request.dont_filter = Truereturn new_request

redis-scrapy

settings.py 千万不能添加:LOG_STDOUT=True

2、scrapy-redis-cluster ( 集群版_1 )

scrapy_redis_cluster ( 已经不在维护 ):https://github.com/thsheep/scrapy_redis_cluster
scrapy-redis-cluster :https://pypi.org/project/scrapy-redis-cluster

scrapy-redis-cluster 已经不在维护 !!!!!

scrapyd-redis 的集群版

  • 此包Python名称:scrapy-redis-cluster
  • 目前版本: scrapy-redis-cluster 0.4
  • 最后维护时间:Jul 5, 2018
  • 摘要:scrapyd-redis的集群版
  • 安装命令:pip install scrapy-redis-cluster
  • 其它:scrapy-redis-cluster 这个Python第三方库的作者没有提供更多的项目描述信息了,2019-11-10 23:44:14。

scrapy-redis 使用 redis 集群进行分布式爬取

正常情况单机的redis可以满足scrapy-redis进行分布式爬取,可是如果单机的redis的内存过小,很容易导致系统内存不够,读取数据缓慢,如果使用docker运行redis,更加可能导致redis的容器的进程被杀掉。(笔者就曾经经常遇到这种情况,机器内存才8GB,上面跑了N个docker容器,一旦内存吃紧,某个容器就被kill掉,导致爬虫经常出问题)。
 
使用redis集群可以增加redis集体内存,防止出现上面的情况。
 
scrapy redis-cluster 很简单,只需要按照以下步骤:

1. 安装库:pip install scrapy-redis-cluster

2. 修改 settings 文件

# Redis集群地址
REDIS_MASTER_NODES = [{"host": "192.168.10.233", "port": "30001"},{"host": "192.168.10.234", "port": "30002"},{"host": "192.168.10.235", "port": "30003"},
]# 使用的哈希函数数,默认为6  
BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter使用的Redis内存位,30表示2 ^ 30 = 128MB,默认为22 (1MB 可去重130W URL)  
BLOOMFILTER_BIT = 22# 不清空redis队列  
SCHEDULER_PERSIST = True  
# 调度队列  
SCHEDULER = "scrapy_redis_cluster.scheduler.Scheduler"  
# 去重 
DUPEFILTER_CLASS = "scrapy_redis_cluster.dupefilter.RFPDupeFilter"  
# queue  
SCHEDULER_QUEUE_CLASS = 'scrapy_redis_cluster.queue.PriorityQueue'

3、scrapy-redis-sentinel( 集群版_2 )

scrapy-redis-sentinel :https://github.com/crawlaio/scrapy-redis-sentinel

pypi 地址:https://pypi.org/project/scrapy-redis-sentinel/

基于原项目 scrpy-redis:https://github.com/rmax/scrapy-redis

进行修改,修改内容如下:

  1. 添加了 Redis 哨兵连接支持
  2. 添加了 Redis 集群连接支持
  3. 添加了 Bloomfilter 去重

安装第三方库:pip install scrapy-redis-sentinel

原版本 scrpy-redis 的所有配置都支持。优先级:哨兵模式 > 集群模式 > 单机模式

配置示例

# ----------------------------------------Bloomfilter 配置-------------------------------------
# 使用的哈希函数数,默认为 6
BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter 使用的 Redis 内存位,30 表示 2 ^ 30 = 128MB,默认为 30   (2 ^ 22 = 1MB 可去重 130W URL)
BLOOMFILTER_BIT = 30# 是否开启去重调试模式 默认为 False 关闭
DUPEFILTER_DEBUG = False# ----------------------------------------Redis 单机模式-------------------------------------
# Redis 单机地址
REDIS_HOST = "172.25.2.25"
REDIS_PORT = 6379# REDIS 单机模式配置参数
REDIS_PARAMS = {"password": "password","db": 0
}# ----------------------------------------Redis 哨兵模式-------------------------------------# Redis 哨兵地址
REDIS_SENTINELS = [('172.25.2.25', 26379),('172.25.2.26', 26379),('172.25.2.27', 26379)
]# REDIS_SENTINEL_PARAMS 哨兵模式配置参数。
REDIS_SENTINEL_PARAMS= {"service_name":"mymaster","password": "password","db": 0
}# ----------------------------------------Redis 集群模式-------------------------------------# Redis 集群地址
REDIS_STARTUP_NODES = [{"host": "172.25.2.25", "port": "6379"},{"host": "172.25.2.26", "port": "6379"},{"host": "172.25.2.27", "port": "6379"},
]# REDIS_CLUSTER_PARAMS 集群模式配置参数
REDIS_CLUSTER_PARAMS= {"password": "password"
}# ----------------------------------------Scrapy 其他参数-------------------------------------# 在 redis 中保持 scrapy-redis 用到的各个队列,从而允许暂停和暂停后恢复,也就是不清理 redis queues
SCHEDULER_PERSIST = True  
# 调度队列  
SCHEDULER = "scrapy_redis_sentinel.scheduler.Scheduler"  
# 去重 
DUPEFILTER_CLASS = "scrapy_redis_sentinel.dupefilter.RFPDupeFilter"  # 指定排序爬取地址时使用的队列
# 默认的 按优先级排序( Scrapy 默认),由 sorted set 实现的一种非 FIFO、LIFO 方式。
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderPriorityQueue'
# 可选的 按先进先出排序(FIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack'
# 可选的 按后进先出排序(LIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack'

注:当使用集群时单机不生效

spiders 使用

原版本 scrpy-redis 使用方式

from scrapy_redis.spiders import RedisSpiderclass Spider(RedisSpider):...

修改 RedisSpider 引入方式后,scrapy-redis-sentinel 的使用方式

from scrapy_redis_sentinel.spiders import RedisSpiderclass Spider(RedisSpider):...

使用示例:

修改 setting.py文件

ITEM_PIPELINES = { 'scrapy_redis_sentinel.pipelines.RedisPipeline': 543,
}# Bloomfilter 配置
# 使用的哈希函数数,默认为 6
BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter 使用的 Redis 内存位,30 表示 2 ^ 30 = 128MB,默认为 30   (2 ^ 22 = 1MB 可去重 130W URL)
BLOOMFILTER_BIT = 30# 是否开启去重调试模式 默认为 False 关闭
DUPEFILTER_DEBUG = False# Redis 集群地址
REDIS_MASTER_NODES = [{"host": "192.168.56.30", "port": "9000"},{"host": "192.168.56.31", "port": "9000"},{"host": "192.168.56.32", "port": "9000"},
]# REDIS_CLUSTER_PARAMS 集群模式配置参数
REDIS_CLUSTER_PARAMS= {# "password": "password"
}# scrapy其他参数
# 在 redis 中保持 scrapy-redis 用到的各个队列,从而允许暂停和暂停后恢复,也就是不清理 redis queues
SCHEDULER_PERSIST = True
# 调度队列
SCHEDULER = "scrapy_redis_sentinel.scheduler.Scheduler"
# 去重
DUPEFILTER_CLASS = "scrapy_redis_sentinel.dupefilter.RFPDupeFilter"# 指定排序爬取地址时使用的队列
# 默认的 按优先级排序( Scrapy 默认),由 sorted set 实现的一种非 FIFO、LIFO 方式。
SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderPriorityQueue'
# 可选的 按先进先出排序(FIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack'
# 可选的 按后进先出排序(LIFO)
# SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack'

修改 spider

from scrapy_redis_sentinel.spiders import RedisSpiderclass scrapy_spider(RedisSpider):......

Redis 集群( Redis5.0.7集群搭建:https://blog.csdn.net/pcengineercn/article/details/104502061 )

经过调试,修复了一个bug使用默认爬取队列时会报错,需要将源码中的 PriorityQueue(位于 Python 安装目录 /lib/python3.6/site-packages/scrapy_redis_sentinel/queue.py)替换为如下

class PriorityQueue(Base):"""Per-spider priority queue abstraction using redis' sorted set"""def __len__(self):"""Return the length of the queue"""return self.server.zcard(self.key)def push(self, request):"""Push a request"""data = self._encode_request(request)score = -request.priority# We don't use zadd method as the order of arguments change depending on# whether the class is Redis or StrictRedis, and the option of using# kwargs only accepts strings, not bytes.self.server.execute_command("ZADD", self.key, score, data)def pop(self, timeout=0):"""Pop a requesttimeout not support in this queue class"""if not isinstance(self.server, RedisCluster):# use atomic range/remove using multi/execpipe = self.server.pipeline()pipe.multi()pipe.zrange(self.key, 0, 0).zremrangebyrank(self.key, 0, 0)results, count = pipe.execute()if results:return self._decode_request(results[0])# 使用集群的时候不能使用 multi/exec 来完成一个事务操作;使用lua脚本来实现类似功能pop_lua_script = """local result = redis.call('zrange', KEYS[1], 0, 0)local element = result[1]if element thenredis.call('zremrangebyrank', KEYS[1], 0, 0)return elementelsereturn nilend"""script = self.server.register_script(pop_lua_script)results = script(keys=[self.key])if results:return self._decode_request(results)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/495287.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

爬虫教程( 5 ) --- Selenium 与 PhantomJS

1. Selenium 中式读法:【 瑟林捏幕 】 Selenium( selenium 中文网:http://www.selenium.org.cn/ )是一个强大的网络数据采集工具,最初是为了网站自动化测试而开发的,被用来测试 Web 应用程序在不同的浏览器…

详解深度学习的可解释性研究(上篇)

作者 | 王小贱来源 | BIGSCity知乎专栏摘要:《深度学习的可解释性研究》系列文章希望能用尽可能浅显的语言带领大家了解可解释性的概念与方法,以及关于深度学习可解释性工作的研究成果。本文是该系列的第一部分。01深度学习的可解释性研究(一…

Office 安装

Office Tool Plus 官网:https://otp.landian.vip/zh-cn/ :https://zhuanlan.zhihu.com/p/486882686 Office Tool Plus 基于 Office 部署工具 (ODT) 打造,可以很轻松地部署 Office。无论你是个体还是团队,Office Tool Plus 都是您…

前瞻性总结:全球未来十年不可不知的10大趋势

来源:混沌大学摘要:无论你身在何处,真正的大趋势必将把地球上每一个人深卷其中,并重构大至国家、城市,小至企业、消费者之间一系列错综复杂的关系。不管你是处于创业模式,还是在大企业里面工作,…

爬虫教程( 6 ) --- 爬虫 进阶、扩展

1. 前言 1. 先看一个最简单的爬虫。 import requestsurl "http://www.cricode.com" r requests.get(url) print(r.text)2. 一个正常的爬虫程序 上面那个最简单的爬虫,是一个不完整的残疾的爬虫。因为爬虫程序通常需要做的事情如下: 1&…

阿里商业白皮书:每个企业都要变成一个数据公司

来源:悟空智能科技摘要:通过近百页的阐述,该报告全面而详实地介绍了阿里巴巴云零售服务生态系统的最新进展情况。通过近百页的阐述,该报告全面而详实地介绍了阿里巴巴云零售服务生态系统的最新进展情况。尤其是,通过数…

NLP通用模型诞生?一个模型搞定十大自然语言常见任务

翻译 | 于之涵编辑 | Leo出品 | AI科技大本营 (公众号ID:rgznai100)然而近日,Salesforce发布了一项新的研究成果:decaNLP——一个可以同时处理机器翻译、问答、摘要、文本分类、情感分析等十项自然语言任务的通用模型。…

爬虫教程( 3 ) --- 手机 APP 数据抓取

1. Fiddler 设置 这是使用 fiddler 进行手机 app 的抓包,也可以使用 Charles,burpSuite 等。。。 电脑安装 Fiddler,手机 和 安装 fiddler 的电脑处于同一个网络里, 否则手机不能把 HTTP 发送到 Fiddler 的机器上来。配置 Fiddle…

从互联网大脑模型看腾讯与今日头条之争

作者:刘锋 互联网进化论作者 计算机博士前言:通过互联网的大脑模型,分析腾讯与今日头条出现激烈竞争的背后原因,同时得出结论,从未来产业发展趋势看,这种竞争对于两家有可能是好的状况而非坏事。2018年以来…

爬虫教程( 1 ) --- 初级、基础、实践

爬虫教程:https://piaosanlang.gitbooks.io/spiders/content/ 如何入门 Python 爬虫:https://zhuanlan.zhihu.com/p/21479334 静觅 崔庆才的个人博客 Python 爬虫系列:http://cuiqingcai.com/category/technique/python http://www.cnblog…

概念炒作的背后,“智能合约”的真相是什么?

来源:36Kr就像“区块链”,“AI”和“云”一样,“智能合约”也是如今收到热捧的新概念。试想一下,有什么能比相确信合约在未来会按照约定被执行而不需要任何司法介入更好? 智能合约的承诺包括:合约可以被自动…

Dota 2被攻陷!OpenAI 人工智能5V5模式击败人类玩家(4000分水平)

来源:机器之心摘要:我们团队构建的模型,OpenAI Five,已经击败业余 Dota2 团队了。虽然如今是在有限制的情况下,但我们计划到 8 月份在有限英雄池下击败 TI 赛中的一支顶级专业队伍。我们团队构建的模型,Ope…

使用 mitmproxy + python 做拦截代理

From:https://blog.wolfogre.com/posts/usage-of-mitmproxy https://www.cnblogs.com/H4ck3R-XiX/p/12624072.html http://www.cnblogs.com/grandlulu/p/9525417.html mitmProxy 介绍:https://blog.csdn.net/h416756139/article/details…

ZeroMQ全面介绍

★ZMQ是啥玩意儿?   通俗地说,ZMQ是一个开源的、跨语言的、非常简洁的、非常高性能、非常灵活的网络通讯库。它的官方网站在"这里",维基百科的介绍在"这里"(暂时没有中文的维基词条)。这玩意儿推…

Android IDA 动态调试最完善攻略,跨过各种坑

From:https://www.pianshen.com/article/3409449384/ IDA 静态分析 与 动态分析:https://zhuanlan.zhihu.com/p/38983223 新手向总结:IDA 动态调试 So 的一些坑:https://zhuanlan.zhihu.com/p/145383282 Android 逆向之旅 --- …

一场科技盛宴,一次“盈”满天下 安创成长营五期Demo Day完美收官

2018年6月21日,安创成长营第五期Demo Day在北京金隅喜来登拉开帷幕,16家安创成长营五期成员代表齐齐亮相,为资本圈带来了一场集科技、创新、前瞻于一体的项目展示。数百数位业内有影响力的Arm生态系统的合作伙伴、知名品牌厂商创新事业部负责…

安卓逆向_25 --- 密码学 之 《Java加密与解密的艺术》

《Java加密与解密的艺术》中文 PDF版 :https://www.jb51.net/books/65048.html 1. 密码学应用 :https://www.bilibili.com/video/BV1oA411J7Lb?p1 密码学应用 密码学的基本概念,对称密码,公钥密码,Hash算法&#xff…

深度丨一文读懂智能制造的主线——智能生产(工厂/车间数字化)

来源: 亿欧摘要: 发展智能制造对于中国制造业乃至中国经济的重要性不言而喻,先进制造业作为深耕制造业领域的专业媒体,长期关注智能制造发展,助力中国制造转型升级,努力做“中国制造2025”的推动者。近年来…

最好用的十六进制编辑器 010 Editor

最好用的十六进制编辑器010 Editor https://blog.csdn.net/qq_38482805/article/details/89309120 新版010Edit注册机&去除网络验证:https://bbs.pediy.com/thread-249724.htm 010Editor脚本语法入门:https://www.jianshu.com/p/ba60ebd8f916 宇宙…

Object to XML

摘要:本节主要介绍如何把Object对象转换为XML 引言:最近工作中因为工作需要,需要把两个系统之间的传递的报文修改一下(现在系统之间一般都是通过发送xml字符串传数据吧),最开始用的Jdom来实现的&#xff0c…