Python初学者笔记第二十二期 -- (JSON数据解析)

发布于:2025-08-13 ⋅ 阅读:(12) ⋅ 点赞:(0)

第31节课 JSON数据解析

1.JSON基础概念

JSON 是一种轻量级的数据交换格式(另一个叫XML),具有简洁、易读的特点,并且在不同编程语言之间能很好地实现数据传递。在 Python 中,json模块能够实现 Python 数据类型与 JSON 数据格式之间的相互转换。

JSON的语法规则

  • 数据以键值对形式表示: "name": "张三"
  • 数据由逗号分隔
  • 花括号 {} 保存对象
  • 方括号 [] 保存数组
  • 支持的数据类型:
    • 数字(整数或浮点数)
    • 字符串(双引号中)
    • 布尔值(true 或 false)
    • 数组(方括号中)
    • 对象(花括号中)
    • null(None)
JSON 类型 Python 类型
对象 dict
数组 list/tuple
字符串 str
数字 int/float
true True
false False
null None

JSON数据示例

{
  "name": "张三",
  "age": 30,
  "isStudent": false,
  "courses": ["Python", "数据分析", "机器学习"],
  "address": {
    "city": "北京",
    "postcode": "100000"
  },
  "phoneNumbers": null
}

json模块中的主要函数

函数 描述
json.dumps() 将Python对象编码成JSON字符串
json.dump() 将Python对象编码成JSON字符串并写入文件
json.loads() 将JSON字符串解码为Python对象
json.load() 从文件中读取JSON字符串并解码为Python对象

2.JSON数据编码(序列化)

在实际开发中,为了让生成的 JSON 字符串更易读,可以使用indent参数进行缩进格式化,使用sort_keys参数按键名进行排序。

import json

# 字典对象
data = {
    "name": "张三",
    "age": 30,
    "isStudent": False,
    "courses": ["Python", "数据分析", "机器学习"],
    "address": {
        "city": "北京",
        "postcode": "100000"
    },
    "phoneNumbers": None
}
# 将Python对象编码为JSON字符串
json_str = json.dumps(data, ensure_ascii=False)
print(json_str)
# 格式化输出
json_str = json.dumps(data, indent=4, ensure_ascii=False)
print(json_str)
json_str = json.dumps(data, indent=4, sort_keys=True, ensure_ascii=False)
print(json_str)

# 写入到文件中
with open("data.json", "w", encoding="utf-8") as file:
    json.dump(data, file, ensure_ascii=False, indent=4, sort_keys=True)

3.JSON数据解码(反序列化)

import json
json_str = '{"name": "张三", "age": 30, "isStudent": false, "courses": ["Python", "数据分析", "机器学习"], "address": {"city": "北京", "postcode": "100000"}, "phoneNumbers": null}'
data = json.loads(json_str)
print(type(data))
print(data)
print(data['name'])
print(data['courses'][1])

with open("data.json", 'r', encoding='utf-8') as file:
    data = json.load(file)
    print(data)
    print(type(data))

4.自定义编码器与解码器

标准的JSON只支持以下数据类型:

  • 字符串(string)
  • 数字(number)
  • 对象(object)- 在Python中表示为字典
  • 数组(array)- 在Python中表示为列表
  • 布尔值(boolean)- 在Python中表示为True/False
  • 空值(null)- 在Python中表示为None

然而,Python中有许多其他数据类型(如日期时间、自定义类、集合等)无法直接被JSON序列化。这就是为什么我们需要自定义编码器和解码器。

import json
import datetime
data = {
    "name": "张三",
    "create_at": datetime.datetime.now(),
    "tags": {"Pythion", "JSON", "编程"}
}

json_str = json.dumps(data, indent=4)
print(json_str)
# TypeError: Object of type set is not JSON serializable

自定义编码器

就是将Python中不能够直接编码为JSON格式的数据进行转换 转换成能够被JSON识别的数据

(1)使用default参数

最简单的自定义编码方式是使用 json.dumps() 的 default 参数:

import json
import datetime
data = {
    "name": "张三",
    "create_at": datetime.datetime.now(),
    "tags": {"Pythion", "JSON", "编程"}
}

# 编码函数:将非JSON类型的数据转换为JSON类型的数据
def custom_encoder(obj):
    if isinstance(obj, datetime.datetime):
        return obj.isoformat()
    elif isinstance(obj, set):
        return list(obj)

json_str = json.dumps(data, indent=4, default=custom_encoder, ensure_ascii=False)
print(json_str)

(2)创建JSONEncoder子类

更灵活的方法是继承 json.JSONEncoder 类并重写 default 方法:

import json
import datetime
data = {
    "name": "张三",
    "create_at": datetime.datetime.now(),
    "tags": {"Pythion", "JSON", "编程"}
}

# 了解
class CustomJSONEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime.datetime):
            return {
                "__datetime__":True,
                "value":obj.isoformat()
            }
        elif isinstance(obj, set):
            return {
                "__set__": True, # 将数据原本的类型进行录入 为了给解码做提示
                "value":list(obj)
            }
        return super().default(obj)

json_str = json.dumps(data, indent=4, cls=CustomJSONEncoder, ensure_ascii=False)
print(json_str)

自定义解码器

就是将某些JSON数据解码为原先的样子(Python数据类型)

(1)使用object_hook参数

json.loads() 函数的 object_hook 参数允许我们自定义JSON对象的解码方式:

import json
import datetime
json_str = """
{
    "name": "张三",
    "create_at": {
        "__datetime__": true,
        "value": "2025-06-09T19:59:05.343916"
    },
    "tags": {
        "__set__": true,
        "value": [
            "编程",
            "JSON",
            "Pythion"
        ]
    }
}
"""
# 自定义解码函数
def custom_decoder(obj):
    if "__datetime__" in obj:
        return datetime.datetime.fromisoformat(obj['value'])
    elif "__set__" in obj:
        return set(obj['value'])
    return obj


data = json.loads(json_str, object_hook=custom_decoder)
print(data)

(2)创建JSONDecoder子类

更复杂的解码可以通过继承 json.JSONDecoder 类实现:

import json
import datetime
json_str = """
{
    "name": "张三",
    "create_at": {
        "__datetime__": true,
        "value": "2025-06-09T19:59:05.343916"
    },
    "tags": {
        "__set__": true,
        "value": [
            "编程",
            "JSON",
            "Pythion"
        ]
    }
}
"""
# 自定义解码类
class CustomJSONDecoder(json.JSONDecoder):
    # 构造函数
    def __init__(self, *args, **kwargs):
        json.JSONDecoder.__init__(self, object_hook=self.object_hook, *args, **kwargs)
    def object_hook(self,obj):
        if "__datetime__" in obj:
            return datetime.datetime.fromisoformat(obj['value'])
        elif "__set__" in obj:
            return set(obj['value'])
        return obj

data = json.loads(json_str, cls=CustomJSONDecoder)
print(data)

5.处理复杂JSON数据

嵌套结构

{
    "company": "ABC科技",
    "employees": [
        {
            "id": 1001,
            "name": "张三",
            "department": "研发",
            "skills": ["Python", "Django", "Docker"]
        },
        {
            "id": 1002,
            "name": "李四",
            "department": "数据",
            "skills": ["Python", "数据分析", "机器学习"]
        }
    ],
    "address": {
        "city": "北京",
        "street": "中关村",
        "postcode": "100080"
    }
}
import json
json_str = '''
{
    "company": "ABC科技",
    "employees": [
        {
            "id": 1001,
            "name": "张三",
            "department": "研发",
            "skills": ["Python", "Django", "Docker"]
        },
        {
            "id": 1002,
            "name": "李四",
            "department": "数据",
            "skills": ["Python", "数据分析", "机器学习"]
        }
    ],
    "address": {
        "city": "北京",
        "street": "中关村",
        "postcode": "100080"
    }
}
'''
data = json.loads(json_str)
print(data['company'])
print(data['employees'][0]['name'])
print(data['employees'][1]['skills'][1])
for employee in data['employees']:
    print(employee['name'])
    print(employee['department'])
    print(employee['skills'])

动态解析路径

import json
json_str = '''
{
    "company": "ABC科技",
    "employees": [
        {
            "id": 1001,
            "name": "张三",
            "department": "研发",
            "skills": ["Python", "Django", "Docker"]
        },
        {
            "id": 1002,
            "name": "李四",
            "department": "数据",
            "skills": ["Python", "数据分析", "机器学习"]
        }
    ],
    "address": {
        "city": "北京",
        "street": "中关村",
        "postcode": "100080"
    }
}
'''
data = json.loads(json_str)

path1 = "employees.0.name"
path2 = "address.city"

def get_value_by_path(data, path):
    keys = path.split(".")
    result = data
    for key in keys:
        if key.isdigit():
            result = result[int(key)]
        else:
            result = result[key]
    return result

print(get_value_by_path(data, path1))
print(get_value_by_path(data, path2))
print(get_value_by_path(data, "employees.1.skills.2"))

6.拓展:jsonpath库

JSONPath提供了一种简洁的方式来导航JSON结构并提取特定数据,而不需要复杂的循环和条件语句。

pip install jsonpath-ng

基本语法元素

语法元素 描述
$ 根对象/元素
@ 当前对象/元素
. 子元素操作符
.. 递归下降操作符
* 通配符,匹配所有对象/元素
[] 下标操作符
[,] 并集操作符
[start:end:step] 数组切片操作符
?() 过滤表达式
() 脚本表达式

示例JSON数据

{
  "store": {
    "book": [
      {
        "category": "参考书",
        "author": "李明",
        "title": "Python编程入门",
        "price": 49.99
      },
      {
        "category": "小说",
        "author": "王芳",
        "title": "梦想之旅",
        "price": 29.99,
        "isbn": "0-553-21311-3"
      }
    ],
    "bicycle": {
      "color": "红色",
      "price": 599.99
    }
  }
}

示例路径演示

JSONPath 描述
$.store.book[*].author 所有书籍的作者
$..author 所有作者
$.store.* store下的所有元素
$.store..price store下所有价格
$..book[2] 第三本书
$..book[-1] 最后一本书
$..book[0,1] 前两本书
$..book[:2] 前两本书
$..book[?(@.isbn)] 所有有isbn属性的书
$..book[?(@.price<10)] 所有价格小于10的书
$..book[?(@.price==8.95)] 所有价格等于8.95的书
$..book[?(@.title =~ /.*REGEX.*/i)] 标题匹配正则表达式的书

基本操作

import json
from jsonpath_ng import parse
json_str = """
{
  "store": {
    "book": [
      {
        "category": "参考书",
        "author": "李明",
        "title": "Python编程入门",
        "price": 49.99
      },
      {
        "category": "小说",
        "author": "王芳",
        "title": "梦想之旅",
        "price": 29.99,
        "isbn": "0-553-21311-3"
      }
    ],
    "bicycle": {
      "color": "红色",
      "price": 599.99
    }
  }
} 
 """
data = json.loads(json_str)
# 导航对象
jsonpath_expr = parse('$.store.book[*].author')

print(jsonpath_expr.find(data))# 列表
print(jsonpath_expr.find(data)[0]) # DatumInContext对象
print(type(jsonpath_expr.find(data)[0]))
for match in jsonpath_expr.find(data):
    print(match.value)
    print(match.path)
    print(match.context)

7.拓展:Schema验证

JSONSchema是一种用于验证JSON数据结构的规范,它允许我们定义JSON数据的格式、类型和约束条件,确保数据符合预期的结构和规则。

pip install jsonschema

JSONSchema是一种基于JSON格式的模式语言,用于:

  • 描述现有数据格式
  • 提供清晰的人机可读文档
  • 验证数据,确保数据质量
  • 自动测试和验证客户端提交的数据

核心关键字

关键字 描述
$schema 指定Schema的版本
title Schema的标题
description Schema的描述
type 定义值的数据类型
properties 定义对象的属性
required 指定必需的属性
minimum/maximum 数值的最小/最大值
minLength/maxLength 字符串的最小/最大长度
pattern 字符串必须匹配的正则表达式
enum 枚举,值必须是指定的值之一
from jsonschema import validate

# 定义一个JSON验证Schema
# 用户信息验证
user_schema = {
    "type": "object",  # 要求JSON整体是个JSON对象
    "properties": {  # 指定属性要求
        "name": {"type": "string"},  # 必须有一个name属性 string类型
        "age": {"type": "integer", "minimum": 0}, # 必须有一个age属性 int类型 最小0
        "email":{"type": "string", "format":"email"},
        "interests": {"type":"array", "items":{"type":"string"}}
    },
    "required":["name","age","email"]
}
user1  = {
    "name":"张三",
    "age":30,
    "email":"zhangsan@qq.com",
    "interests":["编程", "吃饭"]
}
user2  = {
    "name":"张三",
    "age":30,
    "interests":["编程", "吃饭"]
}
user3 = {
    "name":"张三",
    "age":30,
    "email":"zhangsan@qq.com",
}
user4 = {
    "name":"张三",
    "age":"三十",
    "email":"zhangsan@qq.com",
}
# 最好try-except
validate(instance=user1, schema=user_schema)
# validate(instance=user2, schema=user_schema) # 缺email
validate(instance=user3, schema=user_schema)
validate(instance=user4, schema=user_schema)

8.案例:头条新闻

import requests


def print_news(title, uniquekey):
    # 1512-新闻详情查询 - 代码参考(根据实际业务情况修改)
    # 基本参数配置
    apiUrl = 'http://v.juhe.cn/toutiao/content'  # 接口请求URL
    apiKey = '3719af094e850cf3f4c4aea0bdb361d6'  # 在个人中心->我的数据,接口名称上方查看

    # 接口请求入参配置
    requestParams = {
        'key': apiKey,
        'uniquekey': uniquekey,
    }

    # 发起接口网络请求
    response = requests.get(apiUrl, params=requestParams)

    # 解析响应结果
    if response.status_code == 200:
        data = response.json()
        # 网络请求成功。可依据业务逻辑和接口文档说明自行处理。
        print("=" * 20)
        print(title)
        print(data['result']['content'])
    else:
        # 网络异常等因素,解析结果异常。可依据业务逻辑自行处理。
        print('请求异常')

def search_news():
    # 923-新闻列表查询 - 代码参考(根据实际业务情况修改)
    # 基本参数配置
    apiUrl = 'http://v.juhe.cn/toutiao/index'  # 接口请求URL
    apiKey = '3719af094e850cf3f4c4aea0bdb361d6'  # 在个人中心->我的数据,接口名称上方查看
    # 接口请求入参配置
    requestParams = {
        'key': apiKey,
        'type': 'yule',
        'page': 1,
        'page_size': 10,
        'is_filter': 0,
    }
    # 发起接口网络请求
    response = requests.get(apiUrl, params=requestParams)
    # 解析响应结果
    if response.status_code == 200:  # 404
        data = response.json()
        # 网络请求成功。可依据业务逻辑和接口文档说明自行处理。
        # print(data)
        # print(type(data))
        for item in data['result']['data']:
            print_news(item['title'], item['uniquekey'])
    else:
        # 网络异常等因素,解析结果异常。可依据业务逻辑自行处理。
        print('请求异常')

search_news()