
本文详解如何从 api 响应中安全解析 json 数据,并将其准确转换为 pandas dataframe,重点解决因未解析 json 字符串、结构误判或编码错误导致的空 dataframe 问题。
本文详解如何从 api 响应中安全解析 json 数据,并将其准确转换为 pandas dataframe,重点解决因未解析 json 字符串、结构误判或编码错误导致的空 dataframe 问题。
在使用 urllib 调用 RESTful API(如 WTO 时间序列接口)后,开发者常误将原始响应字符串(如 response.read().decode('utf-8') 的结果)直接传入 pd.DataFrame(),从而得到空或报错的 DataFrame。根本原因在于:pd.DataFrame() 无法自动解析 JSON 格式的字符串;它只接受 Python 原生数据结构(如 list、dict),而非 JSON 文本。
以下是一个健壮、可复用的处理流程:
✅ 正确步骤分解
-
发起请求并获取原始响应体
使用 urllib.request.urlopen()(推荐配合 with 语句确保资源释放),并以 'utf-8' 解码(而非 'ASCII',避免 Unicode 错误):
import urllib.request, json
import pandas as pd
url = "https://api.wto.org/timeseries/v1/indicator_categories?lang=1"
hdr = {
'Cache-Control': 'no-cache',
'Ocp-Apim-Subscription-Key': '21cda66d75fc4010b8b4d889f4af6ccd',
}
req = urllib.request.Request(url, headers=hdr)
with urllib.request.urlopen(req) as response:
raw_text = response.read().decode('utf-8') # ✅ 关键:解码为字符串
-
显式解析 JSON 字符串
使用 json.loads() 将字符串转为 Python 对象(dict 或 list)。切勿使用 eval() —— 它不识别 null/true/false(JSON 关键字),且存在严重安全风险:
try:
json_data = json.loads(raw_text) # ✅ 安全、标准解析
except json.JSONDecodeError as e:
raise ValueError(f"Invalid JSON response: {e}")
-
动态判断并提取目标数据结构
API 返回结构可能为:- 直接的字典列表(如 [{...}, {...}])→ 可直接构造 DataFrame;
- 包裹型字典(如 {"status": "ok", "data": [...]})→ 需提取 data 等键值。
if isinstance(json_data, list):
# 最常见情况:API 直接返回数组
df = pd.DataFrame(json_data)
elif isinstance(json_data, dict):
# 检查常见字段名(根据实际 API 文档调整)
for key in ['results', 'data', 'items', 'categories']:
if key in json_data:
target = json_data[key]
if isinstance(target, list):
df = pd.DataFrame(target)
break
else:
raise ValueError("No list-type data found in JSON response")
else:
raise TypeError(f"Unsupported JSON root type: {type(json_data).__name__}")
⚠️ 注意事项与最佳实践
- 永远不要用 eval() 解析 JSON:eval() 执行任意代码,且 null 在 Python 中是 None,true/false 是 True/False,直接 eval() 必报 NameError。
- 避免硬编码订阅密钥:生产环境应通过环境变量或配置文件管理敏感信息。
- 添加异常处理:网络请求、JSON 解析、键缺失均可能失败,需 try/except 包裹关键步骤。
- 验证响应状态码:可在 urlopen 后检查 response.getcode() == 200,避免静默处理错误响应。
- 查看实际结构:调试时先打印 type(json_data) 和 json_data.keys()(若为 dict)或 len(json_data)(若为 list),再决定取数逻辑。
执行完成后,使用 df.head() 和 df.info() 快速验证列名、数据类型与行数,确保转换成功。此方法适用于绝大多数返回 JSON 的公共 API,兼具健壮性与可维护性。










