日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2024/7/23 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 爬虫教程( 4 ) --- 分布式爬虫 scrapy-redis、集群 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1、分布式爬蟲?scrapy - redis

scrapy 分布式爬蟲

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

Scrapy 并沒有提供內(nèi)置的機(jī)制支持分布式(多服務(wù)器)爬取。不過還是有辦法進(jìn)行分布式爬取, 取決于您要怎么分布了。

如果您有很多spider,那分布負(fù)載最簡單的辦法就是啟動(dòng)多個(gè)Scrapyd,并分配到不同機(jī)器上。

如果想要在多個(gè)機(jī)器上運(yùn)行一個(gè)單獨(dú)的spider,那您可以將要爬取的 url 進(jìn)行分塊,并發(fā)送給spider。 例如:

首先,準(zhǔn)備要爬取的 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個(gè)不同的 Scrapd 服務(wù)器中啟動(dòng) spider。spider 會(huì)接收一個(gè)(spider)參數(shù) part , 該參數(shù)表示要爬取的分塊:

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 隊(duì)列實(shí)現(xiàn) request queue 和 items queue,利用 redis 的 set 實(shí)現(xiàn) request 的去重,將 scrapy 從單臺(tái)機(jī)器擴(kuò)展多臺(tái)機(jī)器,實(shí)現(xiàn)較大規(guī)模的爬蟲集群

Scrapy-Redis 架構(gòu)分析

scrapy 任務(wù)調(diào)度是基于文件系統(tǒng),這樣只能在單機(jī)執(zhí)行 crawl。

scrapy-redis 將待抓取 request 請(qǐng)求信息數(shù)據(jù) items 信息 的存取放到 redis queue 里,使多臺(tái)服務(wù)器可以同時(shí)執(zhí)行 crawl 和 items process,大大提升了數(shù)據(jù)爬取和處理的效率。

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

  • 分布式爬蟲。多個(gè)爬蟲實(shí)例分享一個(gè) redis request 隊(duì)列,非常適合大范圍多域名的爬蟲集群
  • 分布式后處理。爬蟲抓取到的 items push 到一個(gè) redis items 隊(duì)列,這就意味著可以開啟多個(gè) items processes 來處理抓取到的數(shù)據(jù),比如存儲(chǔ)到 Mongodb、Mysql
  • 基于 scrapy 即插即用組件。Scheduler + Duplication Filter、Item Pipeline、?Base Spiders

scrapy 原生架構(gòu)

分析 scrapy-redis 的架構(gòu)之前先回顧一下 scrapy 的架構(gòu)

  • 調(diào)度器(Scheduler):調(diào)度器維護(hù) request 隊(duì)列,每次執(zhí)行取出一個(gè) request。
  • Spiders:Spider 是 Scrapy 用戶編寫用于分析 response,提取 item 以及跟進(jìn)額外的 URL 的類。每個(gè) spider 負(fù)責(zé)處理一個(gè)特定 (或一些) 網(wǎng)站。
  • Item Pipeline:Item Pipeline 負(fù)責(zé)處理被 spider 提取出來的 item。典型的處理有清理、驗(yàn)證數(shù)據(jù)及持久化(例如存取到數(shù)據(jù)庫中)。

如上圖所示,scrapy-redis 在 scrapy 的架構(gòu)上增加了 redis,基于 redis 的特性拓展了如下組件:

  • 調(diào)度器(Scheduler)? scrapy-redis 調(diào)度器通過 redis 的 set 不重復(fù)的特性, 巧妙的實(shí)現(xiàn)了Duplication Filter去重(DupeFilter set存放爬取過的request)。Spider 新生成的 request,將 request 的指紋到 redis 的 DupeFilter set 檢查是否重復(fù), 并將不重復(fù)的request push寫入redis的request隊(duì)列。調(diào)度器每次從 redis 的 request 隊(duì)列里根據(jù)優(yōu)先級(jí) pop 出一個(gè) request, 將此 request 發(fā)給 spider 處理。
  • Item Pipeline 將 Spider 爬取到的 Item 給 scrapy-redis 的 Item Pipeline, 將爬取到的 Item 存入 redis 的 items 隊(duì)列。可以很方便的從 items 隊(duì)列中提取 item, 從而實(shí)現(xiàn) items processes 集群

總結(jié)

scrapy-redis 巧妙的利用 redis 實(shí)現(xiàn) request queue 和 items queue,利用 redis 的 set 實(shí)現(xiàn) request 的去重,將 scrapy 從單臺(tái)機(jī)器擴(kuò)展多臺(tái)機(jī)器,實(shí)現(xiàn)較大規(guī)模的爬蟲集群

scrapy-redis 安裝

文檔:?https://scrapy-redis.readthedocs.org.

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

scrapy-redis 源碼截圖:

可以看到 scrapy-redis 的 spiders.py 模塊,導(dǎo)入了 scrapy.spiders 的 Spider、CrawlSpider,然后重新寫了兩個(gè)類 RedisSpiders、RedisCrawlSpider,分別繼承?Spider、CrawlSpider,所以如果要想從 redis 讀取任務(wù),需要把自己寫的 spider 繼承?RedisSpiders、RedisCrawlSpider,而不是 scrapy 的?Spider、CrawlSpider。。。

scrapy-redis 使用 項(xiàng)目案例( 抓取校花網(wǎng)圖片?)

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

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

(?scrapy_redis.spiders?下有兩個(gè)類 RedisSpider RedisCrawlSpider,能夠使 spider 從 Redis 讀取 start_urls,然后執(zhí)行爬取,若爬取過程中返回更多的 request url,那么它會(huì)繼續(xù)進(jìn)行直至所有的 request 完成之后,再從 redis start_urls 中讀取下一個(gè) url,循環(huán)這個(gè)過程 )

創(chuàng)建 scrapy-redis 的工程目錄

方法 1:命令行執(zhí)行:scrapy startproject MyScrapyRedis,然后自己寫的 spider 繼承?RedisSpider 或者 RedisCrawlSpider ,設(shè)置對(duì)應(yīng)的?redis_key ,即隊(duì)列的在 redis 中的 key。注意:這個(gè)需要手動(dòng) 在 setting.py 里面配置設(shè)置。(?參考配置:https://github.com/rmax/scrapy-redis

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

tree 查看項(xiàng)目目錄

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

下面列舉了修改后的配置文件中與 scrapy-redis 有關(guān)的部分,middleware、proxy 等內(nèi)容在此就省略了。

# 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 用到的各個(gè)隊(duì)列,從而允許暫停和暫停后恢復(fù) SCHEDULER_PERSIST = True# 指定排序爬取地址時(shí)使用的隊(duì)列,默認(rèn)是按照優(yōu)先級(jí)排序 SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue" # SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue" # SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"# 只在使用 SpiderQueue 或者 SpiderStack 是有效的參數(shù),,指定爬蟲關(guān)閉的最大空閑時(shí)間 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的連接參數(shù) # 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 數(shù)據(jù)時(shí)進(jìn)行了序列化( 可以查看?RedisPipeline 的?_process_item 方法即刻看到進(jìn)行了序列化),為了看到原始數(shù)據(jù)的 item,這里自定義了一個(gè)?MyRedisPipeline,繼承自?RedisPipeline,重寫?_process_item 方法,不進(jìn)行序列化,直接把數(shù)據(jù)寫到 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 默認(rèn)使用 ScrapyJSONEncoder 進(jìn)行項(xiàng)目序列化 #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 項(xiàng)

class Profile(Item):# 提取頭像地址header_url = Field()# 提取相冊(cè)圖片地址pic_urls = Field()username = Field()# 提取內(nèi)心獨(dú)白monologue = Field()age = Field()# youyuansource = Field()source_url = Field()crawled = Field()spider = Field()

RedisSpider 示例

以 example 下 mycrawler_redis.py 舉例

運(yùn)行:scrapy runspider example/spiders/myspider_redis.py

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

RedisCrawlSpider 示例

首先添加任務(wù),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

添加完任務(wù),可以看到 redis 里面 的?start_urls:yy_spider_request 已經(jīng)有添加的任務(wù)

添加任務(wù)到 Redis 有序集合(sorted set)

思考:

  • 現(xiàn)在添加的任務(wù)是到 redis 的 list 里面,怎么添加任務(wù)到 redis 的 有序集合中 ???
  • 怎么實(shí)現(xiàn)隊(duì)列中添加的任務(wù)是 json 格式的字符串 ???
  • 使用布隆去重代替 scrapy_redis (分布式爬蟲)自帶的 dupefilter ???

防禁封策略 --- 分布式實(shí)戰(zhàn)

丁香園用藥助手(?http://drugs.dxy.cn/ )?項(xiàng)目為例。架構(gòu)示意圖如下:

首先通過藥理分類采集一遍,按照drug_id排序,發(fā)現(xiàn):

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

正常采集:

異常數(shù)據(jù)情況包括如下:

  • 藥品不存在

  • 當(dāng)采集頻率過快,彈出驗(yàn)證碼

  • 當(dāng)天采集累計(jì)操作次數(shù)過多,彈出禁止

這個(gè)時(shí)候就需要用到代理

項(xiàng)目流程

1. 創(chuàng)建項(xiàng)目

scrapy startproject drugs_dxy# 創(chuàng)建 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:#藥品不存在標(biāo)記exists = scrapy.Field()#藥品iddrugtId = scrapy.Field()#數(shù)據(jù)data = scrapy.Field()#標(biāo)記驗(yàn)證碼狀態(tài)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 '請(qǐng)?zhí)顚戲?yàn)證碼繼續(xù)正常訪問' in response.body:drug_item["msg"] = '請(qǐng)?zhí)顚戲?yàn)證碼繼續(xù)正常訪問'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代理設(shè)置

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) 設(shè)置禁止跳轉(zhuǎn)(code=301、302),超時(shí)時(shí)間90s

DOWNLOAD_TIMEOUT = 90 REDIRECT_ENABLED = False

4.2 在 drugs_dxy 目錄下創(chuàng)建 middlewares.py 并編輯 (settings.py 同級(jí)目錄)

# -*- 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, 根據(jù)status是否在允許的狀態(tài)碼中決定是否切換到下一個(gè)proxy, 或者禁用proxy"""print("-------%s %s %s------" % (request.meta["proxy"], response.status, request.url))# status不是正常的200而且不在spider聲明的正常爬取過程中可能出現(xiàn)的# status列表中, 則認(rèn)為代理無效, 切換代理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):"""處理由于使用代理導(dǎo)致的連接異常"""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 (?已經(jīng)不在維護(hù) ):https://github.com/thsheep/scrapy_redis_cluster
scrapy-redis-cluster :https://pypi.org/project/scrapy-redis-cluster

scrapy-redis-cluster 已經(jīng)不在維護(hù) !!!!!

scrapyd-redis 的集群版

  • 此包Python名稱:scrapy-redis-cluster
  • 目前版本:?scrapy-redis-cluster 0.4
  • 最后維護(hù)時(shí)間:Jul 5, 2018
  • 摘要:scrapyd-redis的集群版
  • 安裝命令:pip install scrapy-redis-cluster
  • 其它:scrapy-redis-cluster?這個(gè)Python第三方庫的作者沒有提供更多的項(xiàng)目描述信息了,2019-11-10 23:44:14。

scrapy-redis 使用 redis 集群進(jìn)行分布式爬取

正常情況單機(jī)的redis可以滿足scrapy-redis進(jìn)行分布式爬取,可是如果單機(jī)的redis的內(nèi)存過小,很容易導(dǎo)致系統(tǒng)內(nèi)存不夠,讀取數(shù)據(jù)緩慢,如果使用docker運(yùn)行redis,更加可能導(dǎo)致redis的容器的進(jìn)程被殺掉。(筆者就曾經(jīng)經(jīng)常遇到這種情況,機(jī)器內(nèi)存才8GB,上面跑了N個(gè)docker容器,一旦內(nèi)存吃緊,某個(gè)容器就被kill掉,導(dǎo)致爬蟲經(jīng)常出問題)。
?
使用redis集群可以增加redis集體內(nèi)存,防止出現(xiàn)上面的情況。
?
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"}, ]# 使用的哈希函數(shù)數(shù),默認(rèn)為6 BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter使用的Redis內(nèi)存位,30表示2 ^ 30 = 128MB,默認(rèn)為22 (1MB 可去重130W URL) BLOOMFILTER_BIT = 22# 不清空redis隊(duì)列 SCHEDULER_PERSIST = True # 調(diào)度隊(duì)列 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/

基于原項(xiàng)目 scrpy-redis:https://github.com/rmax/scrapy-redis

進(jìn)行修改,修改內(nèi)容如下:

  • 添加了?Redis?哨兵連接支持
  • 添加了?Redis?集群連接支持
  • 添加了?Bloomfilter?去重
  • 安裝第三方庫:pip install scrapy-redis-sentinel

    原版本 scrpy-redis 的所有配置都支持。優(yōu)先級(jí):哨兵模式 > 集群模式 > 單機(jī)模式

    配置示例

    # ----------------------------------------Bloomfilter 配置------------------------------------- # 使用的哈希函數(shù)數(shù),默認(rèn)為 6 BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter 使用的 Redis 內(nèi)存位,30 表示 2 ^ 30 = 128MB,默認(rèn)為 30 (2 ^ 22 = 1MB 可去重 130W URL) BLOOMFILTER_BIT = 30# 是否開啟去重調(diào)試模式 默認(rèn)為 False 關(guān)閉 DUPEFILTER_DEBUG = False# ----------------------------------------Redis 單機(jī)模式------------------------------------- # Redis 單機(jī)地址 REDIS_HOST = "172.25.2.25" REDIS_PORT = 6379# REDIS 單機(jī)模式配置參數(shù) 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 哨兵模式配置參數(shù)。 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 集群模式配置參數(shù) REDIS_CLUSTER_PARAMS= {"password": "password" }# ----------------------------------------Scrapy 其他參數(shù)-------------------------------------# 在 redis 中保持 scrapy-redis 用到的各個(gè)隊(duì)列,從而允許暫停和暫停后恢復(fù),也就是不清理 redis queues SCHEDULER_PERSIST = True # 調(diào)度隊(duì)列 SCHEDULER = "scrapy_redis_sentinel.scheduler.Scheduler" # 去重 DUPEFILTER_CLASS = "scrapy_redis_sentinel.dupefilter.RFPDupeFilter" # 指定排序爬取地址時(shí)使用的隊(duì)列 # 默認(rèn)的 按優(yōu)先級(jí)排序( Scrapy 默認(rèn)),由 sorted set 實(shí)現(xiàn)的一種非 FIFO、LIFO 方式。 # SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderPriorityQueue' # 可選的 按先進(jìn)先出排序(FIFO) # SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack' # 可選的 按后進(jìn)先出排序(LIFO) # SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack'

    注:當(dāng)使用集群時(shí)單機(jī)不生效

    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 配置 # 使用的哈希函數(shù)數(shù),默認(rèn)為 6 BLOOMFILTER_HASH_NUMBER = 6# Bloomfilter 使用的 Redis 內(nèi)存位,30 表示 2 ^ 30 = 128MB,默認(rèn)為 30 ? (2 ^ 22 = 1MB 可去重 130W URL) BLOOMFILTER_BIT = 30# 是否開啟去重調(diào)試模式 默認(rèn)為 False 關(guān)閉 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 集群模式配置參數(shù) REDIS_CLUSTER_PARAMS= {# "password": "password" }# scrapy其他參數(shù) # 在 redis 中保持 scrapy-redis 用到的各個(gè)隊(duì)列,從而允許暫停和暫停后恢復(fù),也就是不清理 redis queues SCHEDULER_PERSIST = True # 調(diào)度隊(duì)列 SCHEDULER = "scrapy_redis_sentinel.scheduler.Scheduler" # 去重 DUPEFILTER_CLASS = "scrapy_redis_sentinel.dupefilter.RFPDupeFilter"# 指定排序爬取地址時(shí)使用的隊(duì)列 # 默認(rèn)的 按優(yōu)先級(jí)排序( Scrapy 默認(rèn)),由 sorted set 實(shí)現(xiàn)的一種非 FIFO、LIFO 方式。 SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderPriorityQueue' # 可選的 按先進(jìn)先出排序(FIFO) # SCHEDULER_QUEUE_CLASS = 'scrapy_redis_sentinel.queue.SpiderStack' # 可選的 按后進(jìn)先出排序(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 )

    經(jīng)過調(diào)試,修復(fù)了一個(gè)bug使用默認(rèn)爬取隊(duì)列時(shí)會(huì)報(bào)錯(cuò),需要將源碼中的 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])# 使用集群的時(shí)候不能使用 multi/exec 來完成一個(gè)事務(wù)操作;使用lua腳本來實(shí)現(xiàn)類似功能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)

    總結(jié)

    以上是生活随笔為你收集整理的爬虫教程( 4 ) --- 分布式爬虫 scrapy-redis、集群的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。