Python网络爬虫技术与实战
上QQ阅读APP看书,第一时间看更新

2.4.3 增量爬虫技术

某些网站会定时在原有网页数据的基础上更新一批数据。例如某电影网站会实时更新一批最近热门的电影,小说网站会根据作者创作的进度实时更新最新的章节数据等。在遇到类似的场景时,我们便可以采用增量式爬虫。增量爬虫技术(incremental Web crawler)就是通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网站更新后的新数据。

关于如何进行增量式的爬取工作,以下给出三种检测重复数据的思路:1)在发送请求之前判断这个URL是否曾爬取过;2)在解析内容后判断这部分内容是否曾爬取过;3)写入存储介质时判断内容是否已存在于介质中。第一种思路适合不断有新页面出现的网站,比如小说的新章节、每天的实时新闻等;第二种思路则适合页面内容会定时更新的网站;第三种思路则相当于最后一道防线。这样做可以最大限度地达到去重的目的。

不难发现,实现增量爬取的核心是去重。目前存在两种去重方法。第一,对爬取过程中产生的URL进行存储,存储在Redis的set中。当下次进行数据爬取时,首先在存储URL的set中对即将发起的请求所对应的URL进行判断,如果存在则不进行请求,否则才进行请求。第二,对爬取到的网页内容进行唯一标识的制定(数据指纹),然后将该唯一标识存储至Redis的set中。当下次爬取到网页数据的时候,在进行持久化存储之前,可以先判断该数据的唯一标识在Redis的set中是否存在,从而决定是否进行持久化存储。

关于增量爬虫的使用方法示例如下所示。

【例2-3】爬取4567tv网站中所有的电影详情数据


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 it

管道文件:


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.push('movieData',dic)    
        # 如果push不进去,那么dic变成str(dic)或者改变redis版本   
        pip install -U redis==2.10.6
        return item