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

dud*_*ein 56 python json large-files

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

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

有什么建议?谢谢

Jim*_*ski 73

这个问题的副本有一个更好的答案.请参阅/sf/answers/726765161/,其中建议使用ijson.

更新:

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

import ijson
for prefix, the_type, value in ijson.parse(open(json_file_name)):
    print prefix, the_type, value
Run Code Online (Sandbox Code Playgroud)

这里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我正在寻找的东西.

  • 我发现这不仅是对这个问题的最佳回应,而且是我在谷歌搜索后发现的最有用的ijson介绍.感谢您花时间浏览稀疏文档并简单明了地展示其基本功能. (10认同)
  • 不错的链接.还有另一个ijson功能 - 生成器在JSON数据中的给定位置生成字典.执行时间与其它解决方案相比,ijson是相当缓慢的(57号与STDLIB JSON),但它是优秀的,如果你需要保持内存消耗低(13 MB与STDLIB JSON 439 MB).使用yajl2后端,它并不快,但内存消耗下降到5 MB.测试了3个文件,每个文件大约30 MB,有30万条记录. (3认同)

jcd*_*yer 14

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

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

    for json_file in list_of_files:
        process_file(json_file)
    
    Run Code Online (Sandbox Code Playgroud)

    如果您process_file()以不依赖于任何全局状态的方式编写,并且不更改任何全局状态,则垃圾收集器应该能够完成其工作.

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

希望这可以帮助.


kas*_*hif 8

是.

您可以使用我编写的类似jsonstreamer SAX的推送解析器,它允许您解析任意大小的块,您可以在此处获取它并查看自述文件以获取示例.它很快,因为它使用'C'yajl库.


小智 5

可以通过使用ijson来完成。Jim Pivarski在上面的答案中很好地解释了 ijson 的工作原理。下面的代码将读取一个文件并打印列表中的每个 json。例如文件内容如下

[{"name": "rantidine",  "drug": {"type": "tablet", "content_type": "solid"}},
{"name": "nicip",  "drug": {"type": "capsule", "content_type": "solid"}}]
Run Code Online (Sandbox Code Playgroud)

您可以使用以下方法打印数组的每个元素

 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)
Run Code Online (Sandbox Code Playgroud)

注意:'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)
Run Code Online (Sandbox Code Playgroud)

这将只打印那些类型为tablet的json。


cod*_*ape 3

更新

请参阅其他答案以获取建议。

2010年的原始答案,现已过时

简短的回答:不。

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

但是,如果您具备这些知识,那么您可以实现一个类似文件的对象,该对象包装 json 文件并吐出适当的块。

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

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

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

  • 不幸的是,这个答案已被接受,因为存在现有且正在运行的 Python 增量 json 解析器...... (3认同)