@vikholodov

Как загружать большие данные через api?

Импортирую товары с сайта allegro.pl через их апишку (в архитектуре Django), ставлю процесс на VPS 6 ядер, 4гб оперативной памяти, SSD 30gb, Posgresql, CentOS 7, и через пару часов вижу в консоле Killed, т.е. сервер самостоятельно убивает процесс, предположительно из-за высокой нагрузки (товаров в районе 5 млн позиций). Когда импортировал с локальной машины, были помню какие-то предупреждения по памяти, хотя у меня 128 SSD и 8gb оперативки, но процесс тем не менее шел.
Как сделать, чтобы процесс не убивался и можно ли его еще как-то ускорить? Ограничение 9000 запросов в минуту.
Вот сам монстр:
import time
from decimal import Decimal


import zeep
from django.core.management import BaseCommand
from pytils.translit import slugify
from core.models import Product, Category
from multiprocessing.dummy import Pool as ThreadPool
import multiprocessing
from django.db import connection

class Command(BaseCommand):

    def connect(self, catId):
        allegro_api_key = 'тут ключ'
        sandbox_web_api = 'https://webapi.allegro.pl.webapisandbox.pl/service.php?wsdl'
        items_list = []
        prod_web_api = 'https://webapi.allegro.pl/service.php?wsdl'
        offset = 0
        while True:
            client = zeep.Client(wsdl=prod_web_api)
            options = {'item': {'filterId': 'category', 'filterValueId': [str(catId)]}}
            try:
                result = client.service.doGetItemsList(allegro_api_key, 1, filterOptions=options, resultSize=1000, resultOffset=offset, resultScope=2)
            except:
                time.sleep(2)
                print('sleep')
                continue

            if result and result['itemsList']:
                products = result['itemsList']['item']
            else:
                break
            for i in products:
                if i['endingTime'] == u'Zako\u0144czona':
                    continue
                items_list.append(i)
            offset += 1000
        return items_list

    def get_items(self, items_list):

        connection.close()
        product_data = items_list
        for i in product_data:
            sku = i['itemId']  # itemId sku
            if Product.objects.filter(sku=sku):
                continue
            priceType = i['priceInfo']['item'][0]['priceType']  # priceType, исключить bidding
            if priceType == 'bidding':
                continue
            try:
                general_image_sm = i['photosInfo']['item'][0]['photoUrl']  # маленькое фото
            except Exception:
                continue
            try:
                general_image = i['photosInfo']['item'][-1]['photoUrl']  # большое фото
                r = i['photosInfo']['item']
                photos = []
                for ph in r:
                    if ph['photoSize'] == 'large':
                        photos.append(ph['photoUrl'])
            except Exception:
                continue
            condition = i['conditionInfo']  # condition new used
            if condition == 'new':
                condition = 'Новый'
            elif condition == 'used':
                condition = 'Б.у.'
            price_pl = Decimal(i['priceInfo']['item'][0]['priceValue']).quantize(Decimal('.00'))  # priceValue
            price_ru = (price_pl + (price_pl * Decimal(0.05))) * Decimal(16.49)
            category_id = i['categoryId']  # categoryId
            p_shipping = Decimal(i['priceInfo']['item'][1]['priceValue']).quantize(
                Decimal('.00'))  # Price with shipping
            shipping_cost = p_shipping - price_pl
            leftCount = i['leftCount']  # Товарный остаток
            bidsCount = i['bidsCount']  # Количество продаж
            biddersCount = i['biddersCount']  # Количество покупателей
            itemTitle = i['itemTitle']  # Заголовок
            slug = slugify(itemTitle) + '-' + (str(sku))

            product = Product()
            product.slug = slug
            product.sku = sku
            product.price_pl = price_pl
            product.price_ru = price_ru
            try:
                product.category_id = Category.objects.get(catId=int(category_id))
            except:
                continue
            product.name_poland = itemTitle
            product.condition = condition
            product.buyers_count = biddersCount
            product.general_image = general_image
            product.general_image_sm = general_image_sm
            product.product_count = leftCount
            product.sales_count = bidsCount
            product.shipping_price_pl = shipping_cost
            product.is_disabled = 0
            product.save()
            for photo in photos[:-1]:
                product.secondary_image.create(image=photo)

    def make_all(self, cat):
        products = self.connect(cat)
        self.get_items(products)


    def handle(self, *args, **options):
        cat_list = []
        cats = Category.objects.all().exclude(catId=0).distinct()
        for i in cats:
            cat_list.append(str(i.catId))
        pool = ThreadPool(6)
        pool.map(self.make_all, cat_list)
        pool.close()
        pool.join()
  • Вопрос задан
  • 408 просмотров
Пригласить эксперта
Ответы на вопрос 2
@Sovetnikov
технический директор pulsprodaj.ru
1. Про нехватку памяти проверьте сначала, запутить скрипт и в консоле запустите top - наблюдайте как растёт память.
2. Если у вас 5млн записей, то вы принципиально неправильно сделали работу. Вы зачитываете в память сразу всё, хотя ваша единичная операция требует всего одного продукта.
Пишите код правильно, а не делайте костыли, которые восстанавливают работу после того как что-то у вас отвалилось.
3. Вы поулчаете данные по 1000 позиций в connect ... встройте туда же вашу обработку для Product, тупо копипастом хотя бы - это решит проблемы с памятью, если проблема именно в этом.
Обрбатывайте по 1000 позиций за раз.

Ещё поясню:
- когда заканчивается память - это нештатная ситуация для всей системы и для СУБД в первую очередь, есть вероятность потерять данные
- сохранять состояние offset для client.service.doGetItemsList тоже хорошая идея для случаев обрыва связи, но это только если она у вас рвётся
Ответ написан
@vikholodov Автор вопроса
Последовал совету sim3x и записал все в файл, достаточно быстро все выгрузилось, получился монстр весом 3.2 гб) Осталось в базу только все это залить
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы