使用 JSON 对象解析和处理大文件的更有效方法

Posted

技术标签:

【中文标题】使用 JSON 对象解析和处理大文件的更有效方法【英文标题】:Much more efficient way to parse and process large files with JSON Objects 【发布时间】:2014-07-10 21:42:05 【问题描述】:

如何优化下面的代码?(Python 可以做到这一点还是我应该使用其他工具?)

这是迄今为止我在 SO 上提出的最疯狂的问题,但我将试一试,希望获得一些关于我是否利用正确的工具和方法来有效处理大量数据的建议.我不一定要在优化我的代码方面寻求帮助,除非我完全忽略了某些东西,但本质上只是想知道我是否应该一起使用不同的框架而不是 Python。我对 Python 还很陌生,不能完全确定是否可以更有效地处理大量数据并存储到数据库中。

下面的实现读取目录中的文本文件:

每个文本文件包含 50K 行 JSON 对象... 在加载到数据库之前需要解析和读取,然后转换为 CSV。 我讨厌使用列表容器,我希望我可以研究在 Python 中实现的其他东西,以便以更好的方式执行此操作。我最初的想法是我应该使用生成器,但并不完全确定。 末尾疯狂的 concat 部分很重要,因为它将逗号分隔的列表转换为自己的行。 Converting Column with string separated values into rows

代码:

  triggerZipFiles = glob.glob('*.zip')
  for triggerFiles in triggerZipFiles:
      with zipfile.ZipFile(triggerFiles, 'r') as myzip:
          for logfile in myzip.namelist():
              datacc = []
              zipcc = []
              csvout = '_US.csv'.format(logfile[:-4])
              f = myzip.open(logfile)
              contents = f.readlines()
              for line in contents:
                  try:
                      parsed = json.loads(line[:-2])
                      if "CC" in parsed['data']['weatherType'] and "US" in parsed['zipcodes']:
                          datacc.append(parsed['data'])
                          zipcc.append(parsed['zipcodes'])
                  except:
                      pass
              if len(datacc) > 0:
                  df = pd.concat([pd.DataFrame(zipcc), pd.DataFrame(datacc)], axis=1)
                  df = pd.concat((pd.Series((v, row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key'], row['key'], row['key'],
                    row['key'], row['key'], row['key'], row['key']), df.columns) for _,
                      row in df.iterrows() for v in row['US']), axis=1).T                
                  df.to_csv(csvout, header=None, index=False)
              else:
                  pass
              print datetime.now().strftime('%Y/%m/%d %H:%M:%S') + ": Finished: ".format(logfile)

【问题讨论】:

一个很容易解决的低效问题:测试x in a_dict.keys() 首先创建一个包含所有键的列表,然后在该列表中对x 进行线性 搜索。直接在dict 上测试包含不会创建额外的列表并且具有恒定的运行时间。我还将三个if 语句与and 合并为一个。 非常感谢。您能否提供一个小示例,说明直接在 dict 上进行测试包含的样子? 'key36/62/65' 在那个奇怪的 concat 构造中各出现两次。这是故意的吗? @prometheus2305 只需x in a_dict 而不是x in a_dict.keys() 感谢您的所有帮助。我已经用我的最终实现更新了代码,这比我最初的实现要快。肯定想测试 pandas json 库,感谢@AndyHayden,看看它可能会快多少。 【参考方案1】:

首先,对于 json,lines 并不是一个特别有用的指标! 其次,您的想法是正确的:您肯定想要基于块(分别读取/清理/转储每个部分)。

我建议使用 pandas 的 read_json 函数,它在创建 DataFrames 时效率更高(它不会创建临时 python dict),请参阅 reading in json section of the docs。

如果实际上没有 json,那么进行字符串操作以使它们成为 json 通常是你最好的选择。* 如果你有“奇形怪状”的 json,那么你可以在读取时json_normalize,或者在读取 DataFrame 后解析包含多列的列(例如使用 Series 字符串方法或应用)。

*不清楚实际的格式是什么,但通常不需要太多就可以将它们变成valid json。

Python 额外提示:如果您的缩进级别更深,请考虑将其拆分为更多函数。 (这里显而易见的选择是使用f1(logfile)f2(line),但使用描述性名称...)

【讨论】:

以上是关于使用 JSON 对象解析和处理大文件的更有效方法的主要内容,如果未能解决你的问题,请参考以下文章

在 Nodejs 中解析大型 JSON 文件并独立处理每个对象

Node大文件处理

遍历非常大的 JSON 数组

使用 ftp:/// 加载大型 json 对象的有效方法

如何在目标c中逐行解析JSON文件

JSON解析错误,出现双引号,反斜杠处理办法