文章目录
最近在写爬虫对接API接口时,突然遇到这个让人抓狂的报错:requests.exceptions.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
。这个错误简直是Python开发者的噩梦啊(特别是新手朋友们)!今天我就把自己踩过的坑和解决方案全盘托出,保你以后遇到这个问题再也不慌!
一、错误现象与本质原因(必看!!!)
当使用requests库的.json()
方法时,控制台突然抛出这个错误,就像这样:
import requests
response = requests.get('https://api.example.com/data')
data = response.json() # 报错位置!!!
根本原因其实就一句话:服务器返回的内容根本不是合法的JSON格式!但是具体可能由以下五种情况导致:
- 服务器返回了空响应(HTTP状态码204)
- 返回了HTML页面(比如403/404错误页)
- 响应内容编码错误导致乱码
- 网络问题导致数据截断
- 响应头
Content-Type
与实际内容不符
二、六步排查法(超级实用)
步骤1:检查原始响应内容(救命操作)
!!!!在调用.json()之前必须加这个检查!!!!
print(response.text) # 查看原始文本
print(response.status_code) # 检查状态码
print(response.headers) # 查看响应头
最近我就遇到一个坑爹情况:服务器返回了200 OK
状态码,但内容却是<html>403 Forbidden</html>
,这种伪装成成功的失败响应最要命!
步骤2:处理空数据情况
当响应内容为空时(比如response.text
是空字符串),直接上保护代码:
if response.text.strip() == '':
raise ValueError('服务器返回了空响应')
else:
data = response.json()
步骤3:验证数据格式
用在线JSON验证工具检查原始内容是否合法:
# 临时保存响应内容
with open('debug.json', 'w', encoding='utf-8') as f:
f.write(response.text)
推荐使用JSONLint验证格式,最近发现很多API会在JSON开头加)]}',\n
这种奇怪的字符(特别是某些谷歌API),需要先清洗数据:
clean_text = response.text.lstrip(")]}'\n")
data = json.loads(clean_text)
步骤4:编码问题处理
遇到UnicodeDecodeError
时,强制指定编码格式:
response.encoding = 'utf-8-sig' # 处理BOM头
# 或者
response.encoding = response.apparent_encoding # 自动检测
步骤5:异常捕获最佳实践
try:
data = response.json()
except json.JSONDecodeError as e:
print(f"JSON解析失败!原始内容:{response.text[:200]}") # 打印前200字符
raise
步骤6:终极调试大法
使用httpbin
服务测试请求:
# 测试请求是否正常
test_res = requests.get('https://httpbin.org/get')
print(test_res.json()) # 正常应该输出JSON
三、实战案例:某电商平台API对接翻车实录
上周对接某平台商品API时,突然出现间歇性JSON解析失败。按照以下步骤排查:
- 发现
response.text
有时返回<html>504 Gateway Timeout</html>
- 检查请求频率发现触发了QPS限制
- 解决方案:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# 配置重试策略
retry_strategy = Retry(
total=3,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
with requests.Session() as s:
s.mount("https://", adapter)
response = s.get(url, timeout=5)
四、预防性编程技巧(重点收藏)
- 强制校验Content-Type
if 'application/json' not in response.headers.get('Content-Type', ''):
raise ValueError('非法内容类型')
- 设置超时避免死等
response = requests.get(url, timeout=(3.1, 7.5)) # 连接超时3.1s,读取超时7.5s
- 使用schema验证
from jsonschema import validate
schema = {
"type": "object",
"properties": {
"code": {"type": "number"},
"data": {"type": "array"}
}
}
validate(instance=data, schema=schema)
五、常见QA
Q:为什么Postman能正常获取,但代码就报错?
A:八成是请求头不同!用这个代码捕获cURL:
import requests
from requests_toolbelt.utils import dump
resp = requests.get('https://api.example.com')
data = dump.dump_all(resp)
print(data.decode('utf-8'))
Q:总遇到SSL证书错误怎么办?
A:临时解决方案(生产环境慎用):
requests.get(url, verify=False) # 跳过证书验证
Q:返回的JSON里有NaN
导致解析失败?
A:用simplejson
替代标准库:
import simplejson as json
data = json.loads(response.text, ignore_nan=True)
六、总结
遇到JSONDecodeError不要慌,记住这个排查口诀:
一看状态二看头,三验内容四查码,
编码重试加校验,问题定位不用愁!
最后送大家一个调试锦囊:所有网络请求都要假设对方会返回任何可能的垃圾数据!防御性编程搞起来,从此告别JSON解析噩梦~