分布式系统
当爬取内容过多,需要多机合作的时候,就需要用到分布式系统,这节博客讲解一下分布式爬虫的实现。
什么是分布式系统
分布式系统就是把一些计算机通过网络连接起来,然后协同工作
协同工作需要解决两个问题:
-
任务分解
把一个问题拆解成若干个独立任务,每个任务在一台节点上运行,实现多任务的并发执行 -
节点通信
节点之间互相通信,需要设计特定的通信协议来实现。协议可以采用RPC或Message Queue等方式
分布式爬虫系统
-
scrapy_redis
- scrapy-redis是为了更方便地实现scrapy分布式爬取,而提供了一些以redis数据库为基础的组件
- scrapy-redis提供了维持待爬取url的去重以及储存requests的指纹验证。原理是:redis维持一个共同的url队列,各个不同机器上的爬虫程序获取到的url都保存在redis的url队列,各个爬虫都从redis的url队列获取url,并把数据统一保存在同一个数据库里面
- 本节博客主要讲授基于scrapy_redis的分布式爬虫
-
消息队列
- MQ全称为Message Queue, 是一种分布式应用程序的的通信方法,消息发送后可以立即返回,由消息系统来确保消息的可靠传递
- 它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可以读取或者订阅队列中的消息
- 消息生产者只管把消息发布到 MQ 中而不用管谁来取,消息消费者只管从 MQ 中取消息而不管是谁发布的
-
RabbitMQ
RabbitMQ是MQ产品的典型代表,是一款基于AMQP协议可复用的企业消息系统。业务上,可以实现服务提供者和消费者之间的数据解耦,提供高可用性的消息传输机制,在实际生产中应用相当广泛
-
scrapy_rabbitmq分布式
Scrapy-rabbitmq-link 是可以让你从RabbitMQ 消息队列中取到URL并且分发给Scrapy spiders的组件
-
celery初步
Celery 用消息通信,通常使用中间人(Broker)在客户端和程序间斡旋。这个过程从客户端向队列添加消息开始,之后中间人把消息派送给程序
scrapy_redis分布式
什么是Redis数据库
Redis是一个使用ANSI C编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库
安装Redis数据库及scrapy_redi:
这里仅介绍Windows系统下的安装
-
下载Redis压缩包
前往:https://github.com/MicrosoftArchive/redis/releases
或者前往网盘链接:http://pan.baidu.com/s/1i5f9xSL 密码:t5bo
下载Redis-x64-3.2.100.zip,并将其解压缩 -
安装Redis
打开cmd,切换到解压的文件夹下执行命令redis-server –service-install redis.windows.conf –loglevel verbose
-
启动Redis服务
在解压的文件夹下执行命令redis-server –service-start
-
安装scrapy_redis
启动cmd,使用pip安装scrapy_redispip install scrapy_redis
Scrapy_redis设置
在项目中进行如下的设置:
- 更改去重对列:必有项
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
- 利用Redis去重:必有项
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
- 序列化方案
SCHEDULER_SERIALIZER = "scrapy_redis.picklecompat"
- 故障重跑
SCHEDULER_PERSIST = True
- URL队列优先级
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
- 把item储存进Redis管道
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 300
}
- 队列名字
REDIS_ITEMS_KEY = '%(spider)s:items'
- 配置连接
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
#或者
REDIS_URL = 'redis://user:pass@hostname:9001'
- 实例化一个Redis对象
REDIS_PARAMS[‘redis_cls’] = 'myproject.RedisClient'
- 设置set格式(不允许重复的元素)
REDIS_START_URLS_AS_SET = False
- 键值对中键值命名规则
REDIS_START_URLS_KEY = '%(name)s:start_urls'
- 编码方式
REDIS_ENCODING = 'latin1'
启动scrapy_redis
-
运行爬虫:
scrapy crawl 爬虫名
-
启动:(注意:启动的时候应该先切换到redis安装目录下)
- url选用的是list类型:
redis-cli lpush myspider:start_urls http://google.com
- url选用的是set类型:
redis-cli sadd myspider:start_urls http://google.com
实战
这次用的依然是上节课的city58的案例,只是修改了settings.py和city58_test.py两个文件。
-
city58_test.py:实现翻页爬取
# -*- coding: utf-8 -*- import scrapy from pyquery import PyQuery from ..items import City58Item from scrapy.http import Request from scrapy_redis.spiders import RedisSpider class City58TestSpider(RedisSpider): name = 'city58_test' allowed_domains = ['58.com'] start_urls = ['http://bj.58.com/chuzu/'] def parse(self, response): jpy = PyQuery(response.text) li_list = jpy('body > div.mainbox > div.main > div.content > div.listBox > ul > li').items() for it in li_list: a_tag = it('div.des > h2 > a') item = City58Item() item['name'] = a_tag.text() item['url'] = (a_tag.attr('href') if a_tag.attr('href') else '') item['price'] = it('div.listliright > div.money > b').text() yield item if not li_list: return pn = response.meta.get('pn',1) #得到meta中pn的数值,如果没有,就默认为1 pn += 1 #response.meta['pn'] = p if pn > 5 return #把相对路径转化为绝对路径,并request访问 req = response.follow('/chuzu/pn{}/'.format(pn), #拼接相对路径 callback = self.parse, meta = {'pn':pn} #使用meta参数进行传值 ) yield req
-
settings.py
SCHEDULER = "scrapy_redis.scheduler.Scheduler" #必有项:更改去重对列 # Ensure all spiders share same duplicates filter through redis. DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" #必有项:利用Redis去重 #SCHEDULER_PERSIST = True #故障重跑 REDIS_URL = 'redis://localhost:6379' #配置连接 #REDIS_START_URLS_AS_SET = False #设置set格式(不允许重复的元素)
-
运行时要记得在队列中塞入start_urls
redis-cli lpush city58_test:start_urls http://bj.58.com
redis desktop manager软件安装及使用
-
Windows系统安装包下载
前往网盘链接:http://pan.baidu.com/s/1gfpgQxt 密码:iz9u -
根据提示进行安装
只需要执行默认安装便可 -
打开软件,点击connect to redis server
-
输入数据库名字,以及host和端口
数据库名字test,host为127.0.0.1(本机),默认端口为6379 -
打开数据库,就可以看到数据了
其他的分布式系统
本篇博客介绍了基于scrapy-redis的分布式系统,用于分布式爬虫URL队列的存储和去重队列的存储。当然,对于更大型的爬虫系统,或者对于非scrapy框架的爬虫,可以选择其它的方案替代scrapy和scrapy-redis,比如可以使用RabbitMQ消息队列代替Redis。以下的内容,提供了更多关于分布式爬虫的知识和方案
-
消息队列
MQ全称为Message Queue, 是一种分布式应用程序的的通信方法,它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可以读取或者订阅队列中的消息爬虫之消息队列
在构建一个松耦合或是异步的系统时,消息队列是最常用的方法。在爬虫中使用消息队列有哪些好处呢?- 通过消息队列实现线程安全的去重
- 多进程消费爬虫任务队列
- 确保每一条任务都执行
RabbitMQ消息队列
RabbitMQ 是一种消息队列,用于程序间的通信
形象地说 : MQ就像一个邮局,发送者将消息写入 MQ,MQ 负责把消息发送给接收者。RabbitMQ 可支持 Java, PHP, Python, Go, JavaScript, Ruby 等多种语言
注意:RabbitMQ的使用需要事先安装
Scrapy-rabbitmq-link的安装和配置
Scrapy-rabbitmq-link 是可以让你从RabbitMQ 消息队列中取到URL并且分发给Scrapy spiders的组件- 安装:
pip install scrapy-rabbitmq-link
- 配置:
# Enable RabbitMQ scheduler SCHEDULER = "scrapy_rabbitmq_link.scheduler.SaaS" # Provide AMQP connection string RABBITMQ_CONNECTION_PARAMETERS = 'amqp://guest:guest@localhost:5672/' # Set response status codes to requeue messages on SCHEDULER_REQUEUE_ON_STATUS = [500] # Middleware acks RabbitMQ message on success DOWNLOADER_MIDDLEWARES = { 'scrapy_rabbitmq_link.middleware.RabbitMQMiddleware': 999 }
-
Celery - 分布式任务队列
Celery 是一个简单、灵活且可靠的,处理大量消息的分布式系统,并且提供维护这样一个系统的必需工具它是一个专注于实时处理的任务队列,同时也支持任务调度
Celery 需要一个发送和接受消息的传输者。Celery 支持 RabbitMQ、Redis 甚至其他数据库系统作为其消息代理中间件,但也提供大量其他实验性方案的支持
Celery 好处也很多,尤其在使用python构建的应用系统中,无缝衔接,使用相当方便
celery的应用场景
- 处理异步任务
- 任务调度
- 处理定时任务
- 分布式调度
- 安装Celery
安装Celery
pip install celery
配置Celery
from celery import Celery app = Celery('hello', broker='amqp://guest@localhost//') #配置rabbitMQ @app.task #装饰器 def hello(): return 'hello world'