如何解决Python requests库中session.json()返回None或乱码的问题?

问题现象与背景

在使用Python的requests库进行HTTP请求时,开发者经常通过Session对象维持会话状态。当调用session.json()方法解析响应内容时,可能会遇到以下典型问题:

  • 方法返回None值而非预期JSON数据
  • 解析时抛出JSONDecodeError异常
  • 中文字符出现乱码现象
  • 特殊字符编码错误导致解析失败

根本原因分析

1. 响应头缺失Content-Type

服务器未正确设置Content-Type: application/json头部时,requests库可能无法自动识别响应体格式。通过response.headers检查实际响应头:

print(response.headers.get('Content-Type'))

2. 响应体编码不匹配

当服务器使用非UTF-8编码(如GBK)传输JSON数据时,直接调用json()会导致解析失败。应先确认编码方式:

response.encoding = 'gbk'  # 显式设置编码
data = response.json()

3. 无效JSON格式

服务器可能返回以下非标准JSON:

  • 包含BOM头的UTF-8文件
  • 尾部有多余逗号或注释
  • 单引号替代双引号

解决方案实践

方法1:强制编码处理

通过response.content获取原始字节流后手动解码:

import json
data = json.loads(response.content.decode('utf-8-sig'))

方法2:响应验证包装器

创建安全的JSON解析装饰器:

def safe_json(response):
    try:
        return response.json()
    except ValueError:
        response.encoding = response.apparent_encoding
        return json.loads(response.text)

方法3:调试日志记录

使用请求钩子记录原始响应:

def response_logger(resp, *args, **kwargs):
    print(f"Raw response: {resp.content[:200]}")

session = requests.Session()
session.hooks['response'] = [response_logger]

高级排查技巧

1. 使用mitmproxy抓包

通过中间人代理分析原始网络流量,验证实际传输内容是否合规。

2. 对比curl请求

用命令行工具获取基准响应:

curl -v -H "Accept: application/json" http://api.example.com

3. 启用requests调试

设置环境变量查看底层通信:

export HTTP_PROXY=http://127.0.0.1:8888
export DEBUG=requests.packages.urllib3

最佳实践建议

  1. 始终检查response.status_code是否为200
  2. json()调用添加try-catch块
  3. 在测试环境模拟异常响应(如空body、非法字符)
  4. 考虑使用retry机制处理临时故障