Подтвердить что ты не робот

Есть ли эффективный и быстрый способ загрузки больших json файлов в python?

У меня есть json файлы с 500 МБ. Если я использую "тривиальный" json.load для загрузки своего контента сразу, он будет потреблять много памяти.

Есть ли способ частично прочитать файл? Если бы это был текст, файл с разделителями строк, я мог бы перебирать строки. Я ищу аналогию с ним.

Любые предложения? Благодаря

4b9b3361

Ответ 1

Короткий ответ: нет.

Правильное разделение json файла будет иметь непосредственное знание графа объектов json, чтобы получить право.

Однако, если у вас есть это знание, вы можете реализовать файл-подобный объект, который обертывает json файл и выплевывает правильные куски.

Например, если вы знаете, что ваш json файл является единственным массивом объектов, вы можете создать генератор, который обертывает json файл и возвращает куски массива.

Вам нужно будет провести синтаксический анализ строкового содержимого, чтобы получить правку json файла.

Я не знаю, что генерирует ваш json-контент. Если возможно, я бы хотел создать несколько управляемых файлов вместо одного огромного файла.

Ответ 2

Был дубликат на этот вопрос, у которого был лучший ответ. См. fooobar.com/questions/106239/..., где предлагается ijson.

Обновление:

Я попробовал это, и ijson для JSON - это то же самое, что SAX для XML. Например, вы можете сделать это:

import ijson
for prefix, the_type, value in ijson.parse(open(json_file_name)):
    print prefix, the_type, value

где prefix - это разделенный точками индекс в дереве JSON (что произойдет, если в именах ваших ключей есть точки? Полагаю, это тоже будет плохо для Javascript...), theType описывает SAX -подобное событие, одно из 'null', 'boolean', 'number', 'string', 'map_key', 'start_map', 'end_map', 'start_array', 'end_array' и value - это значение объекта или None, если the_type - это событие, подобное началу/окончанию карты/массива.

В проекте есть несколько строк документации, но недостаточно глобальной документации. Мне пришлось копаться в ijson/common.py, чтобы найти то, что я искал.

Ответ 3

Таким образом, проблема заключается не в том, что каждый файл слишком велик, но их слишком много, и они, кажется, складываются в памяти. Сборщик мусора Python должен быть прекрасным, если вы не держите ссылки, которые вам не нужны. Трудно точно сказать, что происходит без какой-либо дополнительной информации, но некоторые вещи, которые вы можете попробовать:

  • Модулизуйте свой код. Сделайте что-то вроде:

    for json_file in list_of_files:
        process_file(json_file)
    

    Если вы пишете process_file() таким образом, чтобы он не полагался на какое-либо глобальное состояние и не изменить любое глобальное состояние, сборщик мусора должен иметь возможность выполнять свою работу.

  • Сделка с каждым файлом в отдельном процессе. Вместо того, чтобы разобрать все файлы JSON сразу, напишите программа, которая анализирует только одну и передает каждую из них из оболочки script или из другого питона который вызывает ваш script через subprocess.Popen. Это немного менее изящно, но если ничто другое не работает, оно гарантирует, что вы не будете удерживать устаревшие данные из одного файла в следующий.

Надеюсь, что это поможет.

Ответ 4

Да.

Вы можете использовать jsonstreamer SAX-подобный синтаксический анализатор, который я написал, который позволит вам разбирать произвольные размерные фрагменты, вы можете получить его здесь и проверить README для примеров. Его быстро, потому что он использует библиотеку 'C' yajl.

Ответ 5

При упоминании об исчерпании памяти я должен задать вопрос, действительно ли вы управляете памятью. Используете ли вы ключевое слово "del", чтобы удалить старый объект, прежде чем пытаться его прочитать? Python никогда не должен сохранять в памяти что-то в памяти, если вы его удалите.

Ответ 6

"сборщик мусора должен освободить память"

Правильно.

Так как это не так, что-то еще не так. Как правило, проблема с бесконечным ростом памяти - это глобальные переменные.

Удалите все глобальные переменные.

Сделать код модуля на более мелкие.

Ответ 7

Другая идея - попробовать загрузить ее в базу данных хранилища документов, такую ​​как MongoDB. Он имеет дело с большими блоками JSON. Хотя вы можете столкнуться с одной и той же проблемой при загрузке JSON - избегайте проблем, загружая файлы по одному.

Если путь работает для вас, то вы можете взаимодействовать с данными JSON через своего клиента и, возможно, не должны удерживать весь блок памяти в памяти

http://www.mongodb.org/

Ответ 8

в дополнение к @codeape

Я бы попробовал написать пользовательский json-парсер, чтобы помочь вам разобраться в структуре блога JSON, с которым вы имеете дело. Распечатайте только имена ключей и т.д. Сделайте иерархическое дерево и решите (самостоятельно), как вы можете его обрезать. Таким образом, вы можете делать то, что предлагает @codeape - разбить файл на более мелкие куски и т.д.

Ответ 9

Это можно сделать с помощью ijson. Работа ijson была очень хорошо объяснена Джимом Пиварски в ответе выше. Код ниже будет читать файл и печатать каждый JSON из списка. Например, содержимое файла показано ниже

[{"name": "rantidine",  "drug": {"type": "tablet", "content_type": "solid"}},
{"name": "nicip",  "drug": {"type": "capsule", "content_type": "solid"}}]

Вы можете распечатать каждый элемент массива, используя метод ниже

 def extract_json(filename):
      with open(filename, 'rb') as input_file:
          jsonobj = ijson.items(input_file, 'item')
          jsons = (o for o in jsonobj)
          for j in jsons:
             print(j)

Примечание. 'item' - это префикс по умолчанию, заданный ijson.

если вы хотите получить доступ только к определенному json в зависимости от условия, вы можете сделать это следующим образом.

def extract_tabtype(filename):
    with open(filename, 'rb') as input_file:
        objects = ijson.items(input_file, 'item.drugs')
        tabtype = (o for o in objects if o['type'] == 'tablet')
        for prop in tabtype:
            print(prop)

Это будет печатать только те JSON, чей тип планшета.