有没有一种内存高效快速的方法来加载python中的大json文件?

时间:2010-03-08 10:34:15

标签: python json large-files

我有一些500MB的json文件。 如果我使用“trivial”json.load一次加载其内容,它将消耗大量内存。

有没有办法部分阅读文件?如果它是一个文本,行分隔文件,我将能够遍历这些行。我正在寻找它的类比。

有什么建议吗? 感谢

11 个答案:

答案 0 :(得分:68)

这个问题的副本有更好的答案。请参阅https://stackoverflow.com/a/10382359/1623645,其中建议ijson

<强>更新

我试了一下,ijson是JSON对XML的SAX。例如,您可以这样做:

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

其中prefix是JSON树中以点分隔的索引(如果你的键名中有点,会发生什么?我想这对Javascript也不好......),{{1} }描述类似SAX的事件,theType之一,'null', 'boolean', 'number', 'string', 'map_key', 'start_map', 'end_map', 'start_array', 'end_array'是对象的值,如果value是一个事件,如开始/结束地图/ None阵列。

该项目有一些文档字符串,但没有足够的全局文档。我不得不深入the_type找到我想要的东西。

答案 1 :(得分:14)

所以问题不是每个文件都太大,而是它们太多了,而且它们似乎在内存中加起来。 Python的垃圾收集器应该没问题,除非你保留你不需要的引用。如果没有任何进一步的信息,很难确切地说出发生了什么,但有些事情可以尝试:

  1. 模块化您的代码。做类似的事情:

    for json_file in list_of_files:
        process_file(json_file)
    

    如果以不依赖于任何全局状态的方式编写process_file(),则不会 改变任何全局状态,垃圾收集器应该能够完成它的工作。

  2. 在单独的流程中处理每个文件。不要一次解析所有JSON文件,而是写一个 程序只解析一个,并从shell脚本或另一个python传递每个 通过subprocess.Popen调用脚本的过程。这有点不太优雅,但如果 没有其他工作,它将确保你没有坚持从一个文件到陈旧的数据 下。

  3. 希望这有帮助。

答案 2 :(得分:8)

您可以使用我编写的 jsonstreamer 类似SAX的推送解析器,它允许您解析任意大小的块,您可以get it here并查看README以获取示例。它很快,因为它使用了&#39; C&#39; yajl图书馆。

答案 3 :(得分:3)

在提到内存不足时,我必须质疑你是否真的在管理内存。在尝试阅读新对象之前,您是否使用“del”关键字删除旧对象?如果你删除它,Python应该永远不会在内存中保留一些内容。

答案 4 :(得分:3)

“垃圾收集器应释放内存”

正确。

既然没有,那就别错了。通常,无限内存增长的问题是全局变量。

删除所有全局变量。

将所有模块级代码转换为更小的函数。

答案 5 :(得分:2)

另一个想法是尝试将其加载到像MongoDB这样的文档存储数据库中。 它很好地处理了大量的JSON。虽然加载JSON可能遇到同样的问题 - 通过一次加载一个文件来避免问题。

如果path适合你,那么你可以通过他们的客户端与JSON数据交互,并且可能不必将整个blob保存在内存中

http://www.mongodb.org/

答案 6 :(得分:1)

除了@codeape

我会尝试编写一个自定义的json解析器来帮助你弄清楚你正在处理的JSON blob的结构。仅打印出关键名称等。制作一个分层树并决定(自己)如何将其分块。这样你就可以做@codeape建议 - 将文件分成更小的块等等

答案 7 :(得分:1)

可以使用ijson完成。 Jim Pivarski在上面的答案中已经很好地解释了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。

答案 8 :(得分:0)

您可以将 JSON 文件解析为 CSV 文件并逐行解析:

Person::addSelect('last_visit_id', Visit::select('id')
    ->whereColumn('person_id', 'persons.id')
    ->latest()
    ->limit(1)
)
->where('last_visit_id', 1)
->get();

答案 9 :(得分:0)

所以简单地使用 json.load() 会花费很多时间。相反,您可以使用键值对将 json 数据逐行加载到字典中,并将该字典附加到最终字典中,并将其转换为 Pandas DataFrame,这将有助于您进一步分析

def get_data():
    with open('Your_json_file_name', 'r') as f:
        for line in f:
            yield line


data = get_data()
data_dict = {}
each = {}


for line in data:
    each = {}
     # k and v are the key and value pair 
    for k, v in json.loads(line).items():
        #print(f'{k}: {v}')
        each[f'{k}'] = f'{v}' 
    data_dict[i] = each
Data = pd.DataFrame(data_dict)
#Data will give you the dictionary data in dataFrame (table format) but it will 
 #be in transposed form , so will then finally transpose the dataframe as ->
Data_1 = Data.T 

答案 10 :(得分:-1)

简答:不。

正确划分json文件会对json对象图有深入了解才能正确。

但是,如果你有这方面的知识,那么你可以实现一个类似文件的对象,它包装json文件并吐出适当的块。

例如,如果您知道您的json文件是单个对象数组,则可以创建一个包装json文件并返回数组块的生成器。

您必须进行一些字符串内容解析才能正确获取json文件的分块。

我不知道是什么生成了你的json内容。如果可能的话,我会考虑生成一些可管理的文件,而不是一个大文件。