Как быстро распарсить много json файлов на python?

Рейтинг: 1Ответов: 1Опубликовано: 26.08.2023

Стоит задача распарсить 8000 Json файлов, объем всего этого добра ~ 8 гигов. Нужно вытащить нужную информацию и записать в базу. На файл уходит по 2 секунды. То есть на все файлы ~5 часов. Ищу решение как сделать быстрее, учитывая, что i\o biund операция. Прошу всех неравнодушных и не душных подсказать куда копать!

Вот часть кода

for filename in os.listdir(download_dir):
    if filename.endswith('.json'):
        with open(os.path.join(download_dir, filename), 'r', encoding='utf-8') as j_f:
            try:
                data = json.load(j_f)
                companies_to_create = []
                for d in data:
                    if isinstance(d, dict):
                        inn = d.get('inn', '')
                        kpp = d.get('kpp', '')
                        name = d.get('name', '')
                        address_ul = d.get('data', {}).get('СвРегОрг', {}).get('АдрРО', '')
                        sv_okved = d.get('data', {}).get('СвОКВЭД', {})
                        if sv_okved:
                            sv_okved_dop = sv_okved.get('СвОКВЭДДоп', [])
                            for sv in sv_okved_dop:
                                if isinstance(sv, dict) and sv.get('КодОКВЭД', '').startswith('96'):
                                    okved = sv.get('КодОКВЭД', '')
                                    if not "Хабаровск" in address_ul:
                                        existing_company = Company.objects.filter(inn=inn).first()
                                        if not existing_company:
                                            company = Company(
                                                name=name,
                                                okved=okved,
                                                inn=inn,
                                                kpp=kpp,
                                                address_ul=address_ul,
                                            )
                                            companies_to_create.append(company)
                Company.objects.bulk_create(companies_to_create)

Ответы

▲ 2Принят

Давайте начнем с уточнения задачи. Если я правильно понял, то Вам нужно выбрать все компании зарегистрированные не в Хабаровске и имеющие дополнительные ОКВЭД начинающиеся на 96 (при этом сами ОКВЭД не нужны)

Если ошибся в формулировке задачи, поправьте, что-то придумаем

Самая главная, на мой взгляд, ошибка заключается в том, что Вы для каждого найденного ДопОКВЭД начинающегося с 96 пытаетесь создать запись, проверяя не создавалась ли она ранее.

Предлагаю несколько изменить такой порядок:

pip install pydantic --upgrade

import json
from pydantic import BaseModel, TypeAdapter
# В этом примере используется pydantic >= 2.0
# Для более ранних версий код будет слегка отличаться


# В данном случае я искусственно увеличил размер набора данных, чтобы воспроизвести Вашу ситуацию. 

with open(r'C:\Users\karpo\Downloads\00001.json', encoding='utf-8') as f:
    data = json.load(f) * 8000

# Тем не менее я предлагаю Вам прежде чем пытаться записывать 
# данные в БД собрать данные в одну переменную и подготовить данные для записи.

class Firm(BaseModel):
    inn: str = ''
    kpp: str = ''
    name: str = ''
    address: str = ''

    # Добавление этих методов даст возможность создать множество
    # (объект типа set) подобных объектов
    def __hash__(self):
        return hash(self.inn)
        # В данном случае объекты будут считаться идентичными если их ИНН равны

    def __eq__(self, other):
        return self.__hash__() == other.__hash__()

# Создадим функцию которая будет возвращать нам генератор только нужных нам элементов
def get_useful(firm_list: list[dict]):
    for firm in firm_list:
        address = ''

        try:
            if 'Хабаровск' in (address := firm['data']['СвРегОрг']['АдрРО']):
                continue
        except KeyError:
            pass

        for item in firm['data'].get('СвОКВЭД', dict()).get('СвОКВЭДДоп', []):
            if isinstance(item, dict) and item['КодОКВЭД'].startswith('96'):
                
                yield Firm.model_validate(
                    firm | {
                        'address': address
                    }
                )
                break

# Далее создадим множество подходящих записей и преобразуем его в кортеж данных для записи в БД

data_to_db = TypeAdapter(tuple[Firm]).dump_python(tuple(set(get_useful(data)))) 


-- так как структуру БД вы не предоставили, пришлось пофантазировать

create table okved_test (
    inn varchar primary key ,
    kpp varchar,
    name text,
    address text
);


import psycopg2 as pg
from psycopg2.extras import execute_values

with pg.connect('<connection string>') as conn, conn.cursor() as cur:
    execute_values(
        cur,
        """
        insert into okved_test (inn, kpp, name, address) values %s on conflict do nothing 
        """,
        data_to_db,
        template="""(
        %(inn)s,
        %(kpp)s,
        %(name)s,
        %(address)s
        )""",
        page_size=10000
    )
    conn.commit()

Вот что получилось из Вашего json

inn kpp name address
0103007142 010101001 ООО "КАСКАД" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0104007787 010401001 ТУЛЬСКОЕ ПО 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
1643000792 164301001 КП АЗНАКАЕВСКОГО РПУ БОН ,420054,,, Казань г,, Владимира Кулагина ул, 1,,
0105037625 010501001 ООО "МОНОЛИТ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105012701 010501001 АО "МАЙКОПНОРМАЛЬ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105034293 010501001 ООО "АНИТ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105035875 231201001 ООО "МАЙКОПГИПССТРОЙ" ,350020,,, Краснодар г,, Коммунаров ул, д 235,,
0103006420 010101001 ООО "АНТЕЯ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105005341 010501001 ООО "НАРТ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105031260 010501001 ООО "МИР ЦЕНТР" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105004595 010501001 ТПП РА 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105029550 010501001 ООО "ЮГ-КОМПЛЕКТ" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.
0105027730 010501001 ООО ФИРМА "ЦЕНТР" 385009,Республика Адыгея,город Майкоп, ул. Привокзальная, 331.