0

我有数千个非常大的 JSON 文件,需要对特定元素进行处理。为了避免内存过载,我使用了一个名为ijson的 python 库,当我只处理 json 文件中的单个元素但当我尝试一次处理多个元素时它工作正常

IncompleteJSONError:解析错误:过早的 EOF

部分 JSON:

{
  "info": {
    "added": 1631536344.112968,
    "started": 1631537322.81162,
    "duration": 14,
    "ended": 1631537337.342377
  },
  "network": {
    "domains": [
      {
        "ip": "231.90.255.25",
        "domain": "dns.msfcsi.com"
      },
      {
        "ip": "12.23.25.44",
        "domain": "teo.microsoft.com"
      },
      {
        "ip": "87.101.90.42",
        "domain": "www.msf.com"
      }
    ]
  }
}

工作代码:(打开多个文件)

my_file_list = [f for f in glob.glob("data/jsons/*.json")]
final_result = []
for filename in my_file_list:
    row = {}
    with open(filename, 'r') as f:
        info = ijson.items(f, 'info')
        for o in info:
             row['added']= float(o.get('added'))
             row['started']= float(o.get('started'))
             row['duration']= o.get('duration')
             row['ended']= float(o.get('ended'))
    
    with open(filename, 'r') as f:
        domains = ijson.items(f, 'network.domains.item')
        domain_count = 0
        for domain in domains:
            domain_count+=1
        row['domain_count'] = domain_count

失败代码:(单个文件打开)

my_file_list = [f for f in glob.glob("data/jsons/*.json")]
final_result = []
for filename in my_file_list:
    row = {}
    with open(filename, 'r') as f:
        info = ijson.items(f, 'info')
        for o in info:
             row['added']= float(o.get('added'))
             row['started']= float(o.get('started'))
             row['duration']= o.get('duration')
             row['ended']= float(o.get('ended'))
    
        domains = ijson.items(f, 'network.domains.item')
        domain_count = 0
        for domain in domains:
            domain_count+=1
        row['domain_count'] = domain_count

不确定这是 使用 python ijson 读取具有多个 json 对象的大型 json 文件的原因,而 ijson 无法同时处理多个 json 元素。

另外,让我知道任何其他 python 包或任何可以处理大尺寸 json 而没有内存问题的示例示例。

4

2 回答 2

3

我认为发生这种情况是因为您已经从文件中读取了 IO 流,您已经到了最后,并且已经在请求另一个查询。

您可以做的是在第二次查询之前将光标重置为 0 位置:

f.seek(0)

在评论中我说你也应该尝试json-stream,但这不是一个ijsonjson-stream错误,它是一个 TextIO 功能。

这相当于您再次打开文件。

如果您不想这样做,那么也许您应该查看遍历 JSON 的每个部分,然后确定每个对象是否具有infonetwork.domains.item.

于 2021-12-04T12:58:22.093 回答
1

虽然上面的答案是正确的,但您可以做得更好:如果您知道 JSON 文件的结构并且可以依赖它,那么您可以利用这一点并只读取一次文件。

ijson有一个均匀的拦截机制,那里的例子和你想要实现的非常相似。在您的情况下,您想要获取info值,然后遍历network.domains.item并计算它们。这应该这样做:

row = {}
with open(filename, 'r') as f:
    parse_events = ijson.parse(f, use_float=True)
    for prefix, event, value in parse_events:
        if prefix == 'info.added':
            row['added'] = value
        elif prefix == 'info.started':
            row['started'] = value
        elif prefix == 'info.duration':
             row['duration'] = value
        elif prefix == 'info.ended':
             row['ended'] = value
        elif prefix == 'info' and event == 'end_map':
            break
    row['domain_count'] = sum(1 for _ in ijson.items(parse_events, 'network.domains.item'))

注意如何:

  • ijson.items得到 的结果ijson.parse
  • use_float=True使您不必将值转换为float自己。
  • 可以通过sum()-ing1对来自的每个项目进行计数,ijson.items因此您不必手动循环自己。
于 2021-12-04T13:21:51.663 回答