一、分布式爬虫介绍
分布式爬虫概念:多台机器上执行同一个爬虫程序,实现网站数据的分布爬取。
1、原生的Scrapy无法实现分布式爬虫的原因?
- 调度器无法在多台机器间共享:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls列表中的url。
- 管道无法给多台机器共享:多台机器爬取到的数据无法通过同一个管道对数据进行统一的数据持久出存储。
2、scrapy-redis组件
scrapy-redis是专门为scrapy框架开发的一套组件。该组件可以解决上述两个问题,让Scrapy实现分布式。 组件安装:
$ pip3 intall scrapy-redis
二、基于RedisCrawlSpider的分布式爬取
1、redis配置和启动
(1)对redis配置文件redis.conf进行配置
注释该行:bind 127.0.0.1,表示可以让其他ip访问redis
将yes该为no:protected-mode no,表示可以让其他ip操作redis
不注释时,只允许本机的客户端连接
bind 127.0.0.1
yes改为no,关闭redis的保护模式,客户端可以对服务器进行读写操作
protected-mode no
(2)基于配置文件开启redis服务器
# MAC/Linux
$ pwd
/Users/hqs/redis-5.0.2
$ src/redis-server redis.conf
# windows
$ redis-server.exe redis-windows.conf
2、项目和爬虫创建
$ scrapy startproject redisPro
$ cd redisPro/
$ scrapy genspider -t crawl qiubai www.qiushibaike.com/pic/
这里运用CrawlSpider创建爬虫文件爬取全站图片数据。
3、爬虫文件编写
import scrapy
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from redisPro.items import RedisproItem # 引入items类
class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'
# allowed_domains = ['www.qiushibaike.com/pic/']
# start_urls = ['http://www.qiushibaike.com/pic/']
# redis_key表示调度器中的队列(将要爬取的页面数据对应的url都需要放置到调度器队列中)
redis_key = 'qiubaispider' # 自定义调度器队列的名称,该行代码表示的含义和start_urls一样
# 链接提取器
# 从页面提取链接页码的url:/pic/page/2?s=5147462
# s后对应的参数值会在浏览器刷新时动态变化, 正则忽略s参数的变化进行提取
link = LinkExtractor(allow=r'/pic/page/\d+')
# 规则解析器
rules = (
Rule(link, callback='parse_item', follow=True),
)
def parse_item(self, response):
"""解析操作:提取图片链接"""
div_list = response.xpath('//div[@id="content-left"]/div') # 获取divz列表
for div in div_list: # 遍历div列表
# 解析提取div中图片url,保存在img标签的src属性下
img_url = "https:" + div.xpath('.//div[@class="thumb"]/a/img/@src').extract_first()
# 实例化items对象
item = RedisproItem()
item['img_url'] = img_url # 封装items
yield item
(1)引入scrapy-redis中的模块
在爬虫文件中要导入RedisCrawlSpider类,然后将爬虫文件修改成基于该类的源文件。
from scrapy_redis.spiders import RedisCrawlSpider
# class QiubaiSpider(CrawlSpider):
class QiubaiSpider(RedisCrawlSpider):
(2)声明redis_key属性
redis_key表示调度器中的队列(将要爬取的页面数据对应的url都需要放置到调度器队列中)。 redis_key自定义调度器队列的名称,表示的含义和start_urls一样,因此需要将原来的start_url注释掉。
class QiubaiSpider(RedisCrawlSpider):
name = 'qiubai'
# allowed_domains = ['www.qiushibaike.com/pic/']
# start_urls = ['http://www.qiushibaike.com/pic//']
# redis_key表示调度器中的队列(将要爬取的页面数据对应的url都需要放置到调度器队列中)
redis_key = 'qiubaispider' # 自定义调度器队列的名称,该行代码表示的含义和start_urls一样
(3)链接提取器和规则解析器定义
class QiubaiSpider(RedisCrawlSpider)
"""代码省略"""
# 链接提取器
# 从页面提取链接页码的url:/pic/page/2?s=5147462
# s后对应的参数值会在浏览器刷新时动态变化, 正则忽略s参数的变化进行提取
link = LinkExtractor(allow=r'/pic/page/\d+')
# 规则解析器
rules = (
Rule(link, callback='parse_item', follow=True),
)
(4)items.py编写和爬虫引入调用
import scrapy
class RedisproItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
img_url =scrapy.Field()
4、settings.py配置修改
(1)使用scrapy-redis组件中封装好的管道
使用scrapy-redis组件中封装好的可以被共享的管道。 可以将每台机器爬取到的数据存储通过该管道存储到redis数据库中,从而实现了多台机器的管道共享。
# Configure item pipelines
# See https://doc.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'redisPro.pipelines.RedisproPipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400,
}
(2)使用scrapy-redis组件中封装好的调度器
使用scrapy-redis组件中封装好的调度器,将所有的url存储到该指定的调度器中,从而实现了多台机器的调度器共享。 以下代码可在settings.py中任意位置粘贴:
# 使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 核心配置
# 是否允许暂停
SCHEDULER_PERSIST = True # 值为True表示:宕机恢复服务时,从宕机的那个地方开始爬取,不用从头开始
(3)爬虫程序不在redis本机时,指定redis地址
管道默认会连接且将数据存储到本机的redis服务中,如果想要连接存储到其他redis服务中需要在settings.py中进行如下配置
# 如果redis服务器不在自己本机,则需要做如下配置
REDIS_HOST = '192.168.31.31' # redis服务的ip地址
REDIS_PORT = 6379
# 可选配置
# REDIS_ENCODING = 'utf-8'
# REDIS_PARAMS = {'password':'123456'}
(4)修改ua和关闭robots协议
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36' # 伪装请求载体身份
# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 不遵从门户网站robots协议,避免某些信息爬取不到
5、执行爬虫程序
执行爬虫程序的方式与之前不同。 格式:lpush 队列名称(redis-key) 起始url
$ pwd
/Users/hqs/ScrapyProjects/redisPro/redisPro/spiders
$ scrapy runspider qiubai.py
.......
2018-12-08 12:04:34 [scrapy.extensions.telnet] DEBUG: Telnet console listening on 127.0.0.1:6023
然后需要在redis客户端中,将起始url扔到调度器队列中:
$ src/redis-cli
127.0.0.1:6379> lpush qiubaispider https://www.qiushibaike.com/pic/ # 将url扔入队列
(integer) 1
查看reids中保存的爬取数据:
127.0.0.1:6379> keys *
1) "qiubai:items"
2) "data"
3) "name"
4) "qiubai:dupefilter"
127.0.0.1:6379> lrange qiubai:items 0 -1
1) "{\"img_url\": \"https://pic.qiushibaike.com/system/pictures/12132/121326116/medium/FO4Z7Q83NXSOPYTL.jpg\"}"
......