• Scrapy框架(3)


    一、如何提升scrapy框架的爬取效率

      增加并发: 默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 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。

    二、CrawlSpider

      参考博客:https://www.cnblogs.com/bobo-zhang/p/9670764.html

    1、介绍

      CrawlSpider其实是Spider的一个子类,除了继承到Spider的特性和功能外,还派生出了自己独有的更加强大的特性和功能。其中最显著的功能就是"LinkExtractors链接提取器"。Spider是所有爬虫的基类,其设计原则只是为了爬取start_url列表中网页,而从爬取到的网页中提取出的url进行继续的爬取工作使用CrawlSpider更合适。

    2、使用

    1)案例一:爬取抽屉网段子模块(https://dig.chouti.com/r/scoff/hot/1)第一页到最后一页的数据的标题

      新建一个项目:scrapy startproject choutiPro

      进入项目目录下,新建一个爬虫文件:scrapy genspider -t crawl chouti www.xxx.com

      注意:生成爬虫文件的指令多了 "-t crawl",表示创建的爬虫文件是基于CrawlSpider这个类的,而不再是Spider这个基类,生成的爬虫文件内容如下:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    
    
    class ChoutiSpider(CrawlSpider):
        name = 'chouti'
        allowed_domains = ['www.xxx.com']
        start_urls = ['http://www.xxx.com/']
    
        rules = (
            Rule(LinkExtractor(allow=r'Items/'), callback='parse_item', follow=True),
        )
    
        def parse_item(self, response):
            item = {}
            #item['domain_id'] = response.xpath('//input[@id="sid"]/@value').get()
            #item['name'] = response.xpath('//div[@id="name"]').get()
            #item['description'] = response.xpath('//div[@id="description"]').get()
            return item

      项目的目录结构如下:

      各文件内容如下:

    # choutiPro/choutiPro/spiders/chouti.py
    
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    
    
    class ChoutiSpider(CrawlSpider):
        name = 'chouti'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://dig.chouti.com/r/scoff/hot/1']
    
        # 链接提取器,参数 allow表示链接提取器提取链接的规则(正则)
        link = LinkExtractor(allow=r'/r/scoff/hot/d+')
        # 让链接提取器继续作用到提取到的链接所对应的页面中
        rules = (
            # 规则解析器:将链接提取器提取到的链接所对应的页面数据进行指定形式的解析,注意follow要等于True
            Rule(link, callback='parse_item', follow=True),
        )
    
        def parse_item(self, response):
         print(response)   # div_list
    = response.xpath('//div[@class="item"]') # for div in div_list: # title = div.xpath('.//div[@class="part1"]/a/text()').extract_first() # print(title)

      注意修改settings.py中的USER_AGENT和ROBOTSTXT_OBEY。

      启动程序,如下图:

    2)案例二:爬取糗事百科的糗图模块(https://www.qiushibaike.com/pic/)所有页码的数据

      使用上面案例一的项目,修改爬虫文件 chouti.py文件为如下内容:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    
    
    class ChoutiSpider(CrawlSpider):
        name = 'qiubai'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.qiushibaike.com/pic/']
    
        link = LinkExtractor(allow=r'/pic/page/d+?s=d+')
        link1 = LinkExtractor(allow=r'/pic/$')
    
        rules = (
            Rule(link, callback='parse_item', follow=True),
            Rule(link1, callback='parse_item', follow=True),
        )
    
        def parse_item(self, response):
            print(response)

      启动项目,如下图:

    三、分布式爬虫

      参考博客:https://www.cnblogs.com/bobo-zhang/p/9686978.html

      思考:

        为什么原生的scrapy不能实现分布式?

          - 调度器不能被共享;

          - 管道无法被共享;

        scrapy-redis组件的作用是什么?

          - 提供了可以被共享的调度器和管道;

    1、下载安装scrapy-redis

      pip3 install scrapy-redis

    2、使用流程

      1)创建工程:scrapy startproject redisChoutiPro

      2)进入工程,创建爬虫文件:scrapy genspider -t crawl chouti www.xxx.com         

        目录结构如下:

      3)对爬虫文件,即spiders/chouti.py文件中的相关属性进行修改:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from scrapy_redis.spiders import RedisCrawlSpider   # 导包
    
    class ChoutiSpider(RedisCrawlSpider):      # 修改继承的类
        name = 'chouti'
        # allowed_domains = ['www.xxx.com']
        # start_urls = ['http://www.xxx.com/']
        redis_key = 'chouti'   # 调度器队列的名称
        
        ......

      4)在配置文件,即settings.py中进行配置如下参数:

    # - 使用组件中封装好的可以被共享的管道类
    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  # 数据指纹

      5)指定存储数据的redis,即在settings.py文件中配置如下参数:

    REDIS_HOST = 'redis服务的ip地址'
    REDIS_PORT = 6379

      6)配置redis数据库的配置文件

    protected-mode no      # 取消保护模式
    # bind 127.0.0.1       # 取消bind绑定

      7)启动redis

      8)写好爬虫文件的功能后,启动爬虫文件(进入spiders目录下执行):scrapy runspider chouti.py

        爬虫文件chouti.py内容如下:

    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from redisChoutiPro.items import RedischoutiproItem
    from scrapy_redis.spiders import RedisCrawlSpider   # 导包
    
    class ChoutiSpider(RedisCrawlSpider):      # 修改继承的类
        name = 'chouti'
        # allowed_domains = ['www.xxx.com']
        # start_urls = ['http://www.xxx.com/']
        redis_key = 'chouti'   # 调度器队列的名称
    
        rules = (
            Rule(LinkExtractor(allow=r'/all/hot/recent/d+'), callback='parse_item', follow=True),
        )
    
        def parse_item(self, response):
            div_list = response.xpath('//div[@class="item"]')
            for div in div_list:
                title = div.xpath('./div[4]/div[1]/a/text()').extract_first()
                author = div.xpath('./div[4]/div[2]/a[4]/b/text()').extract_first()
                item = RedischoutiproItem()
                item['title'] = title
                item['author'] = author
    
                yield item

        注意配置setting.py中的USER_AGENT和ROBOTSTXT_OBEY参数。

      9)向调度器队列中扔入一个起始url,即在redis-cli中执行如下命令:

    四、增量式爬虫

      参考博客:https://www.cnblogs.com/bobo-zhang/p/10373942.html

    1、案例一:对url去重

      需求:爬取4567tv网站中所有的电影详情数据

      创建项目,目录结构如下:

      各文件内容如下:

    # 爬虫文件movie.py
    
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    from increment1_Pro.items import Increment1ProItem
    from redis import Redis
    
    class MovieSpider(CrawlSpider):
        name = 'movie'
        # allowed_domains = ['www.xxx.com']
        start_urls = ['https://www.4567tv.tv/index.php/vod/show/id/7.html']
    
        rules = (
            Rule(LinkExtractor(allow=r'/index.php/vod/show/id/7/page/d+.html'), callback='parse_item', follow=True),
        )
    
        def parse_item(self, response):
            conn = Redis(host='127.0.0.1', port=6379)
            detail_url_list = response.xpath('//li[@class="col-md-6 col-sm-4 col-xs-3"]/div/a/@href').extract()
            for url in detail_url_list:
                url = 'https://www.4567tv.tv' + url
                ex = conn.sadd('movies_url',url)
                # ex == 1 表示set中没有存储url
                if ex == 1:
                    yield scrapy.Request(url=url, callback=self.parse_detail)
                else:
                    print('网站没有更新数据,暂无新数据可爬取!')
    
    
        def parse_detail(self, response):
            item = Increment1ProItem()
            item['name'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/h1/text()').extract_first()
            item['actor'] = response.xpath('/html/body/div[1]/div/div/div/div[2]/p[3]/a/text()').extract_first()
    
            yield item
    # 管道文件pipelines.py
    
    from redis import Redis
    
    class Increment1ProPipeline(object):
    
        conn = None
    
        def open_spider(self, spider):
            self.conn = Redis(host='127.0.0.1', port=6379)
    
        def process_item(self, item, spider):
            print('有新数据被爬取到,正在入库......')
            self.conn.lpush('movie_data', item)
            return item
    # items.py文件
    
    import scrapy
    
    class Increment1ProItem(scrapy.Item):
        name = scrapy.Field()
        actor = scrapy.Field()

      注意:settings.py中配置好ROBOTSTXT_OBEY、USER_AGENT和管道参数ITEM_PIPELINES。

    2、案例二:对内容去重

      需求:爬取糗事百科中的段子和作者数据

      创建项目,目录结构如下:

     

      各文件结构如下:

    # 爬虫文件 qiubai.py
    
    # -*- coding: utf-8 -*-
    import scrapy
    from scrapy.linkextractors import LinkExtractor
    from scrapy.spiders import CrawlSpider, Rule
    
    from increment2_Pro.items import Increment2ProItem
    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),
        )
    
        def parse_item(self, response):
            div_list = response.xpath('//div[@class="article block untagged mb15 typs_hot"]')
            conn = Redis(host='127.0.0.1', port=6379)
    
            for div in div_list:
                item = Increment2ProItem()
                item['content'] = div.xpath('.//div[@class="content"]/span//text()').extract()
                item['content'] = ''.join(item['content'])
                item['author'] = div.xpath('./div/a[2]/h2/text() | ./div[1]/span[2]/h2/text()').extract_first()
                source = item['author'] + item['content']
                # 自己制定了一种形式的数据指纹
                hashValue = hashlib.sha256(source.encode()).hexdigest()
    
                ex = conn.sadd('qiubai_hash', hashValue)
                if ex == 1:
                    yield item
                else:
                    print('没有更新数据要爬取!')
    # 管道文件 pipelines.py
    
    from redis import Redis
    
    class Increment2ProPipeline(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']
            }
            self.conn.lpush('qiubaiData', dic)
            print('爬取到一条数据,正在入库......')
            return item
    # items.py文件
    
    import scrapy
    
    class Increment2ProItem(scrapy.Item):
        content = scrapy.Field()
        author = scrapy.Field()

      注意:settings.py中配置好ROBOTSTXT_OBEY、USER_AGENT和管道参数ITEM_PIPELINES。

      第一次启动爬虫文件如下图:

      马上再次启动如下图:

      过十分钟再次启动如下图:

     

      

      

  • 相关阅读:
    JS 给li标签下所有a标签添加点击事件并添加和删除样式
    Linux简介及常用命令
    简易计算器
    登陆窗口小项目
    时间工具类
    String类的常用方法(附带练习)
    java-自定义异常
    Java小练习
    Java-接口练习1
    动态加载js
  • 原文地址:https://www.cnblogs.com/li-li/p/10475923.html
Copyright © 2020-2023  润新知