19.基于scrapy-redis两种形式的分布式爬虫
redis分布式部署
1.scrapy框架是否可以自己实现分布式?
- 不可以。原因有二。
其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的url。(多台机器无法共享同一个调度器)
其二:多台机器爬取到的数据无法通过同一个管道对数据进行统一的数据持久出存储。(多台机器无法共享同一个管道)
2.基于scrapy-redis组件的分布式爬虫
- scrapy-redis组件中为我们封装好了可以被多台机器共享的调度器和管道,我们可以直接使用并实现分布式数据爬取。
- 实现方式:
1.基于该组件的RedisSpider类
2.基于该组件的RedisCrawlSpider类
3.分布式实现流程:上述两种不同方式的分布式实现流程是统一的
- 3.1 下载scrapy-redis组件:pip3 install scrapy-redis
- 3.2 redis配置文件的配置:
— 注释该行 :bind 127.0.0.1 或 将 ip 更改为 0.0.0.0 ,表示可以让其他ip访问redis
— 在这一行 将yes改为no : protected-mode no,表示可以让其他ip操作redis
-3.3 修改爬虫文件相关代码
- 1.导入scrapy-redis模块: from scrapy_redis.spiders import RedisSpider/RedisCrawlSpider
- 2.将当前爬虫类的父类修改成RedisSpider/RedisCrawlSpider
- 3.将allowed_domains和start_urls进行删除
- 4.添加一个新的属性redis_key = 'xxx',该属性值表示的就是可以被共享的调度器队列的名称
总结:将爬虫类的父类修改成基于RedisSpider或者RedisCrawlSpider。
注意:如果原始爬虫文件是基于Spider的,则应该将父类修改成RedisSpider,
如果原始爬虫文件是基于CrawlSpider的,则应该将其父类修改成RedisCrawlSpider。
-3.4 在配置文件中进行相关配置
1.开启使用scrapy-redis组件中封装好的管道,保证爬虫文件提交的item会被封装到共享管道中
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400
}
2.开启使用scrapy-redis组件中封装好的调度器
# 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。
# 如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据
SCHEDULER_PERSIST = True
3.在配置文件中进行爬虫程序链接redis的配置:
REDIS_HOST = 'redis服务的ip地址'
REDIS_PORT = 6379
REDIS_ENCODING = ‘utf-8’
REDIS_PARAMS = {‘password’:’123456’} #如果没有设置密码,可以不写
-3.5 开启redis服务器:redis-server 配置文件
-3.6 开启redis客户端:redis-cli
-3.7 运行爬虫文件:scrapy runspider SpiderFile
-3.8 向调度器队列中扔入一个起始url(在redis客户端中操作):lpush redis_key属性值 起始url
示例一:
基于Spider的,spider-redis分布式爬虫
爬取医疗器械每个网页中,医疗器械的注册编码,注册名称,生产地址
http://db.pharmcube.com/database/cfda/detail/cfda_cn_instrument/1
爬虫文件:
# -*- coding: utf-8 -*- import scrapy from scrapy_redis.spiders import RedisSpider from scrapy_redis_Pro.items import ScrapyRedisProItem class RedisdemoSpider(RedisSpider): name = 'redisDemo' url = 'http://db.pharmcube.com/database/cfda/detail/cfda_cn_instrument/%d' page = 1 # allowed_domains = ['www.xxx.com'] # start_urls = ['http://www.xxx.com/'] redis_key = "mwh" def parse(self, response): # 获取解析到数据 register_num = response.xpath('/html/body/div/table/tbody/tr[1]/td[2]/text()').extract_first() register_name = response.xpath('/html/body/div/table/tbody/tr[2]/td[2]/text()').extract_first() production_address = response.xpath('/html/body/div/table/tbody/tr[4]/td[2]/text()').extract_first() # 实例化一个item类,并将解析到数据封装该类中 item = ScrapyRedisProItem() item["register_num"] = register_num item["register_name"] = register_name item["production_address"] = production_address # 将解析到数据传递给管道文件中 yield item if self.page <130000: self.page += 1 new_url = format(self.url%self.page) yield scrapy.Request(url=new_url,callback=self.parse)
item.py 文件
import scrapy class ScrapyRedisProItem(scrapy.Item): # define the fields for your item here like: register_num = scrapy.Field() # 注册编号 register_name = scrapy.Field() # 注册名称 production_address = scrapy.Field() # 生产地址
pipelines.py 管道文件
class ScrapyRedisProPipeline(object): def process_item(self, item, spider): print(item) return item
setting.py 配置文件
BOT_NAME = 'scrapy_redis_Pro'
SPIDER_MODULES = ['scrapy_redis_Pro.spiders']
NEWSPIDER_MODULE = 'scrapy_redis_Pro.spiders'
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
ROBOTSTXT_OBEY = False
# 使用scrapy-redis中封装好的可以被共享的管道 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } # 开启使用scrapy-redis组件中封装好的 可以被共享调度器 # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True #配置redis REDIS_HOST = '192.168.12.29' REDIS_PORT = 6380 REDIS_ENCODING = 'utf-8'
示例二:
基于CrawlSpider的,spider-redis分布式爬虫
爬取抽屉网站网页中每一新闻的标题和作者
https://dig.chouti.com/all/hot/recent/1
爬虫文件
import scrapy from scrapy.linkextractors import LinkExtractor from scrapy.spiders import CrawlSpider, Rule from scrapy_redis.spiders import RedisCrawlSpider from scrapy_Crawl_Redis_Pro.items import ScrapyCrawlRedisProItem class CrawlRedisSpider(RedisCrawlSpider): name = 'crawl_redis' # allowed_domains = ['www.xxx.com'] # start_urls = ['http://www.xxx.com/'] redis_key = "drawer" rules = ( Rule(LinkExtractor(allow=r'/r/scoff/hot/d+'), callback='parse_item', follow=True), ) def parse_item(self, response): div_list = response.xpath('//div[@>) for div in div_list: item = ScrapyCrawlRedisProItem() item['title'] = div.xpath('.//div[@class="part1"]/a/text()').extract_first() item['author'] = div.xpath('.//div[@class="part2"]/a[4]/b/text()').extract_first() yield item
item.py 文件
import scrapy
class ScrapyCrawlRedisProItem(scrapy.Item):
# define the fields for your item here like:
title = scrapy.Field()
author = scrapy.Field()
pipelines.py管道文件
class ScrapyCrawlRedisProPipeline(object): def process_item(self, item, spider): print(item) return item
setting.py配置文件
BOT_NAME = 'scrapy_redis_Pro' SPIDER_MODULES = ['scrapy_redis_Pro.spiders'] NEWSPIDER_MODULE = 'scrapy_redis_Pro.spiders' USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36' ROBOTSTXT_OBEY = False # 使用scrapy-redis中封装好的可以被共享的管道 ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } # 开启使用scrapy-redis组件中封装好的 可以被共享调度器 # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True #配置redis REDIS_HOST = '192.168.12.29' REDIS_PORT = 6380 REDIS_ENCODING = 'utf-8'