Есть ли эффективный и быстрый способ памяти для загрузки больших файлов JSON в Python? - PullRequest
53 голосов
/ 08 марта 2010

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

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

Есть предложения? Спасибо

Ответы [ 8 ]

69 голосов
/ 26 июня 2013

Был дубликат на этот вопрос, у которого был лучший ответ. См. https://stackoverflow.com/a/10382359/1623645,, который предлагает 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, чтобы найти то, что я искал.

14 голосов
/ 08 марта 2010

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

  1. Модульный код. Сделайте что-то вроде:

    for json_file in list_of_files:
        process_file(json_file)
    

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

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

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

8 голосов
/ 19 декабря 2014

Да.

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

3 голосов
/ 09 марта 2010

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

Исправить.

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

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

Превратите весь код уровня модуля в меньшие функции.

3 голосов
/ 08 марта 2010

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

2 голосов
/ 08 марта 2010

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

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

http://www.mongodb.org/

1 голос
/ 08 марта 2010

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

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

0 голосов
/ 08 марта 2010

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

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

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

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

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

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

...