Есть ли эффективный и быстрый способ загрузки больших json файлов в python?
У меня есть json файлы с 500 МБ.
Если я использую "тривиальный" json.load для загрузки своего контента сразу, он будет потреблять много памяти.
Есть ли способ частично прочитать файл? Если бы это был текст, файл с разделителями строк, я мог бы перебирать строки. Я ищу аналогию с ним.
Любые предложения?
Благодаря
Ответы
Ответ 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, чей тип планшета.