如何从一个文件中读取多个 JSON 数据列表到 Pandas

How to read multiple lists of JSON data from one file into Pandas

我有一个脚本可以从 Twitter API 收集 JSON 数据。此脚本每分钟收集数据并用 jq 解析数据。这些数据被收集到一个文件中,最终看起来如下所示:

[
  {"text": "Tweet 01",
   "id": "001"
  },
  {"text": "Tweet 02",
   "id": "002"
  },
  {"text": "Tweet 03",
   "id": "003"
  }
]
[
  {"text": "Tweet 04",
   "id": "004"
  },
  {"text": "Tweet 05",
   "id": "005"
  },
  {"text": "Tweet 06",
   "id": "006"
  },
  {"text": "Tweet 07",
   "id": "007"
  },
  {"text": "Tweet 08",
   "id": "008"
  }
]
[
  {"text": "Tweet 09",
   "id": "009"
  },
  {"text": "Tweet 10",
   "id": "010"
  }
]

我以前每个文件都有一个 JSON 数据列表,Pandas 可以轻松地处理一个文件中的一个列表。但是,我如何有效地遍历这些不是逗号分隔且长度不一定相同的多个列表?

我的最终目标是聚合来自该文件的所有 JSON 数据并将其转换为 CSV 文件,其中每一列都是 JSON 数据中的一个键。它最终应该看起来像:

text, id
Tweet 01, 001
Tweet 02, 002
Tweet 03, 003
Tweet 04, 004
Tweet 05, 005
Tweet 06, 006
Tweet 07, 007
Tweet 08, 008
Tweet 09, 009
Tweet 10, 010

如果我仍然尝试读取文件,则会发生以下情况:

>>> import pandas as pd
>>> df = pd.read_json("sample.json")
>>> df.head()
Traceback (most recent call last):
  File "lists.py", line 3, in <module>
    df = pd.read_json("sample.json")
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/util/_decorators.py", line 214, in wrapper
    return func(*args, **kwargs)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/io/json/_json.py", line 608, in read_json
    result = json_reader.read()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/io/json/_json.py", line 731, in read
    obj = self._get_object_parser(self.data)
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/io/json/_json.py", line 753, in _get_object_parser
    obj = FrameParser(json, **kwargs).parse()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/io/json/_json.py", line 857, in parse
    self._parse_no_numpy()
  File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/pandas/io/json/_json.py", line 1089, in _parse_no_numpy
    loads(json, precise_float=self.precise_float), dtype=None
ValueError: Trailing data
  • 需要将文件内容转换为标准列表,方法是读入文件,并将各个列表转换为单个列表。
  • .readlines 将文件的每一行作为字符串列表读取
    • 使用 list comprehension to iterate through each row, to remove whitespace and newlines at the front and tail of the string, using str.strip.
  • str.join 将列表中的项目组合成一个字符串。
  • str.replace'][' 替换为 ','
  • 使用 ast.literal_eval 将字符串转换回列表。
from ast import literal_eval
import pandas as pd

# open and clean the contents of the file
with open('test.json', 'r') as f:
    data = literal_eval(''.join([row.strip() for row in f.readlines()]).replace('][', ','))

# print(data)
[{'text': 'Tweet 01', 'id': '001'},
 {'text': 'Tweet 02', 'id': '002'},
 {'text': 'Tweet 03', 'id': '003'},
 {'text': 'Tweet 04', 'id': '004'},
 {'text': 'Tweet 05', 'id': '005'},
 {'text': 'Tweet 06', 'id': '006'},
 {'text': 'Tweet 07', 'id': '007'},
 {'text': 'Tweet 08', 'id': '008'},
 {'text': 'Tweet 09', 'id': '009'},
 {'text': 'Tweet 10', 'id': '010'}]

# load into pandas
df = pd.json_normalize(data)

# display(df)
       text   id
0  Tweet 01  001
1  Tweet 02  002
2  Tweet 03  003
3  Tweet 04  004
4  Tweet 05  005
5  Tweet 06  006
6  Tweet 07  007
7  Tweet 08  008
8  Tweet 09  009
9  Tweet 10  010

正如@Trenton McKinney 所说,您需要清理数据。所以你可以使用 f.read() to get the file as a string, then str.replace()']\n[' 替换为 ',',因为 ']\n[' 它导致了错误,最后 你可以试试 pd.read_json:

with open('data.json') as f:
    data=f.read()
data=data.replace(']\n[',',')

df=pd.read_json(data)

输出:

df
0  Tweet 01   1
1  Tweet 02   2
2  Tweet 03   3
3  Tweet 04   4
4  Tweet 05   5
5  Tweet 06   6
6  Tweet 07   7
7  Tweet 08   8
8  Tweet 09   9
9  Tweet 10  10

只要原始 json 文件适合内存,您就可以使用 raw_decoder 拉出 json 列表,一次一个。 raw_decoder returns 解析中消耗的数据长度,您可以 trim 字符串并继续直到没有数据剩余。

import csv
import json
with open('data.json') as f:
    rawdata = f.read()

decoder = json.JSONDecoder().raw_decode

with open('data.csv','w') as f:
    writer = csv.DictWriter(f, fieldnames=["id", "text"])
    while rawdata:
        data, idx = decoder(rawdata)
        writer.writerows(data)
        rawdata = rawdata[idx:].lstrip()

print(open('data.csv').read())