为什么使用分布式爬虫
分布式:MongoDB的主从(一主多从)
分布式就比如说一个工厂生产线,有车间主人,车间主人分配任务给一个员工,和任务分给多个员工的时候,那是一个人还是多个人的效率高那
同理:
- 单机版爬虫:
引擎把一个request请求任务放到调度器任务队列,向服务器发起请求的时候会从调度器拿到request请求给下载器发起请求。
-
多台机器同时爬取数据大大提高了爬取的效率。当多台机器同时进行爬虫的时候我们需要一个资源管理的容器(调度器)取管理和分配任务
提取的数据、新提取的url会继续构建一个request请求交给资源管理器
scrapy-redis
安装:pip3 install scrapy-redis
- Scrapy_redis在scrapy的基础上实现了更多,更强大的功能,具体体现在:reqeust去重,爬虫持久化,和轻松实现分布式
- Scrapy-redis提供了下面四种组件(components):(四种组件意味着这四个模块都要做相应的修改)
Scheduler
Duplication Filter
Item Pipeline
Base Spider
-
Scrapy_redis是工作流程:
具体操作:
配置settings文件
# 1:设置去重组件,使用的是scrapy_redis的去重组件,而不是scrapy自己的去重组件了
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 2:设置调度器,使用scrapy——redis重写的调度器,
# 而不再使用scrapy内部的调度器了
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 3:可以实现断点爬取=jondir,(请求的记录不会丢失,会存储在redis数据库中,
# 不会清楚 redis的队列,下次直接从redis的队列中爬取)
SCHEDULER_PERSIST = True
# 4:设置任务队列的模式(三选一):
# SpiderPriorityQueue数据scrapy-redis默认使用的队列模式(
# 有自己的优先级)默认第一种
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
# 使用了队列的形式,任务先进先出。
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
# 采用了栈的形式:任务先进后出
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
#5: 实现这个管道可以将爬虫端获取的item数据,统一保存在redis数据库中
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 6:指定要存储的redis数据库的主机IP
REDIS_HOST = '127.0.0.1' # 远端的ip地址
# 指定redis数据库主机的端口
REDIS_PORT = 6379
使用scrapy-redis
- 代码执行之后redis自动生成以下内容:
"xcfCrawlSpider:requests":存储的是请求的request对象
"xcfCrawlSpider:items":存储的爬虫端获取的items数据
"xcfCrawlSpider:dupefilter":存储的指纹(为了实现去重)
127.0.0.1:6379> type xcfCrawlSpider:requests
zset
127.0.0.1:6379> type xcfCrawlSpider:items
list
127.0.0.1:6379> type xcfCrawlSpider:dupefilter
set
- 第一种情况:只设置settings文件,并没有实现分布式,只是实现了scrapy_redis的数据储存和去重功能(只实现了存,没有取)
import scrapy
from xiachufang.items import XiachufangTagItem,XiachufangCaiPuItem,XiachufangUserInfoItem
class XcfSpider(scrapy.Spider):
name = 'xcf'
allowed_domains = ['xiachufang.com']
#start_urls = ['https://www.xiachufang.com/category/40076/?page=1']
start_urls = ['http://www.xiachufang.com/category/']
def start_requests(self):
pass
- 第二种:通用爬虫
from scrapy_redis.spiders import RedisCrawlSpider
# 继承自redis——crawlspider
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
# 缺少了start_url,多了redis_key:根据redis_key从redis数据库中获取任务
redis_key = 'mycrawler:start_urls'
启动爬虫
爬虫出现等待状态:我们需要在redis中设置起始任务:
redis输入命令:lpush xcfCrawlSpider:start_urls http://www.xiachufang.com/category/
注意:在redis保存起始url的时候,windows系统写url的时候不加引号,ubuntu如果输入redis命令不生效,url需要加引号
- 第三种情况:实现scrpy.spider爬虫的分布式爬虫
from scrapy_redis.spiders import RedisSpider
#继承自:RedisSpider
class MyCrawler(RedisSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
allowed_domains = ['dmoz.org']
#缺少了start_url,多了redis_key:根据redis_key从redis
#数据库中获取任务
redis_key = 'mycrawler:start_urls'
def start_requests(self):
"""
重写这个方法的目的可以根据自己的需求发起请求
:return:
"""
for url in self.start_urls:
yield scrapy.Request(url, callback=self.parse, dont_filter=True)
def parse(self, response):
pass
启动爬虫:scrapy crawl 爬虫名称
现象:爬虫处于等待状态
需要设置起始任务:
lpush mycrawler:start_urls 目标url
注意:实现scrpy.spider爬虫的分布式爬虫第一个回调方法必须是parse,否则代码无法运行。第三种情况同样要注意redis的命令