zoukankan      html  css  js  c++  java
  • 20.scrapy框架之增量式爬虫

    一、增量式爬虫

    1.什么是增量式爬虫???

      —  通俗的来说,爬取网站中更新的数据,不管是产生新页面,还是原本的页面更新,这种变化都被称为增量, 而爬取过程则被称为增量爬取

    2.回顾一下爬虫的工作流程

      1. 指定URL,发送URL请求,获取页面数据

      2. 获得响应对象

      3. 解析对象的内容

      4. 储存内容

    3. 实现增量式爬虫的方案:

      1.在发送请求之前,判断url之前是否爬取过

        a.将即将进行爬取的数据对应的url存储到redis的set中.

      2.根据爬取到的数据进行重复过滤,然后在进行持久化存储(在解析内容后判断这部分内容是不是之前爬取过)

           b.将爬取到的数据给其生成一个唯一的标识(可以将该标识作为mysql的列.可以将该标识存储到redis的set中)

      3.写入存储介质时判断内容是不是已经在介质中存在

    4. 实现增量式爬取:4567

      不难发现,其实增量爬取的核心是去重, 至于去重的操作在哪个步骤起作用,只能说各有利弊

      前两种思路需要根据实际情况取一个(也可能都用)。第一种思路适合不断有新页面出现的网站,比如说小说的新章节,每天的最新新闻等等;第二种思路则适合页面内容会更新的网站。第三个思路是相当于是最后的一道防线。这样做可以最大程度上达到去重的目的。

    最简单的去重方式自然是将所有访问过的URL和其对应的内容保存下来,然后过一段时间重新爬取一次并进行比较,然后决定是否需要覆盖。这显然是不实际的,因为会消耗很多资源。目前比较实际的做法就是给URL或者其内容(取决于这个网站采用哪种更新方式)上一个标识,这个标识有个比较好听的名字,叫数据指纹

      这里很容易想到的一种数据指纹就是哈希值,根据哈希函数的特性,我们可以为任意内容生成一个独一无二的定长字符串,之后只要比较这个哈希值就行了。哈希值是一个很伟大的发明,几乎在任何地方都有它的影子,它利用数学特性,计算机只要经过简单的计算就可以得到唯一的特征值,这个计算过程的开销基本可以忽略不计,当然这是题外话了。

      不过即使用了哈希值,你仍需要一个地方存储所有的哈希值,并且要能做到方便的取用。如果你的存储介质是数据库,一般的数据库系统都能提供索引,如果把哈希值作为唯一索引呢,这应该是可行的。有些数据库也提供查询后再插入的操作,不过本质上应该也是索引。和哈希值类似的还有MD5校验码,殊途同归。

      除了自建指纹,其实在发送请求时还有一些技巧,比如说304状态码,Last-modified字段,文件大小和MD5签名。很好理解,就不细说了。

      综上所述,在数据量不大的时候,几百个或者就几千个的时候,简单自己写个小函数或者利用集合的特性去重就行了。如果数据量够大,数据指纹的价值就体现出来了,另外,如果要对数据做持久化(简单说就是去重操作不会被事故影响,比如说断电),就需要用到Redis数据库

    5. 具体实现

      示例一:在发送请求之前,判断url之前是否爬取过

            基于scrape框架中的CrawlSpider类进行爬取,4567电影网中电影下面的爱情片对应数据

          http://www.4567tv.tv/frim/index7-11.html

       
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from redis import Redis
    from moviePro.items import MovieproItem
    
    
    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=6380,db=1)
        def parse_item(self, response):
            li_list = response.xpath('//div[@class="index-area clearfix"]/ul/li')
            for li in li_list:
                # 获取详情页面的url
                detail_url = 'http://www.4567tv.tv' + li.xpath('./a/@href').extract_first()
                #将详情页面的url存入redis
                ex = self.conn.sadd("urls",detail_url)
                # 在向redis中存取的时候判断当前的url是否存在,如果不存在返回1,存在返回0
                if ex==1:
                    print("该url还未被爬取,可以进行数据爬取")
                    # 获取详情页面
                    yield scrapy.Request(url=detail_url,callback=self.detail_data)
                else:
                    print("该url已被爬取,暂无更新数据")
    
        # 解析详情页中的电影名称和类型,进行持久化存储
        def detail_data(self,response):
            # 解析出所需的数据
            name = response.xpath('//dt[@class="name"]/text()').extract_first()
            kind = response.xpath('//div[@class="ct-c"]/dl/dt[4]/a/text()').extract_first()
            # 创建一个item对象,并将解析到数据进行储存到该对象当中
            item = MovieproItem()
            item["name"] = name
            item["kind"] = kind
            item["kind"] = "".join(item["kind"])
            # 将item对象进行持久化储存
            yield item
    爬虫文件
       
    import scrapy
    
    class MovieproItem(scrapy.Item):
        # define the fields for your item here like:
        name = scrapy.Field()
        kind = scrapy.Field()
    items.py
       pipelines.py 管道文件 
       
    BOT_NAME = 'moviePro'
    
    SPIDER_MODULES = ['moviePro.spiders']
    NEWSPIDER_MODULE = 'moviePro.spiders'
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    
    ROBOTSTXT_OBEY = False
    
    # 开启管道文件
    ITEM_PIPELINES = {
       'moviePro.pipelines.MovieproPipeline': 300,
    }
    setting.py 配置文件

      示例二: 在解析内容后判断这部分内容是不是之前爬取过

         基于scrape框架中的CrawlSpider类进行爬取,糗事百科,文字对应新闻的名称,和内容

         https://www.qiushibaike.com/text/

        
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from qiubaiPro.items import QiubaiproItem
    import hashlib
    from redis import Redis
    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=6380,db=2)
    
        def parse_item(self, response):
            print(response)
            div_list = response.xpath('//div[@id="content-left"]/div')
            for div in div_list:
                author = div.xpath('./div/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()
                content = div.xpath('.//div[@class="content"]/span/text()').extract_first()
                #
                item = QiubaiproItem()
                item["author"] = author
                item["content"] = content
    
                # 将解析到的数据值生成一个唯一的标识进行redis存储
                source = item["author"] + item["content"]
                # 使用md5进行加密
                md5_obj = hashlib.md5()
                md5_obj.update(source.encode("utf-8"))
                source_id= md5_obj.hexdigest()
    
                # 将解析内容的唯一表示存储到redis的data_id中
                ex = self.conn.sadd("data_id",source_id)
    
                if ex == 1:
                    print("数据还没有被爬取,可以进行爬取!!!")
                    yield item
                else:
                    print("该条数据已被爬取,请勿再次爬取")
    爬虫文件 
       
    import scrapy
    
    class QiubaiproItem(scrapy.Item):
        # define the fields for your item here like:
        author = scrapy.Field()
        content = scrapy.Field()
    items.py
       
    from redis import Redis
    import json
    class QiubaiproPipeline(object):
        conn =None
    
        def open_spider(self,spider):
            self.conn = Redis(host="127.0.0.1",port=6380,db=2)
        def process_item(self, item, spider):
            dic = {
                "author":item["author"],
                "content":item["content"]
            }
            print(dic)
            self.conn.lpush("qiubaiData",json.dumps(dic))
            return item
    piipelines.py 管道文件
        
    BOT_NAME = 'qiubaiPro'
    
    SPIDER_MODULES = ['qiubaiPro.spiders']
    NEWSPIDER_MODULE = 'qiubaiPro.spiders'
    USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
    
    ROBOTSTXT_OBEY = False
    
    # 开启管道文件
    ITEM_PIPELINES = {
       'qiubaiPro.pipelines.QiubaiproPipeline': 300,
    }
    setting.py 配置文件
  • 相关阅读:
    7. Spring验证、数据绑定和类型转换
    J2EE应用与移动互联网-写在前头
    IT基础设施资源的实践----写在前头
    JavaScript随笔记(一)基础概念以及变量类型
    js函数表达式
    js面形对象(2)
    js面向对象
    viPlugin安装破解
    Ubuntu12.04 使用中遇到的问题
    关于sizeof
  • 原文地址:https://www.cnblogs.com/mwhylj/p/10279554.html
Copyright © 2011-2022 走看看