应避免直接调用 response.json(),因其不校验状态码和 content-type,非法 json 或非 json 响应会抛出未捕获的 json.jsondecodeerror 导致协程崩溃;正确做法是先判状态码、再验 content_type,最后手动 text() + json.loads() 并包裹 try-except。

直接用 response.json() 会崩,别这么干
asyncio 环境下调用 response.json()(来自 aiohttp 或 httpx)看似方便,但一旦响应体不是合法 JSON,它会立刻抛出 json.JSONDecodeError,而且这个异常**不会被 asyncio 自动捕获或降级**——协程直接中断,上游 await 点崩溃,错误还可能被吞掉。更糟的是,很多 API 在 401/429/500 时返回 HTML 或纯文本,response.json() 完全不检查 Content-Type 或状态码,硬解析必挂。
aiohttp.ClientResponse.json() 的坑与替代写法
aiohttp 的 .json() 方法默认不校验响应头,也不处理空响应体。正确做法是手动读取 + 条件解析:
- 先检查
response.status是否在 200–299 范围,非成功状态优先用await response.text()提取错误信息 - 再检查
response.content_type是否为"application/json",否则跳过解析 - 用
await response.text()获取字符串,再交给json.loads()—— 这样你才能加try-except
示例:
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
import json
import aiohttp
async def fetch_and_parse(url):
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
if not (200
<h3>异步场景下 <code>json.loads()</code> 的容错封装要点</h3>
<p>同步封装的 <code>safe_json_loads</code> 直接搬进 async 函数里没问题,但要注意三件事:</p>
- 输入必须是
str,不能是bytes——aiohttp的resp.read()返回 bytes,得先 decode;resp.text()已帮你做了,优先用它 - 别在
try块里做耗时操作(比如日志写磁盘),否则阻塞事件循环;错误信息建议只存内存或发到异步队列 - 如果业务允许“部分容忍”,比如字段缺失可填默认值,就别在解析层兜底,而应在解析后用
dict.get("key", default)处理,保持解析逻辑纯粹
大响应体 + 异步流式解析的边界在哪
当响应体超过几 MB,尤其是一次返回上千条记录的数组,await resp.text() 把整个响应加载进内存再 json.loads() 就危险了。这时候要换路子:
- 确认格式:如果是 NDJSON(每行一个 JSON 对象),用
async for line in resp.content:逐行解码 +json.loads(line.decode()) - 如果是单一大数组(
[{...},{...}]),json.loads()必须全量加载,没得选;真要流式,得上ijson配合asyncio.to_thread()把解析挪到线程池,但注意这会增加调度开销 - 永远不要在协程里调
json.load()(文件 IO 版),它同步阻塞,等于给 event loop 打麻药
真正容易被忽略的,是 HTTP 响应流关闭时机 —— 如果你在解析中途出错并提前 return,记得确保 resp 被正确释放(aiohttp 通常自动处理,但自定义 connector 时得留心)。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










