当我们在浏览相关网页的时候会发现, 某些网站定时会在原有网页数据的基础上更新一批数据, 例如某电影网站会实时更新一批最近热门的电影. 小说网站会根据作者创作的进度实时更新最新的章节数据等等. 那么, 类似的情景, 当我们在爬虫的过程中遇到时, 我们是不是需要定时更新程序以便能爬取到网站中最近更新的数据呢?
概念: 通过爬虫程序监测某网站数据更新的情况, 以便可以爬取到该网站更新出的新数据.
如何进行增量式的爬取工作:
在发送请求之前判断这个 URL 是不是之前爬取过
在解析内容后判断这部分内容是不是之前爬取过
写入存储介质时判断内容是不是已经在介质中存在
分析:
不难发现, 其实增量爬取的核心是去重, 至于去重的操作在哪个步骤起作用, 只能说各有利弊. 在我看来, 前两种思路需要根据实际情况取一个 (也可能都用). 第一种思路适合不断有新页面出现的网站, 比如说小说的新章节, 每天的最新新闻等等; 第二种思路则适合页面内容会更新的网站. 第三个思路是相当于是最后的一道防线. 这样做可以最大程度上达到去重的目的.
去重方法
将爬取过程中产生的 url 进行存储, 存储在 Redis 的 set 中. 当下次进行数据爬取时, 首先对即将要发起的请求对应的 url 在存储的 url 的 set 中做判断, 如果存在则不进行请求, 否则才进行请求.
对爬取到的网页内容进行唯一标识的制定, 然后将该唯一表示存储至 Redis 的 set 中. 当下次爬取到网页数据的时候, 在进行持久化存储之前, 首先可以先判断该数据的唯一标识在 Redis 的 set 中是否存在, 在决定是否进行持久化存储.
二. 项目案例
- 需求: 爬取 4567tv 网站中所有的电影详情数据.
爬虫文件:
- # -*- coding: utf-8 -*-
- import scrapy
- from scrapy.linkextractors import LinkExtractor
- from scrapy.spiders import CrawlSpider, Rule
- from Redis import Redis
- from incrementPro.items import IncrementproItem
- class MovieSpider(CrawlSpider):
- name = 'movie'
- # allowed_domains = ['www.xxx.com']
- start_urls = ['http://www.4567tv.tv/frim/index7-11.html']
- rules = (
- Rule(LinkExtractor(allow=r'/frim/index7-\d+\.html'), callback='parse_item', follow=True),
- )
- #创建 Redis 链接对象
- conn = Redis(host='127.0.0.1',port=6379)
- def parse_item(self, response):
- li_list = response.xpath('//li[@class="p1 m1"]')
- for li in li_list:
- #获取详情页的 url
- detail_url = 'http://www.4567tv.tv'+li.xpath('./a/@href').extract_first()
- #将详情页的 url 存入 Redis 的 set 中
- ex = self.conn.sadd('urls',detail_url)
- if ex == 1:
- print('该 url 没有被爬取过, 可以进行数据的爬取')
- yield scrapy.Request(url=detail_url,callback=self.parst_detail)
- else:
- print('数据还没有更新, 暂无新数据可爬取!')
- #解析详情页中的电影名称和类型, 进行持久化存储
- def parst_detail(self,response):
- item = IncrementproItem()
- item['name'] = response.xpath('//dt[@class="name"]/text()').extract_first()
- item['kind'] = response.xpath('//div[@class="ct-c"]/dl/dt[4]//text()').extract()
- item['kind'] = ''.join(item['kind'])
- yield item
管道文件:
- # -*- coding: utf-8 -*-
- # Define your item pipelines here
- #
- # Don't forget to add your pipeline to the ITEM_PIPELINES setting
- # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
- from Redis import Redis
- class IncrementproPipeline(object):
- conn = None
- def open_spider(self,spider):
- self.conn = Redis(host='127.0.0.1',port=6379)
- def process_item(self, item, spider):
- dic = {
- 'name':item['name'],
- 'kind':item['kind']
- }
- print(dic)
- self.conn.lpush('movieData',dic)
- return item
- 需求: 爬取糗事百科中的段子和作者数据.
爬虫文件:
- # -*- coding: utf-8 -*-
- import scrapy
- from scrapy.linkextractors import LinkExtractor
- from scrapy.spiders import CrawlSpider, Rule
- from incrementByDataPro.items import IncrementbydataproItem
- from Redis import Redis
- import hashlib
- class QiubaiSpider(CrawlSpider):
- name = 'qiubai'
- # allowed_domains = ['www.xxx.com']
- start_urls = ['https://www.qiushibaike.com/text/']
- rules = (
- Rule(LinkExtractor(allow=r'/text/page/\d+/'), callback='parse_item', follow=True),
- Rule(LinkExtractor(allow=r'/text/$'), callback='parse_item', follow=True),
- )
- #创建 Redis 链接对象
- conn = Redis(host='127.0.0.1',port=6379)
- def parse_item(self, response):
- div_list = response.xpath('//div[@id="content-left"]/div')
- for div in div_list:
- item = IncrementbydataproItem()
- item['author'] = div.xpath('./div[1]/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()
- item['content'] = div.xpath('.//div[@class="content"]/span/text()').extract_first()
- #将解析到的数据值生成一个唯一的标识进行 Redis 存储
- source = item['author']+item['content']
- source_id = hashlib.sha256(source.encode()).hexdigest()
- #将解析内容的唯一表示存储到 Redis 的 data_id 中
- ex = self.conn.sadd('data_id',source_id)
- if ex == 1:
- print('该条数据没有爬取过, 可以爬取......')
- yield item
- else:
- print('该条数据已经爬取过了, 不需要再次爬取了!!!')
管道文件:
- # -*- coding: utf-8 -*-
- # Define your item pipelines here
- #
- # Don't forget to add your pipeline to the ITEM_PIPELINES setting
- # See: https://doc.scrapy.org/en/latest/topics/item-pipeline.html
- from Redis import Redis
- class IncrementbydataproPipeline(object):
- conn = None
- def open_spider(self, spider):
- self.conn = Redis(host='127.0.0.1', port=6379)
- def process_item(self, item, spider):
- dic = {
- 'author': item['author'],
- 'content': item['content']
- }
- # print(dic)
- self.conn.lpush('qiubaiData', dic)
- return item
来源: http://www.bubuko.com/infodetail-2984199.html