• 在Scrapy中使用Django的ORM异步存储数据


    在Scrapy中使用Django的ORM异步存储数据

    django的orm可以脱离django使用,只要我们将django的环境舒适化就可以了。

    在scrapy中使用

    首先我们的创建一个django项目,然后在创建一个scrapy项目。

    然后再scrapy中初始化django的环境

    一般我们在scrapy的项目的__init__.py里面初始化

    import django
    import os
    import sys
    
    # 将django的项目路径加入到当前的环境
    sys.path.insert(0, os.path.dirname(os.getcwd()))
    
    # django项目舒适化
    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject.settings')
    
    django.setup()
    

    初始化完成之后,我们就可以直接在scrapy中导入django的orm并使用了。

    异步存储

    因为scrapy是异步的爬虫框架,如果我们在里面直接使用django的orm会有io阻塞的操作。这时候我们就借助asyncio这个包来帮助我们在scrapy中运行同步阻塞的代码。

    因为我们scrapy中处理数据都放在pipline中处理,所以我放在pipline中展示

    from concurrent.futures import ThreadPoolExecutor
    import asyncio
    from goods import models
    from . import items
    
    
    class WebspidersPipeline:
        '''todo 异步存储'''
        
        # 创建事件循环对象
        loop = asyncio.get_event_loop()
        # 创建线程池
        executor = ThreadPoolExecutor()
        # 任务队列
        tasks = []
    	
        # 处理不同的pipline
        async def process_item(self, item, spider):
            if isinstance(item, items.GoodsItem):
                return self.process_goods_item(item, spider)
            elif isinstance(item, items.GoodsSizeItem):
                return self.process_goods_size_item(item, spider)
            elif isinstance(item, items.GoodsStockItem):
                return self.process_goods_stock_item(item, spider)
            return item
    	
        def process_goods_item(self, item, spider):
            '''将保存数据的处理方法加入到任务队列'''
            task = self.loop.run_in_executor(self.executor, self.executor_func(models.Goods, item), )
            self.tasks.append(task)
            return item
    
        def process_goods_size_item(self, item, spider):
            task = self.loop.run_in_executor(self.executor, self.executor_func(models.GoodsSize, item), )
            self.tasks.append(task)
            return item
    
        def process_goods_stock_item(self, item, spider):
            task = self.loop.run_in_executor(self.executor, self.executor_func(models.GoodsStock, item), )
            self.tasks.append(task)
            return item
    
        @staticmethod
        def executor_func(model, item):
            '''主要作用是将有参数的函数转换为无参数的函数返回,方便run_in_executor方法调用,这个方法它只接受位置传参,不接受关键字传参'''
            def func():
                return model.objects.create(**item)
    
            return func
    
        def close_spider(self, spider):
            '''当爬虫关闭的时候调用这个方法保存数据'''
            self.loop.run_until_complete(asyncio.wait(self.tasks))
    

    运行结果

    之前直接使用同步的方法存储的时候,2000个请求+数据存储花费了大约10分钟(sqlite3)

    后面使用异步存储的时候,使用sqlite3会报错,因为sqlite3是单线程的,我们是一个线程池对象,并发存储会被sqlite3拒绝(database was locked)

    后面改用了mysql存储,2000个请求+数据存储花费了大约40s,这个提升量还是很惊人的。

    后面分析了一下,在scrapy中使用同步的方式存储会导致scrapy的异步请求会等待同步的存储完成之后才去执行,大量的时间浪费了等待上面。

    后面单独执行网络请求部分,没有数据存储,2000个请求花费了大约25s旁边。

  • 相关阅读:
    $P5240 Derivation$
    $P2504 [HAOI2006]聪明的猴子$
    $P1194 买礼物$
    $P2690 接苹果$
    $CF1141C Polycarp Restores Permutation$
    $CF1141B Maximal Continuous Rest$
    $CF1141A Game 23$
    $P1215 [USACO1.4]母亲的牛奶 Mother's Milk$
    $luogu2375[NOI2014]$
    poj 1419 (最大独立集)
  • 原文地址:https://www.cnblogs.com/ivy-blogs/p/14085745.html
Copyright © 2020-2023  润新知