在Python中打开一个大型JSON文件

时间:2012-05-23 07:49:23

标签: python json nltk

当我尝试使用json.load()打开时,我有一个1.7 GB的JSON文件然后它会给出内存错误,怎么能在python中读取json文件?

我的JSON文件是包含特定键的大量对象。

编辑:如果它只是一个大的对象数组,并且事先知道对象的结构,那么就不需要使用我们可以逐行读取的工具。一行只包含数组的一个元素。我注意到这是json文件的存储方式,对我来说它只是

>>>for line in open('file.json','r').readline():
...    do something with(line) 

5 个答案:

答案 0 :(得分:10)

你想要一个增量的json解析器,比如yajl和它的一个python绑定。增量解析器从输入中尽可能少地读取,并在解码有意义的内容时调用回调。例如,仅从大型json文件中提取数字:

class ContentHandler(YajlContentHandler):
    def yajl_number(self, ctx, val):
         list_of_numbers.append(float(val))

parser = YajlParser(ContentHandler())
parser.parse(some_file)

有关详细信息,请参阅http://pykler.github.com/yajl-py/

答案 1 :(得分:1)

我找到了另一个围绕 yajl 库的python包装器,它是ijson

由于以下原因,它比yajl-py更适合我:

  • yajl-py没有在我的系统上检测到yajl库,我不得不破解代码以使其正常工作
  • ijson代码更紧凑,更易于使用
  • ijson可以同时使用yajl v1和yajl v2,甚至还有纯python yajl替换
  • ijson有非常好的ObjectBuilder,它不仅可以帮助提取事件,还可以从已解析的流中提取有意义的子对象,并在指定的级别提取

答案 2 :(得分:0)

当从本地磁盘访问大型数据文件时,我发现yajl(因此ijson)比模块json慢得多。当与Cython一起使用时,这是一个声称比yajl / ijson(仍然慢于json)表现更好的模块:

http://pietrobattiston.it/jsaone

正如作者所指出的,当通过网络接收文件时,性能可能优于json,因为增量解析器可以更快地开始解析。

答案 3 :(得分:0)

对于简单用途(即遍历顶层数组中的项目),json-stream-parser看起来不错(我没有使用过)。它似乎是一个从234行纯Python重新实现的独立JSON解析器。

它不需要将JSON存储为“每行一个对象”或类似的东西。 JSON可以全是一行,也可以有换行符。

用法:

import sys
from json_stream_parser import load_iter
for obj in load_iter(sys.stdin):
    print(obj)

答案 4 :(得分:0)

我已将Dask用于大型遥测JSON-Lines文件(以换行符分隔)...
Dask的好处是可以为您完成很多工作。
有了它,您可以读取,处理和写入磁盘,而无需全部读取到内存中。
Dask还将为您并行化并使用多个内核(线程)...

有关Dask袋的更多信息,请点击此处:
https://examples.dask.org/bag.html

import ujson as json #ujson for speed and handling NaNs which are not covered by JSON spec
import dask.bag as db

def update_dict(d):
    d.update({'new_key':'new_value', 'a':1, 'b':2, 'c':0})
    d['c'] = d['a'] + d['b']
    return d

def read_jsonl(filepaths):
    """Read's a JSON-L file with a Dask Bag

    :param filepaths: list of filepath strings OR a string with wildcard
    :returns: a dask bag of dictionaries, each dict a JSON object
    """
    return db.read_text(filepaths).map(json.loads)



filepaths = ['file1.jsonl.gz','file2.jsonl.gz']
#OR
filepaths = 'file*.jsonl.gz' #wildcard to match multiple files

#(optional) if you want Dask to use multiple processes instead of threads
# from dask.distributed import Client, progress
# client = Client(threads_per_worker=1, n_workers=6) #6 workers for 6 cores
# print(client)

#define bag containing our data with the JSON parser
dask_bag = read_jsonl(filepaths)

#modify our data
#note, this doesn't execute, it just adds it to a queue of tasks
dask_bag.map(update_dict)

#(optional) if you're only reading one huge file but want to split the data into multiple files you can use repartition on the bag
# dask_bag = dask_bag.repartition(10)

#write our modified data back to disk, this is when Dask actually performs execution
dask_bag.map(json.dumps).to_textfiles('file_mod*.jsonl.gz') #dask will automatically apply compression if you use .gz