学习目标
1.理解scrapy-redis概念
2.理清工作原理和主要组件
3.理清爬虫编写的思路
4.实战
简介
scrapy-redis是scrapy基于Redis数据库的组件。
scrapy本身不支持分布式,所以并不会共享调度队列。scrapy-redis是为了方便实现scrapy分布式爬取,提供了redis为基础的组件。
特点
- 分布式爬取:可启动多个spider工程,相互间共享单个redis的request队列
- 分布式数据处理:将爬到的item推送到redis队列中(意味着可以按照需求启动尽可能的多的处理程序来共享item的队列,进行item数据持久化处理)
scrapy 与scrapy-redis
scrapy:通用爬虫框架,不支持分布式
scrapy-redis:在原有的scrapy基础上修改了组件,使之能够支持分布式
注意
分布式听起来好像蛮厉害的,但它只不过是提高爬虫运行效率的一个小环节。当爬取的要求是:短时间内爬取海量的信息,这时候适合使用分布式爬虫。不过难点不在于代码怎么设计,而是着重于反爬策略。速度过快,会给对方服务器造成必要的压力,自然而然会被反爬技术阻拦。
工作流程
爬虫(起始的url构造成request对象)→爬虫中间件→引擎
引擎(从爬虫中获取到第一个要爬的url,封装成request)→调度器
调度器(访问Redis数据库,Redis对请求判重,不重则添加到Redis中)→Redis
Redis→调度器(当调度条件满足时,调度器从Redis中取出request)→引擎→下载中间件→下载器
下载器(页面一旦下载完毕,下载器将生成该页面的response)→下载中间件→引擎
引擎(接收response响应)→爬虫中间件→爬虫
爬虫(处理response并将item和新的request)→引擎
引擎(将item)→item pipeline→Redis
同时引擎(将新的request)→调度器(重复2步骤)
主要组件
Scheduler(调度器)
scrapy改变了python原本collection.deque(双向队列),形成自己的scrapy.queue。但是scrapy里多个spider不能共享待爬的scrapy.queue队列,因此scrapy本身不支持分布式爬虫。
scrapy-redis把scrapy Queue换成了Redis数据库,由一个数据库统一存放要爬取请求,这样就能够实现分布式爬虫。
Dupilaction Filter (去重)
scrapy使用集合实现去重功能,这是它自带的模块。它会把已经发送出去的请求指纹(指纹可以理解未一个特殊值)放到一个集合。它会把下一个请求指纹和集合中做对比,存在则说明请求已经发送,不存在则继续操作。
scrapy-redis去重是由Dupilcation Filter组件实现的,调度器通过redis的set不重复特性,实现去重。Dupilcation Filter set存放爬取过的request,调度器将spider新生成的request放到redisd Dupilcation Filter set中检查是否重复,不重复则将request push到redis的request队列中。
Item Pipeline(管道)
当爬虫取到的Item数据时,引擎将会把Item数据交给Item Pipeline,Item Pipeline再把Item数据保存发哦Redistribution的Item队列中。scrapy-redis对ItemPipeline进行修改,可以根据key从Item队列中取出Item,从而实现Item Process集群。
Base Spider(爬虫)
不再使用原来的Spider类,而是使用RedisSpider类,这个类继承了Spider和RedisMixin这两个类。RedisMixin用来读取Redistribution上的url。
爬虫编写的思路
普通爬虫
将普通爬虫改为分布式爬虫
实战
环境要求
- 安装scrapy-redis库:
pip install scrapy-redis
- 安装Redis
- 安装Redis可视化工具(可选)
创建项目
流程简单,不讲
配置scrapy-redis
详细的代码可以到这里看看,文章中就不赘述了。
这次的代码我是直接拿上一篇的实战改的,普通scrapy思路都一样,我这里就不讲了,只讲修改的部分
修改settings.py
在该文件下添加以下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # 设置重复过滤器模块
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
# 设置调取器,scrap_redis中的调度器具备与数据库交互的功能
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 设置爬虫结束时是否保持redis数据库中的去重集合与任务队列,程序结束后不清空redis数据库
SCHEDULER_PERSIST = True
ITEM_PIPELINES = {
#这一行更具项目名不同而不同,本身就有,不用管也不用抄
'suningBook.pipelines.SuningbookPipeline': 300,
# 开启管道,该管道将会把数据存到Redis数据库中,也可以自己设置数据库
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 设置redis数据库
REDIS_URL = "redis://127.0.0.1:6379"
# 请求间隔时长
DOWNLOAD_DELAY = 1
|
pipeline.py
增加下面的代码,可以查看item获取的时间,同时知道这些数据是哪一个虫子爬取的
1
2
3
4
5
6
7
8
9
| from datetime import datetime
class SuningbookPipeline(object):
def process_item(self, item, spider):
# 获取UTC时间
item["crawled"] = datetime.utcnow()
# 爬虫名称
item["spider"] = spider.name
return item
|
spider/bookPro.py
这里是写爬虫的地方,只需要修改部分即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
| # -*- coding=utf-8 -*-
from scrapy_redis.spiders import RedisSpider
class BookproSpider(RedisSpider):
name = 'bookPro'
redis_key = "bookprospider:sun_urls"
def parse(self, response):
# 一级分类(L1表示Lecel 1)
L1_list = response.xpath('//div[@class="menu-list"]/div/dl')
# 获取大分类
for L1 in L1_list:
item = {}
item['L1_cate'] = L1.xpath('./dt/h3/a/text()').extract_first()
# 获取二级分类
L2_list = L1.xpath('./dd/a')
for L2 in L2_list:
# 二级分类url
item['L2_href'] = L2.xpath('./@href').extract_first()
# 二级分类名称
item['L2_name'] = L2.xpath('./text()').extract_first()
# return item
yield scrapy.Request(
url=item['L2_href'],
callback=self.parse_book_list,
meta={'item': item}
)
def parse_book_list(self, response):
item = response.meta['item']
# #获取每一本书的信息
books_list = response.xpath('//ul[@class="clearfix"]/li')
for book in books_list:
#书籍详情URL
item['book_href'] = 'https:' + book.xpath('//div[@class="res-img"]/div/a/@href').extract_first()
# 书籍名字
item['book_name'] = book.xpath('//div[@class="res-img"]/div/a/img/@alt').extract_first()
# 书籍图片
item['src'] = 'https:' + book.xpath('//div[@class="res-img"]/div/a/img/@src2').extract_first()
yield scrapy.Request(
url=item['book_href'],
callback=self.parse_book_detail,
meta={'item': item}
)
def parse_book_detail(self, response):
item = response.meta['item']
# 作者
item['author'] = response.xpath('//ul[@class="bk-publish clearfix"]/li[1]/text()').extract_first()
# 出版社
item['publish'] = response.xpath('//ul[@class="bk-publish clearfix"]/li[2]/text()').extract_first()
yield item
|
简单解释一下下:这个爬虫继承了RedisSpider,支持分布式爬取。这里注意的是,不再需要写start_urls和了,被redis_key取代。必须指定redis_key,这个是启动爬虫的格式。
执行
- 打开Redis的redis-server.exe
- 打开Redis的redis-cli.exe
- 在项目里面执行运行代码:
1
2
3
| # 注意,要进入spiders文件目录下执行这条命令
# 格式: scrapy runspider [要执行的爬虫文件名]
scrapy runspider bookPro.py
|
- 在Master端的redis-cli输入lpush命令:
1
2
| # 命令格式: lpush [redis_key] [网站URL]
127.0.0.1:6379> lpush bookprospider:sun_urls http://book.suning.com/
|
ok,这样就可以了
再看一眼Redis可视化工具