【问题标题】:How to use ijson/other to parse this large JSON file?如何使用 ijson/other 来解析这个大的 JSON 文件?
【发布时间】:2017-12-12 19:21:10
【问题描述】:

我有这个庞大的 json 文件 (8gb),但在尝试将其读入 Python 时内存不足。我将如何使用 ijson 或其他一些对大型 json 文件更有效的库来实现类似的过程?

import pandas as pd

#There are (say) 1m objects - each is its json object - within in this file. 
with open('my_file.json') as json_file:      
    data = json_file.readlines()
    #So I take a list of these json objects
    list_of_objs = [obj for obj in data]

#But I only want about 200 of the json objects
desired_data = [obj for obj in list_of_objs if object['feature']=="desired_feature"]

我将如何使用 ijson 或类似的东西来实现它?有没有一种方法可以在不读取整个 JSON 文件的情况下提取我想要的对象?

该文件是一个对象列表,例如:

{
    "review_id": "zdSx_SD6obEhz9VrW9uAWA",
    "user_id": "Ha3iJu77CxlrFm-vQRs_8g",
    "business_id": "tnhfDv5Il8EaGSXZGiuQGg",
    "stars": 4,
    "date": "2016-03-09",
    "text": "Great place to hang out after work: the prices are decent, and the ambience is fun. It's a bit loud, but very lively. The staff is friendly, and the food is good. They have a good selection of drinks.",
    "useful": 0,
    "funny": 0,
}

【问题讨论】:

    标签: json python-3.x data-structures ijson


    【解决方案1】:

    文件是对象列表

    这有点模棱两可。查看您的代码 sn-p 看起来您的文件在每一行都包含单独的 JSON 对象。这与以[ 开头、以] 结尾并在项目之间有, 的实际JSON 数组不同。

    对于 json-per-line 文件,它很简单:

    import json
    from itertools import islice
    
    with(open(filename)) as f:
        objects = (json.loads(line) for line in f)
        objects = islice(objects, 200)
    

    注意区别:

    • 你不需要.readlines(),文件对象本身就是一个迭代器,它会产生单独的行
    • 括号 (..) 而不是 (... for line in f) 中的括号 [..] 在内存中创建一个惰性生成器表达式,而不是包含所有行的 Python 列表
    • islice(objects, 200) 将为您提供前 200 个项目,而无需进一步迭代。如果objects 是一个列表,你可以做objects[:200]

    现在,如果您的文件实际上是 JSON 数组,那么您确实需要 ijson:

    import ijson  # or choose a faster backend if needed
    from itertools import islice
    
    with open(filename) as f:
        objects = ijson.items(f, 'item')
        objects = islice(objects, 200)
    

    ijson.items 在已解析数组上返回一个惰性迭代器。第二个参数中的'item' 表示“顶级数组中的每一项”。

    【讨论】:

      【解决方案2】:

      问题在于,并非所有 JSON 格式都很好,您不能依赖逐行解析来提取对象。 我将您的“接受标准”理解为“只想收集那些指定键包含指定值的 JSON 对象”。例如,仅在某人的名字是“Bob”时才收集有关该人的对象。以下函数将提供符合您条件的所有对象的列表。解析是逐个字符完成的(这在 C 中效率更高,但 Python 仍然相当不错)。这应该更健壮,因为它不关心换行符、格式化等。我在格式化和未格式化的 JSON 上测试了这个,包含 1,000,000 个对象。

      import json
      
      def parse_out_objects(file, feature, desired_value):
          with open(file) as f:
              compose_object_flag = False
              ignore_characters_flag = False
              object_string = ''
              selected_objects = []
              json_object = None
              while True:
                  c = f.read(1)
                  if c == '"':
                      ignore_characters_flag = not ignore_characters_flag
                  if c == '{' and ignore_characters_flag == False:
                      compose_object_flag = True
                  if c == '}' and compose_object_flag == True and ignore_characters_flag == False:
                      compose_object_flag = False
                      object_string = object_string + '}'
                      json_object = json.loads(object_string)
                      if json_object[feature] == desired_value:
                          selected_objects.append(json_object)
                      object_string = ''
                  if compose_object_flag == True:
                      object_string = object_string + c
                  if not c:
                      break
              return selected_objects
      

      【讨论】:

      • 这可能适用于简单的情况,但如果你有嵌套的对象或包含} 的字符串,它就会中断。你真的需要一个真正的解析器。
      • @isagalaev 是的,我知道这不是生产代码,但它直接解决了发布者的问题,没有做太多假设。如您所见,这也有点“从头开始”。我当然可以用逻辑来处理嵌套和 } 。
      • … 或者你可以使用标题中提到的专门为它设计的海报的解析器库:-)
      • @isagalaev 但是它没有单元测试,所以也不能保证你的代码不会阻塞像 {"8GBjson": {8gb of JSON here}} 这样的 JSON。既然你写了 ijson,我会推荐彻底的单元测试。
      • 修复了包含'}'的字符串的中断
      猜你喜欢
      • 2019-04-07
      • 2013-11-28
      • 2018-08-14
      • 2016-03-16
      • 2021-09-08
      • 2016-09-08
      • 1970-01-01
      • 1970-01-01
      • 2021-10-09
      相关资源
      最近更新 更多