你必须知道的Pandas 解析json数据的函数-json_normalize(),自从用了它,彻底摆脱了网络上的各种JSON格式化工具
写了好几篇文章了,今天写点很少人写但是很有用的!记得点赞收藏加关注哦。
前言:Json介绍
Json是一个应用及其广泛的用来传输和交换数据的格式,它被应用在数据库中,也被用于API请求结果数据集中。虽然它应用广泛,机器很容易阅读且节省空间,但是却不利于人来阅读和进一步做数据分析,因此通常情况下需要在获取json数据后,将其转化为表格格式的数据,以方便人来阅读和理解。常见的Json数据格式有2种,均以键值对的形式存储数据,只是包装数据的方法有所差异:
a. 一般JSON对象
采用
{}
将键值对数据括起来,有时候会有多层
{}
b. JSON对象列表
采用
[]
将
JSON对象
括起来,形成一个JSON对象的列表,JSON对象中同样会有多层
{}
,也会有
[]
出现,形成
嵌套列表
这篇文章主要讲述
pandas
内置的Json数据转换方法
json_normalize()
,它可以对以上两种Json格式的数据进行解析,最终生成
DataFrame
,进而对数据进行更多操作。本文的主要解构如下:
- 解析一个最基本的Json
- 解析一个带有多层数据的Json
- 解析一个带有嵌套列表的Json
-
当
Key
不存在时如何忽略系统报错 -
使用
sep
参数为嵌套Json的Key
设置分隔符 - 为嵌套列表数据和元数据添加前缀
- 通过URL获取Json数据并进行解析
-
探究:解析带有
多个嵌套列表
的Json
json_normalize()函数参数讲解
在进行代码演示前先导入相应依赖库,未安装pandas库的请自行安装(此代码在Jupyter Notebook环境中运行)。
from pandas import json_normalize
import pandas as pd
1. 解析一个最基本的Json
a. 解析一般Json对象
a_dict = {
'school': 'ABC primary school',
'location': 'London',
'ranking': 2
pd.json_normalize(a_dict)
输出结果为:
b. 解析一个Json对象列表
json_list = [
{'class': 'Year 1', 'student number': 20, 'room': 'Yellow'},
{'class': 'Year 2', 'student number': 25, 'room': 'Blue'}
pd.json_normalize(json_list)
输出结果为:
2. 解析一个带有多层数据的Json
a. 解析一个有多层数据的Json对象
json_obj = {
'school': 'ABC primary school',
'location': 'London',
'ranking': 2,
'info': {
'president': 'John Kasich',
'contacts': {
'email': {
'admission': 'admission@abc.com',
'general': 'info@abc.com'
'tel': '123456789',
pd.json_normalize(json_obj)
输出结果为:
多层key之间使用点隔开,展示了所有的数据,这已经解析了3层,上述写法和
pd.json_normalize(json_obj, max_level=3)
等价。
如果设置
max_level=1
,则输出结果为下图所示,
contacts
部分的数据汇集成了一列
如果设置
max_level=2
,则输出结果为下图所示,
contacts
下的
email
部分的数据汇集成了一列
b. 解析一个有多层数据的Json对象列表
json_list = [
'class': 'Year 1',
'student count': 20,
'room': 'Yellow',
'info': {
'teachers': {
'math': 'Rick Scott',
'physics': 'Elon Mask'
'class': 'Year 2',
'student count': 25,
'room': 'Blue',
'info': {
'teachers': {
'math': 'Alan Turing',
'physics': 'Albert Einstein'
pd.json_normalize(json_list)
输出结果为:
若分别将
max_level
设置为
2
和
3
,则输出结果应分别是什么?请自行尝试~
3. 解析一个带有嵌套列表的Json
json_obj = {
'school': 'ABC primary school',
'location': 'London',
'ranking': 2,
'info': {
'president': 'John Kasich',
'contacts': {
'email': {
'admission': 'admission@abc.com',
'general': 'info@abc.com'
'tel': '123456789',
'students': [
{'name': 'Tom'},
{'name': 'James'},
{'name': 'Jacqueline'}
pd.json_normalize(json_obj)
此例中
students
键对应的值是一个
列表
,使用
[]
括起来。直接采用上述的方法进行解析,则得到的结果如下:
students
部分的数据并未被成功解析,此时可以为
record_path
设置值即可,调用方式为
pd.json_normalize(json_obj, record_path='students')
,在此调用方式下,得到的结果只包含了
name
部分的数据。
若要增加其他字段的信息,则需为
meta
参数赋值,例如下述调用方式下,得到的结果如下:
pd.json_normalize(json_obj, record_path='students', meta=['school', 'location', ['info', 'contacts', 'tel'], ['info', 'contacts', 'email', 'general']])
4. 当
Key
不存在时如何忽略系统报错
data = [
'class': 'Year 1',
'student count': 20,
'room': 'Yellow',
'info': {
'teachers': {
'math': 'Rick Scott',
'physics': 'Elon Mask',
'students': [
{ 'name': 'Tom', 'sex': 'M' },
{ 'name': 'James', 'sex': 'M' },
'class': 'Year 2',
'student count': 25,
'room': 'Blue',
'info': {
'teachers': {
# no math teacher
'physics': 'Albert Einstein'
'students': [
{ 'name': 'Tony', 'sex': 'M' },
{ 'name': 'Jacqueline', 'sex': 'F' },
pd.json_normalize(
data,
record_path =['students'],
meta=['class', 'room', ['info', 'teachers', 'math']]
)
在
class
等于
Year 2
的Json对象中,
teachers
下的
math
键不存在,直接运行上述代码会报以下错误,提示
math
键并不总是存在,且给出了相应建议:
Try running with errors='ignore'
。
添加
errors
条件后,重新运行得出的结果如下图所示,没有
math
键的部分使用
NaN
进行了填补。
pd.json_normalize(
data,
record_path =['students'],
meta=['class', 'room', ['info', 'teachers', 'math']],
errors='ignore'
)
5. 使用
sep
参数为嵌套Json的
Key
设置分隔符
在
2.a
的案例中,可以注意到输出结果的具有多层
key
的数据列标题是采用
.
对多层key进行分隔的,可以为
sep
赋值以更改分隔符。
json_obj = {
'school': 'ABC primary school',
'location': 'London',
'ranking': 2,
'info': {
'president': 'John Kasich',
'contacts': {
'email': {
'admission': 'admission@abc.com',
'general': 'info@abc.com'
'tel': '123456789',
pd.json_normalize(json_obj, sep='->')
输出结果为:
6. 为嵌套列表数据和元数据添加前缀
在
3
例的输出结果中,各列名均无前缀,例如
name
这一列不知是元数据解析得到的数据,还是通过
student
嵌套列表的的出的数据,因此为
record_prefix
和
meta_prefix
参数分别赋值,即可为输出结果添加相应前缀。
json_obj = {
'school': 'ABC primary school',
'location': 'London',
'ranking': 2,
'info': {
'president': 'John Kasich',
'contacts': {
'email': {
'admission': 'admission@abc.com',
'general': 'info@abc.com'
'tel': '123456789',
'students': [
{'name': 'Tom'},
{'name': 'James'},
{'name': 'Jacqueline'}
pd.json_normalize(json_obj, record_path='students',
meta=['school', 'location', ['info', 'contacts', 'tel'], ['info', 'contacts', 'email', 'general']],
record_prefix='students->',
meta_prefix='meta->',
sep='->')
本例中,为嵌套列表数据添加
students->
前缀,为元数据添加
meta->
前缀,将嵌套key之间的分隔符修改为
->
,输出结果为:
7. 通过URL获取Json数据并进行解析
通过URL获取数据需要用到
requests
库,请自行安装相应库。
import requests
from pandas import json_normalize
# 通过天气API,获取深圳近7天的天气
url = 'https://tianqiapi.com/free/week'
# 传入url,并设定好相应的params
r = requests.get(url, params={"appid":"59257444", "appsecret":"uULlTGV9 ", 'city':'深圳'})
# 将获取到的值转换为json对象
result = r.json()
df = json_normalize(result, meta=['city', 'cityid', 'update_time'], record_path=['data'])
df
result的结果如下所示,其中
data
为一个嵌套列表:
{'cityid': '101280601',
'city': '深圳',
'update_time': '2021-08-09 06:39:49',
'data': [{'date': '2021-08-09',
'wea': '中雨转雷阵雨',
'wea_img': 'yu',
'tem_day': '32',
'tem_night': '26',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-10',
'wea': '雷阵雨',
'wea_img': 'yu',
'tem_day': '32',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-11',
'wea': '雷阵雨',
'wea_img': 'yu',
'tem_day': '31',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-12',
'wea': '多云',
'wea_img': 'yun',
'tem_day': '33',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-13',
'wea': '多云',
'wea_img': 'yun',
'tem_day': '33',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-14',
'wea': '多云',
'wea_img': 'yun',
'tem_day': '32',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'},
{'date': '2021-08-15',
'wea': '多云',
'wea_img': 'yun',
'tem_day': '32',
'tem_night': '27',
'win': '无持续风向',
'win_speed': '<3级'}]}
解析后的输出结果为:
8. 探究:解析带有
多个嵌套列表
的Json
当一个Json对象或对象列表中有超过一个嵌套列表时,
record_path
无法将所有的嵌套列表包含进去,因为它只能接收一个key值。此时,我们需要先根据多个嵌套列表的
key
将Json解析成多个
DataFrame
,再将这些
DataFrame
根据实际关联条件拼接起来,并去除重复值。
json_obj = {
'school': 'ABC primary school',
'location': 'shenzhen',
'ranking': 2,
'info': {
'president': 'John Kasich',
'contacts': {
'email': {
'admission': 'admission@abc.com',
'general': 'info@abc.com'
'tel': '123456789',
'students': [
{'name': 'Tom'},
{'name': 'James'},
{'name': 'Jacqueline'}
# 添加university嵌套列表,加上students,该JSON对象中就有2个嵌套列表了
'university': [
{'university_name': 'HongKong university shenzhen'},
{'university_name': 'zhongshan university shenzhen'},
{'university_name': 'shenzhen university'}
# 尝试在record_path中写上两个嵌套列表的名字,即record_path = ['students', 'university],结果无济于事
# 于是决定分两次进行解析,分别将record_path设置成为university和students,最终将2个结果合并起来
df1 = pd.json_normalize(json_obj, record_path=['university'],
meta=['school', 'location', ['info', 'contacts', 'tel'],
['info', 'contacts', 'email', 'general']],
record_prefix='university->',
meta_prefix='meta->',
sep='->')
df2 = pd.json_normalize(json_obj, record_path=['students'],
meta=['school', 'location', ['info', 'contacts', 'tel'],