4、处理 JSON 数据:JSON 格式解析、Python 字典与 JSON 互转、使用 json() 方法提取数据。

好,咱们进入第四章。这一章聊的是 JSON,说白了就是 API 世界里最通用的“普通话”。你调用任何外部接口,十有八九返回的都是 JSON。不会处理它,后面的集成工作根本没法做。

我个人习惯把 JSON 理解成“轻量级的数据交换格式”。它长得跟 Python 的字典很像,但又不完全一样。很多新手一上来就混淆,觉得 JSON 就是字典,结果在类型转换上栽跟头。嗯,咱们今天就把这事彻底捋清楚。

4.1 JSON 格式长什么样?

先看一个最典型的 JSON 结构。假设我们调用了一个天气 API,返回的数据是这样的:

{
  "city": "北京",
  "temperature": 23,
  "humidity": 65,
  "wind": {
    "direction": "东南风",
    "speed": 3.5
  },
  "forecast": [
    {"day": "周一", "temp_high": 25, "temp_low": 18},
    {"day": "周二", "temp_high": 27, "temp_low": 20}
  ]
}

注意几个关键点:

  • 键名必须用双引号,单引号不行。这是 JSON 和 Python 字典最大的区别之一。
  • 字符串值也必须用双引号
  • 数字不需要引号,布尔值用 true/false(小写),空值用 null
  • 支持嵌套,对象里套对象,对象里套数组,非常灵活。

核心认知:JSON 本质上就是一个字符串。它有自己的语法规则,跟 Python 的数据结构是两码事。你从 API 拿到的 JSON,必须先“解析”成 Python 能理解的东西,才能操作。

4.2 Python 字典与 JSON 互转

Python 标准库 json 提供了两个核心方法:json.dumps()json.loads()。名字很好记,dumps 是“把字典 dump 成字符串”,loads 是“把字符串 load 成字典”。

4.2.1 JSON 字符串 → Python 字典(json.loads)

这是最常用的场景。你从 requests.get() 拿到响应文本,然后转成字典:

import json

json_str = '{"name": "张三", "age": 30, "is_student": false}'
data = json.loads(json_str)

print(data)          # {'name': '张三', 'age': 30, 'is_student': False}
print(type(data))    # <class 'dict'>
print(data["name"])  # 张三

注意看,JSON 里的 false 变成了 Python 的 Falsenull 会变成 None。这个转换是自动的,你不需要手动处理。

我的经验:我曾经在解析一个金融接口时,对方返回的 JSON 里某个字段有时是字符串,有时是数字。直接用 json.loads 解析后,类型不一致导致后续计算报错。后来我加了一层类型校验,才把问题解决。所以,别太相信接口文档,实际数据可能跟你想象的不一样。

4.2.2 Python 字典 → JSON 字符串(json.dumps)

反过来,你要把数据发给 API 时,需要把字典转成 JSON 字符串:

import json

data = {
    "name": "李四",
    "age": 25,
    "skills": ["Python", "Java", "Go"],
    "is_active": True
}

json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)

这里有两个参数值得注意:

  • ensure_ascii=False:让中文正常显示,而不是变成 \uXXXX 的编码。
  • indent=2:格式化输出,方便调试时阅读。

输出效果:

{
  "name": "李四",
  "age": 25,
  "skills": [
    "Python",
    "Java",
    "Go"
  ],
  "is_active": true
}

你看,Python 的 True 又变回了 JSON 的 true。这就是互转的对称性。

4.3 使用 json() 方法提取数据

如果你用的是 requests 库,有一个更省事的办法——直接用响应对象的 .json() 方法。它会自动帮你完成“读取响应体 → 解析 JSON → 返回字典”这一整套流程。

import requests

url = "https://api.example.com/user/123"
response = requests.get(url)

# 直接拿到字典
user_data = response.json()

print(user_data["name"])
print(user_data["email"])

这比先拿 response.textjson.loads() 要简洁得多。而且 .json() 内部做了编码处理,基本不会出现乱码问题。

注意.json() 方法只有在响应内容确实是 JSON 格式时才有效。如果服务器返回了 500 错误页面或者 HTML,调用 .json() 会抛出 json.JSONDecodeError。我建议你在调用前先检查 response.status_code,或者用 try-except 包一下。

4.4 实战:处理嵌套 JSON

实际项目中的 JSON 往往嵌套很深。比如一个电商订单的返回数据:

{
  "order_id": "ORD20241001",
  "user": {
    "id": 1001,
    "name": "王五",
    "address": {
      "province": "广东",
      "city": "深圳",
      "detail": "南山区科技园"
    }
  },
  "items": [
    {"product_id": "P001", "name": "机械键盘", "price": 399, "quantity": 1},
    {"product_id": "P002", "name": "鼠标垫", "price": 29.9, "quantity": 2}
  ],
  "total": 458.8
}

要提取用户所在城市,你需要链式访问:

data = response.json()
city = data["user"]["address"]["city"]
print(city)  # 深圳

要遍历商品列表,计算总价:

items = data["items"]
total = sum(item["price"] * item["quantity"] for item in items)
print(total)  # 458.8

这里有个小技巧:如果你不确定某个键是否存在,用 .get() 方法更安全:

# 如果 "discount" 不存在,返回默认值 0
discount = data.get("discount", 0)

避坑指南:我曾经在解析一个日志分析系统的 API 时,对方返回的 JSON 里某个数组字段有时是 null,有时是空数组 []。直接用 for item in data["logs"] 遍历时,遇到 null 直接报错。后来我统一做了空值处理:logs = data.get("logs") or []。这个写法很实用,推荐给你。

4.5 本章知识体系

下面这张图帮你理清 JSON 处理的核心流程:

JSON 数据处理核心流程 外部 API 返回 JSON 字符串 方式一:json.loads() 手动解析字符串 方式二:.json() requests 内置方法 Python 字典 可操作的数据结构 json.dumps() 字典 → JSON 字符串 ↓ 反向转换

这张图展示了 JSON 处理的两条路径:从 API 拿到 JSON 字符串后,你可以用 json.loads().json() 把它变成 Python 字典,然后随心所欲地提取数据。处理完后,如果需要把数据发出去,再用 json.dumps() 转回 JSON 字符串。

4.6 总结

JSON 处理说白了就三件事:

  • 读进来:用 json.loads().json() 把字符串变字典。
  • 写出去:用 json.dumps() 把字典变字符串。
  • 小心嵌套:链式访问时注意空值,多用 .get() 和默认值。

这些操作在后续的 API 集成中会反复出现。你写多了就会发现,JSON 处理其实就那么几个套路,熟练了之后闭着眼睛都能写。

一个小建议:调试 JSON 数据时,我习惯先 print(json.dumps(data, indent=2, ensure_ascii=False)) 把结构打印出来看看。这样能快速了解数据长什么样,避免写错键名。这个习惯帮我省了不少排查时间。


专注资料整理