Python 从流中生成 JSON 文档

3 投票
2 回答
2030 浏览
提问于 2025-04-18 00:50

我有一个REST API(RavenDB的查询流),它返回了很多很多的数据,格式是JSON。一次性加载到内存里并解析这些数据太多了。

问题是,它返回的数据不是“每行一个文档”,这样会很简单,而是把所有文档放在一个叫“Results”的字段里,变成了一个字符串,格式如下:

{"Results":[
  {"Name":"Hello World"}
]}

我其实想用Python的requests库来像这样流式获取响应:

r = requests.get('.../streams/query/Raven/DocumentsByEntityName?query=', stream=True)
for chunk in r.iter_content(chunk_size=512, decode_unicode=False):
    print chunk

但我希望能一个一个地处理JSON文档,这样就不需要解析整个响应。有什么高效的方法可以一次只处理一个JSON文档吗?

2 个回答

0

我现在的做法是匹配大括号({}),这样我就可以输出每一行的内部JSON文档(可以参考一下:JSON Lines)。

这样做的好处是我可以把输出流式传输到一个文本文件中,之后可以逐行解码,而不需要一次性把整个内容都加载到内存里。

如果有任何建议或优化的办法,我非常欢迎!

def yield_stream(url1 = '/streams/query/Raven/DocumentsByEntityName?query=', query1=''):
    r = requests.get(conf.db + url1 + query1, auth=conf.db_auth, stream=True)
    i = 0
    is_doc = False
    is_str = False
    doc1 = []
    for chunk in r.iter_content(chunk_size=1024, decode_unicode=True):
        for char in chunk:
            if is_doc:
                doc1.append(char)

            if doc1[-2:-1] != ['\\'] and doc1[-1:] == ['"']:
                is_str = not is_str

            if char == '{' and not is_str: 
                i += 1
                if i == 2:
                    doc1.append(char)
                    is_doc = True

            if char == '}' and not is_str: 
                i -= 1
                if i == 1:
                    yield ''.join(doc1)
                    doc1 = []
                    is_doc = False
1

json.load() 这个函数有一个可选的参数叫 object_pairs_hook,你可以试着用一下。这个参数的意思是,在处理每个内部的 dict 时,可以通过一个回调函数来捕捉它们,回调函数可以返回一个空的字典(或者返回 None),这样就能避免在内存中构建一个巨大的数据结构。

不过要注意,这并不是为了提高性能:在我的测试中(使用 import simplejson as json),我发现虽然可以节省内存,但使用这个钩子来检查每个元素反而让解析速度慢了好几倍。不过,如果你内存不够,这总比什么都不做要好。

撰写回答