zoukankan      html  css  js  c++  java
  • 爬虫之增量式和请求传参

    Scrapy的日志等级

     在命令行中使用scrapy crawl spiderFileName运行程序时,在终端里打印输出的就是scrapy的日志信息。

    日志信息的种类

    - ERROR : 一般错误

    - WARNING : 警告

    - INFO : 一般的信息

    - DEBUG : 调试信息

    在settings文件中设置指定输出

    在配置文件内加入:
      LOG_LEVEL = 日志信息的种类

      LOG_FILE= “log.txt”  设置日志信息存储到指定文件

    请求传参

    在某些情况下,我们爬取的数据不在同一个页面中,例如,我们爬取一个电影网站,电影的名称,评分在一级页面,而要爬取的其他电影详情在其二级子页面中。这时我们就需要用到请求传参。

    请求传参方式

    yield scrapy.Request(url=item['detail_url'],callback=self.parse_detail,meta={'item':item}),

    请求二级详情页面,解析二级页面中的相应内容,通过meta参数进行Request的数据传递

    demo代码

    #爬虫文件
    # -*- coding: utf-8 -*-
    import scrapy
    from moviePro.items import MovieproItem
    
    class MovieSpider(scrapy.Spider):
        name = 'movie'
        allowed_domains = ['www.id97.com']
        start_urls = ['http://www.id97.com/']
    
        def parse(self, response):
            div_list = response.xpath('//div[@class="col-xs-1-5 movie-item"]')
    
            for div in div_list:
                item = MovieproItem()
                item['name'] = div.xpath('.//h1/a/text()').extract_first()
                item['score'] = div.xpath('.//h1/em/text()').extract_first()
                #xpath(string(.))表示提取当前节点下所有子节点中的数据值(.)表示当前节点
                item['kind'] = div.xpath('.//div[@class="otherinfo"]').xpath('string(.)').extract_first()
                item['detail_url'] = div.xpath('./div/a/@href').extract_first()
                #请求二级详情页面,解析二级页面中的相应内容,通过meta参数进行Request的数据传递
                yield scrapy.Request(url=item['detail_url'],callback=self.parse_detail,meta={'item':item})
    
        def parse_detail(self,response):
            #通过response获取item
            item = response.meta['item']
            item['actor'] = response.xpath('//div[@class="row"]//table/tr[1]/a/text()').extract_first()
            item['time'] = response.xpath('//div[@class="row"]//table/tr[7]/td[2]/text()').extract_first()
            item['long'] = response.xpath('//div[@class="row"]//table/tr[8]/td[2]/text()').extract_first()
            #提交item到管道
            yield item
    spider.py
    # -*- coding: utf-8 -*-
    
    # Define here the models for your scraped items
    #
    # See documentation in:
    # https://doc.scrapy.org/en/latest/topics/items.html
    
    import scrapy
    
    
    class MovieproItem(scrapy.Item):
        # define the fields for your item here like:
        name = scrapy.Field()
        score = scrapy.Field()
        time = scrapy.Field()
        long = scrapy.Field()
        actor = scrapy.Field()
        kind = scrapy.Field()
        detail_url = scrapy.Field()
    items.py
    # -*- 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
    
    import json
    class MovieproPipeline(object):
        def __init__(self):
            self.fp = open('data.txt','w')
        def process_item(self, item, spider):
            dic = dict(item)
            print(dic)
            json.dump(dic,self.fp,ensure_ascii=False)
            return item
        def close_spider(self,spider):
            self.fp.close()
    pipeline

    提高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。

    增量式爬虫

    简介及应用场景

     当我们在浏览相关网页的时候会发现,某些网站定时会在原有网页数据的基础上更新一批数据,例如某电影网站会实时更新一批最近热门的电影。小说网站会根据作者创作的进度实时更新最新的章节数据等等。通过爬虫程序监测某网站数据更新的情况,以便可以爬取到该网站更新出的新数据。

    思路

    - 在发送请求之前判断这个URL是不是之前爬取过。

    - 在解析内容后判断这部分内容是不是之前爬取过。

    - 写入存储介质是判断内容是否已经存在。

    不难发现,其实增量爬取的核心是去重, 至于去重的操作在哪个步骤起作用,只能说各有利弊。在我看来,前两种思路需要根据实际情况取一个(也可能都用)。第一种思路适合不断有新页面出现的网站,比如说小说的新章节,每天的最新新闻等等;第二种思路则适合页面内容会更新的网站。第三个思路是相当于是最后的一道防线。这样做可以最大程度上达到去重的目的。

    去重方法

    - 将爬取过程中产生的URl存储在redis的set中,在接下来进行数据爬取时,先对将要发起的请求对应的url和set中的url进行判断,若存在,则不景行请求。

    - 对爬取到的内容进行唯一标识的指定,可以做md5摘要,并存储到redis的set中,下一次进行存储之前,先进行判断。

    DEMO源码(一)

    #spider.py
    # -*- 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

    pipelines.py

     -*- 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

    DEMO源码(二)

    #spider.py
    # -*- 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('该条数据已经爬取过了,不需要再次爬取了!!!')
    #pipelines.py
    # -*- 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
  • 相关阅读:
    flask总结02
    flask总结01
    恩智浦Freescale Cortex-A9 迅为IMX6开发板平台初体验
    [分享] IMX6嵌入式开发板linux QT挂载U盘及TF卡
    迅为4412嵌入式安卓开发板兼容3G网络|4G网络
    迅为嵌入式4412平台兼容3G/4G模块的安卓开发板
    飞思卡尔开发板-迅为IMX6开兼容单核 双核 四核Plus开发板
    物联网初学者智能家居必备迅为iTOP-4412开发板
    【分享】4412开发板POP烧写ubuntu出错,如何挂载emmc分区解决方法
    [安卓开发板]迅为IMX6 四核Android开发板
  • 原文地址:https://www.cnblogs.com/cuiyuanzhang/p/9555432.html
Copyright © 2011-2022 走看看