1. 简介
流程
scrapy-redis
scrapy电商商城定制开发是一个基于redis的scrapy组件,电商商城定制开发用于快速实现scrapy电商商城定制开发项目的部署和数据爬取。
组件
- Scrapy Engine(引擎):负责Spider、ItemPipeline、Downloader、Scheduler电商商城定制开发中间的通讯,信号、电商商城定制开发数据传递等。
- Scheduler(调度器):电商商城定制开发它负责接受引擎发送过来的Request请求,电商商城定制开发并按照一定的方式进行整理排列,入队,电商商城定制开发当引擎需要时,电商商城定制开发交还给引擎。
- Downloader(下载器):负责下载Scrapy Engine(引擎)电商商城定制开发发送的所有Requests请求,电商商城定制开发并将其获取到的Responses交还给Scrapy Engine(引擎),电商商城定制开发由引擎交给Spider来处理。
- Spider(爬虫):它负责处理所有Responses,从中分析提取数据,获取Item字段需要的数据,并将需要跟进的URL提交给引擎,再次进入Scheduler(调度器)。
- Item Pipeline(管道):它负责处理Spider中获取到的Item,并进行进行后期处理(详细分析、过滤、存储等)的地方。
- Downloader Middlewares(下载中间件):自定义扩展下载功能的组件。
- Spider Middlewares(Spider中间件):自定扩展和操作引擎和Spider中间通信的功能组件。
流程
(1) 引擎(Scrapy Engine)向爬虫(Spiders)请求第一个要爬取的URL。
(2) 引擎从爬虫中获取到第一个要爬取的URL,封装成请求(Request)并交给调度器()。
(3) 调度器访问Redis数据库对请求进行判重,如果不重复,就把这个请求添加到Redis中。
(4) 当调度条件满足时,调度器会从Redis中取出Request,交给引擎,引擎将这个Request通过下载中间件转发给下载器。
(5) 一旦页面下载完毕,下载器(Downloader)生成一个该页面的响应(Response),并将其通过下载中间件发送给引擎。
(6) 引擎从下载器中接收到响应,并通过爬虫中间件(Spider Middlewares)发送给爬虫处理。
(7) Spider处理Response,并返回爬取到的Item及新的Request给引擎。
(8) 引擎将爬取到的Item通过Item Pipeline给Redis数据库,将Request给调度器。
从(2) 开始重复,直到调度器中没有更多的Request为止。
2. scrapy-redis对比scrapy
scrapy
-
调度器、管道不可以被分布式集群共享
-
scrapy使用改造后的collection.deque(双端队列)存放待爬取的request
-
scrapy中跟待爬取队列直接相关的是Scheduler(调度器),scheduler负责对新的requests进行入列操作,把待爬取的队列安装优先级建立字典,根据request中的priority属性做优先级(越小的优先级越高)进行出队列操作(不能共享)。
-
scrapy把已发送的request指纹放到集合中,下一个request指纹拿到集合中对比,存在说明已经爬取过不继续执行操作。
def request_seen(self, request: Request) -> bool: fp = self.request_fingerprint(request) if fp in self.fingerprints: # self.fingerprints指纹集合 return True self.fingerprints.add(fp) if self.file: self.file.write(fp + '') return False
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
scrapy-redis
-
scrapy-redis使用redis list存放待爬取的request
-
scrapy-redis在setting配置SCHEDULER = "scrapy_redis.scheduler.Scheduler"替换原本的待爬取队列。使用redis进行任务分发与调度,把所有待爬取的请求都放入redis,所有爬虫都去redis读取请求。
-
Scrapy-Redis中的去重是由Duplication Filter组件实现的,该组件利用Redis中set集合不重复的特性,巧妙地实现了这个功能。首先Scrapy-Redis调度器接收引擎递过来的请求,然后将这个请求指纹存入set集合中检查是否重复,并把不重复的请求加入到Redis的请求队列中。
def request_seen(self, request): fp = self.request_fingerprint(request) # This returns the number of values added, zero if already exists. added = self.server.sadd(self.key, fp) return added == 0
- 1
- 2
- 3
- 4
- 5
-
scrapy-redis不再使用原有的Spider类,重写RedisSpider继承Spider和RedisMixin类。当我们生成一个Spider继承RedisSpider时,调用setup_redis函数,这个函数会去连接redis数据库,然后会设置signals(信号):一个是当spider空闲时候的signal,会调用spider_idle函数,这个函数调用schedule_next_request函数,保证spider是一直活着的状态,并且抛出DontCloseSpider异常。一个是当抓到一个item时的signal,会调用item_scraped函数,这个函数会调用schedule_next_request函数,获取下一个request。
3. 简述
命令
# 新建项目$ scrapy startproject project_name# 新建爬虫$ scrapy genspider -t basic spider_name www.baidu.combasic 基础crawl 自动爬虫csvfeed 用来处理csv文件xmlfeed 用来处理xml文件# 运行爬虫$ scrapy crawl spider_name# 自动爬虫(做增量爬虫)$ scrapy genspider -t crawl xxx www.xxx.com$ scrapy runspider xxx.py# 交互式终端进入终端:scrapy shell ‘www.baidu.com’查看状态: response显示网页html:response.textxpath匹配数据:response.xpath(‘//div[@class=”head_wrapper”]’)退出终端:exit()# 帮助$ scrapy -h/--help
- 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
目录文件说明
scrapy.cfg :项目的配置文件Spider/ :项目的Python模块,将会从这里引用代码Spider/items.py :项目的目标文件Spider/pipelines.py :项目的管道文件Spider/middlewares.py : 爬虫、下载中间件Spider/settings.py :项目的设置文件Spider/spiders/ :存储爬虫代码目录
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
代码修改
# setting.py# 1.启用调度将请求存储进redisSCHEDULER = "scrapy_redis.scheduler.Scheduler"# 2.确保所有spider通过redis共享相同的重复过滤。DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"# 3.指定连接到Redis时要使用的主机和端口。REDIS_HOST = '47.97.102.116'REDIS_PORT = 6379# 默认的scrapy-redis请求队列形式(按优先级)SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"# 队列形式,请求先进先出#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"# 栈形式,请求先进后出#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"# 设置为True不清理redis队列,允许暂停/恢复抓取。多个爬虫读取url会造成重复抓取SCHEDULER_PERSIST = True# spider修改from scrapy_redis.spiders import RedisSpiderclass Spider(RedisSpider): name = 'spider_name' # allowed_domains = ['movie.douban.com'] # 爬取边界 redis_key = 'db:start_urls' # 开启爬虫钥匙# redis$ lpush db:start_urls www.baidu.com
- 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
调度算法
爬虫请求调度算法有三种:
-
队列(默认)
SCHEDULER_QUEUE_CLASS=‘scrapy_redis.queue.SpiderQueue’
先进先出队列,先放进Redis的请求会优先爬取。
-
栈
SCHEDULER_QUEUE_CLASS=‘scrapy_redis.queue.SpiderStack’
后进先出,后放入redis的请求会优先爬取。
-
优先级队列
SCHEDULER_QUEUE_CLASS=‘scrapy_redis.queue.SpiderPriorityQueue’
根据优先级算法计算出请求爬取先后。
Redis存放内容
-
spidername:items(不建议用,爬取内容多时会很占用内存,一般把数据保存到mongodb)
list类型,保存爬虫获取到的数据item内容是json字符串。
-
spidername:dupefilter
set类型,用于爬虫访问的URL去重内容,是40个字符的url的hash字符串
-
spidername:start_urls
list类型,用于接收redis spider启动时的第一个url
-
spidername:requests
zset类型,用于存放requests等待调度。内容是requests对象的序列化字符串
4. 分布式策略
Slaver获取Master待爬取Request进行数据爬取,在爬取过程中处理生成新的任务抛给Master。Master只有一个Redis数据库复制对Slave任务进行去重、加入待爬取队列。
注意:Master和Slaver交互的信息并不单单是url而是包含许多信息的Request
{'url': 'https://book.qidian.com/info/1010868264/', 'callback': 'parse_detail', 'errback': None, 'headers': {b'Referer': [b'https://www.qidian.com/all/page2/'], b'Accept': [b'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'], b'Accept-Language': [b'en'], b'User-Agent': [b'Scrapy/2.6.1 (+https://scrapy.org)'], b'Accept-Encoding': [b'gzip, deflate'], b'Cookie': [b'_csrfToken=Y4KP9vSv2X6XuWvDyVeke5o0jlyUazCqrrosBGrJ; newstatisticUUID=1652946272_1559582696; fu=762226548']}, 'method': 'GET', 'body': b'', 'cookies': {}, 'meta': {'item': {'detail_url': 'https://book.qidian.com/info/1010868264/', 'img_url': 'https://bookcover.yuewen.com/qdbimg/349573/1010868264/150', 'name': '诡秘之主'}, 'depth': 2, 'download_timeout': 180.0, 'download_slot': 'book.qidian.com', 'download_latency': 0.5939218997955322, 'retry_times': 1}, 'encoding': 'utf-8', 'priority': -1, 'dont_filter': True, 'flags': [], 'cb_kwargs': {}}
- 1
- 2
- 3
5.进阶
中间件使用
下载中间件(Downloader Middleware)核心方法有3个:
-
process_request(request, spider)
。设置headers,proxy
-
process_response(request, response, spider)
。设置response编码等信息
-
process_exception(request, exception, spider)
。异常报错(曾它做过重试,但后来发现直接在setting设置更方便)
crawl 爬虫
基于CrawlSpider
可以很方便地进行全站数据爬取
class QidianCrawlSpider(CrawlSpider): # 继承CrawlSpider类 name = 'qidian_crawl' allowed_domains = ['www.qidian.com', 'book.qidian.com'] start_urls = ['http://www.qidian.com/'] rules = ( Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True), )# rules规则解析器内包含链接提取器LinkExtractor(allow=r'Items/'),callback指定解析方法,follow指定爬取页面内可见部分还是全部(True把链接提取器继续作用到链接提取器对应的页面,False爬取页面内可见部分页面)。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
使用CrawlSpider
生成爬虫文件时,在规则解析器rules
里面添加正则表达式进而发起请求,如果要一个请求内需要再次发起请求,就需要在rules
中添加链接请求并指定对应的解析方法
6. 优缺点
优点:广泛多域爬取大量url,节约时间,去重简单,可以启动尽可能爬虫去进行数据爬取。
内包含链接提取器LinkExtractor(allow=r’Items/'),callback指定解析方法,follow指定爬取页面内可见部分还是全部(True把链接提取器继续作用到链接提取器对应的页面,False爬取页面内可见部分页面)。
使用`CrawlSpider`生成爬虫文件时,在规则解析器`rules`里面添加正则表达式进而发起请求,如果要一个请求内需要再次发起请求,就需要在`rules`中添加链接请求并指定对应的解析方法### 6. 优缺点优点:广泛多域爬取大量url,节约时间,去重简单,可以启动尽可能爬虫去进行数据爬取。缺点:相对于单个爬虫不便于管理。Request对象里面信息量较大,降低爬虫速度、占用Redis存储空间。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11