爬虫入门(六)
1.回顾上篇
1.请求传参(item): - 应用场景:解析的数据不在同一张页面中 - Request(callback,meta={}) 2.LOG_LEVEL LOG_FILE 3.下载中间件: - 批量拦截请求(代理ip和UA)和响应(处理页面数据) 4.如何在scrapy使用selenium 1.在spider的init方法中实例化一个浏览器对象 2.在spider的closed方法中关闭浏览器对象 3.在下载中间件类的process_response方法中接收spider中的浏览器对象 4.处理执行相关自动化操作(发起请求,获取页面数据) 5.实例化一个新的响应对象(from scrapy.http import HtmlResponse),且将页面数据存储到该对象中 6.返回新的响应对象 7.在配置文件中开启中间件 5.如何提升scrapy爬取数据的效率: 增加并发: 默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。 降低日志级别: 在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’ 禁止cookie: 如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False 禁止重试: 对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False 减少下载超时: 如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
2.使用scrapy自带的分页处理
# 创建spider爬虫文件命令 scrapy genspider -t crawl filename url
1 # -*- coding: utf-8 -*- 2 import scrapy 3 from scrapy.linkextractors import LinkExtractor 4 from scrapy.spiders import CrawlSpider, Rule 5 6 7 class ChoutiSpider(CrawlSpider): 8 # name = 'chouti' 9 # # allowed_domains = ['www.xxx.com'] 10 # start_urls = ['https://dig.chouti.com/r/scoff/hot/1'] 11 # # 连接提取器 12 # # allow表示的就是连接提取器提取连接的规则(正则) 13 # link = LinkExtractor(allow=r'/r/scoff/hot/\d+') 14 # 15 # rules = ( 16 # # 规则解析器:将连接提取器提取到的连接所对应的页面数据进行指定形式的解析 17 # Rule(link, callback='parse_item', follow=True), 18 # # 让连接提取器继续作用到连接提取器提取到的连接所对应的页面中 19 # ) 20 # 21 # def parse_item(self, response): 22 # print(response) 23 24 name = 'qiushi' 25 # allowed_domains = ['www.xxx.com'] 26 start_urls = ['https://www.qiushibaike.com/pic/'] 27 # 连接提取器 28 # allow表示的就是连接提取器提取连接的规则(正则) 29 link = LinkExtractor(allow=r'/pic/page/\d+\?s=\d+') 30 link1 = LinkExtractor(allow=r'/pic/$') 31 rules = ( 32 # 规则解析器:将连接提取器提取到的连接所对应的页面数据进行指定形式的解析 33 Rule(link, callback='parse_item', follow=True), 34 # 让连接提取器继续作用到连接提取器提取到的连接所对应的页面中 35 Rule(link1, callback='parse_item', follow=True), 36 ) 37 38 def parse_item(self, response): 39 print(response)
3.分布式爬虫
分布式爬虫实现流程 1.环境安装:pip install scrapy-redis 2.创建工程 3.创建爬虫文件:RedisCrawlSpider RedisSpider - scrapy genspider -t crawl xxx www.xxx.com 4.对爬虫文件中的相关属性进行修改: - 导报:from scrapy_redis.spiders import RedisCrawlSpider - 将当前爬虫文件的父类设置成RedisCrawlSpider - 将起始url列表替换成redis_key = 'xxx'(调度器队列的名称) 5.在配置文件中进行配置: - 使用组件中封装好的可以被共享的管道类: ITEM_PIPELINES = { 'scrapy_redis.pipelines.RedisPipeline': 400 } - 配置调度器(使用组件中封装好的可以被共享的调度器) # 增加了一个去重容器类的配置, 作用使用Redis的set集合来存储请求的指纹数据, 从而实现请求去重的持久化 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" # 使用scrapy-redis组件自己的调度器 SCHEDULER = "scrapy_redis.scheduler.Scheduler" # 配置调度器是否要持久化, 也就是当爬虫结束了, 要不要清空Redis中请求队列和去重指纹的set。如果是True, 就表示要持久化存储, 就不清空数据, 否则清空数据 SCHEDULER_PERSIST = True - 指定存储数据的redis: REDIS_HOST = 'redis服务的ip地址' REDIS_PORT = 6379 - 配置redis数据库的配置文件 - 取消保护模式:protected-mode no - bind绑定: #bind 127.0.0.1 - 启动redis 6.执行分布式程序 scrapy runspider xxx.py(文件的绝对路径) 7.向调度器队列中仍入一个起始url: 在redis-cli中执行: lpush redis_key url(起始url)
settings
1 ITEM_PIPELINES = { 2 'scrapy_redis.pipelines.RedisPipeline': 400 3 } 4 # 增加了一个去重容器类,作用使用Redis的set集合来存储请求的指纹数据,从而实现请求去重的持久化 5 DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" 6 # 使用scrapy-redis组件自己的调度器 7 SCHEDULER = "scrapy_redis.scheduler.Scheduler" 8 # 配置调度器是否要持久化,也就是当爬虫结束了,要不要清空redis中请求队列和去重指纹的set。如果是true,就表示要持久化,就不清空,否则清空数据。 9 SCHEDULER_PERSIST = True #数据指纹 10 11 REDIS_HOST = '127.0.0.1' 12 REDIS_PORT = 6379
spider
1 # -*- coding: utf-8 -*- 2 import scrapy 3 from scrapy.linkextractors import LinkExtractor 4 from scrapy.spiders import CrawlSpider, Rule 5 from scrapy_redis.spiders import RedisCrawlSpider 6 from redisChoutiPro.items import RedischoutiproItem 7 8 9 class ChoutiSpider(RedisCrawlSpider): 10 name = 'chouti' 11 # allowed_domains = ['www.xxx.com'] 12 # start_urls = ['http://www.xxx.com/'] 13 redis_key = 'chouti' # 调度器队列的名称 14 rules = ( 15 Rule(LinkExtractor(allow=r'/all/hot/recent/\d+'), callback='parse_item', follow=True), 16 ) 17 18 def parse_item(self, response): 19 div_list = response.xpath('//div[@class="item"]') 20 for div in div_list: 21 title = div.xpath('./div[4]/div[1]/a/text()').extract_first() 22 autor = div.xpath('./div[4]/div[2]/a[4]/b/text()').extract_first() 23 24 item = RedischoutiproItem() 25 item['title'] = title 26 item['author'] = autor 27 28 yield item
4.增量式的常见两种情况
1.处理url地址的重复
spider
1 # -*- coding: utf-8 -*- 2 import scrapy 3 from scrapy.linkextractors import LinkExtractor 4 from scrapy.spiders import CrawlSpider, Rule 5 from redis import Redis 6 from increment1_Pro.items import Increment1ProItem 7 8 9 class MovieSpider(CrawlSpider): 10 name = 'movie' 11 # allowed_domains = ['www.123.com'] 12 start_urls = ['https://www.4567tv.tv/index.php/vod/show/id/7.html'] 13 14 rules = ( 15 Rule(LinkExtractor(allow=r'/index.php/vod/show/id/7/page/\d+\.html'), callback='parse_item', follow=True), 16 ) 17 18 def parse_item(self, response): 19 conn = Redis(host='127.0.0.1', port=6379) 20 detail_url_list = ['https://www.4567tv.tv' + i for i in response.xpath('//li[@class="col-md-6 col-sm-4 col-xs-3"]/div/a/@href').extract()] 21 for url in detail_url_list: 22 # ex == 1:set中没有存储url 23 ex = conn.sadd('moves_url', url) 24 if ex == 1: 25 yield scrapy.Request(url=url, callback=self.parse_detail) 26 else: 27 print('网站没有更新数据,暂无新数据可爬!') 28 29 def parse_detail(self, response): 30 item = Increment1ProItem() 31 item['name'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/h1/text()').extract_first() 32 item['actor'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[3]/a/text()').extract_first() 33 34 yield item
pipelines
1 # -*- coding: utf-8 -*- 2 3 # Define your item pipelines here 4 # 5 # Don't forget to add your pipeline to the ITEM_PIPELINES setting 6 # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html 7 8 from redis import Redis 9 10 class Increment1ProPipeline(object): 11 conn = None 12 13 def open_spider(self, spider): 14 self.conn = Redis(host='127.0.0.1', port=6379) 15 16 def process_item(self, item, spider): 17 dic = { 18 'name': item['name'], 19 'actor': item['actor'] 20 } 21 print('有新数据被爬取到,正在入库......') 22 self.conn.lpush('movie_data', item) 23 return item
2.处理爬取数据内容重复
spider
1 # -*- coding: utf-8 -*- 2 import scrapy 3 import hashlib 4 from scrapy.linkextractors import LinkExtractor 5 from scrapy.spiders import CrawlSpider, Rule 6 from redis import Redis 7 from increment2_Pro.items import Increment2ProItem 8 9 10 class QiubaiSpider(CrawlSpider): 11 name = 'qiubai' 12 # allowed_domains = ['www.123.com'] 13 start_urls = ['https://www.qiushibaike.com/text/'] 14 15 rules = ( 16 Rule(LinkExtractor(allow=r'/text/page/\d+/'), callback='parse_item', follow=True), 17 ) 18 19 def parse_item(self, response): 20 div_list = response.xpath('//div[@class="article block untagged mb15 typs_hot"]') 21 conn = Redis(host='127.0.0.1', port=6379) 22 for div in div_list: 23 item = Increment2ProItem() 24 item['content'] = div.xpath('./a[1]/div/span//text()').extract() 25 item['content'] = ''.join(item['content']) 26 item['author'] = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first() 27 28 source = item['author'] + item['content'] 29 # 自己定制一种形式的数据指纹 30 hash_value = hashlib.sha256(source.encode()).hexdigest() 31 32 ex = conn.sadd('qiubai_hash', hash_value) 33 if ex == 1: 34 yield item 35 else: 36 print('没有更新数据!')
pipelines
1 # -*- coding: utf-8 -*- 2 3 # Define your item pipelines here 4 # 5 # Don't forget to add your pipeline to the ITEM_PIPELINES setting 6 # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html 7 from redis import Redis 8 9 10 class Increment2ProPipeline(object): 11 conn = None 12 13 def open_spider(self, spider): 14 self.conn = Redis(host='127.0.0.1', port=6379, encoding='utf-8') 15 16 def process_item(self, item, spider): 17 dic = { 18 'author': item['author'], 19 'content': item['content'] 20 } 21 22 self.conn.lpush('aiubaiData', dic) 23 print('爬取到一条数据,正在入库!') 24 return item