
当 api 返回的 json 中某些字段(如 `child_sku_options`)实际是经过 html 实体编码的 json 字符串时,直接 `json.loads()` 会失败;需先用 `html.unescape()` 还原转义字符,再二次解析。
在调用某些搜索服务(如 SearchSpring)的 API 时,常见一种“伪嵌套 JSON”现象:主响应是合法 JSON,但其中某个字符串字段(例如 "child_sku_options")的值并非原始 JSON 对象,而是被双重编码的 JSON 字符串——它被 HTML 实体转义(如 \/ 代替 /、" 替代 "),导致直接解析报错或输出乱码。
你的原始代码:
print(json.loads(response.content))
看似合理,但问题出在 response.content 解析后得到的 data["results"][i]["child_sku_options"] 是一个字符串形式的、被 HTML 转义的 JSON,例如:
"{\"option_value_id\":197674,\"value\":\"12 Degree\",...\"image_url\":\"https:\\/\\/cdn11.bigcommerce.com\\/...jpg\"}"该字符串中的 \/ 和 \" 是 HTML/XML 编码结果,并非标准 JSON 允许的原始反斜杠,因此无法被 json.loads() 直接识别。
立即学习“前端免费学习笔记(深入)”;
✅ 正确处理流程如下:
- 先解析顶层 JSON:使用 response.json() 或 json.loads(response.content) 获取主数据结构;
- 定位被编码的字段:如 r["child_sku_options"](类型为 str);
- HTML 解码:调用 html.unescape() 消除 "、/、\/ 等转义,还原为标准 JSON 字符串;
- 二次 JSON 解析:对解码后的字符串执行 json.loads(),得到真正的 Python 字典/列表。
以下是完整、健壮的示例代码(含错误处理和推荐实践):
import json
import requests
from html import unescape
url = "https://547os1.a.searchspring.io/api/search/search.json?ajaxCatalog=v3&resultsFormat=native&siteId=547os1&domain=https%3A%2F%2Fwww.golfbox.com.au%2Fsearch%3Fq%3DCalaway%2BParadym%2BDriver%26Search%3DSEARCH&q=Calaway%20Paradym%20Driver"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36"
}
try:
response = requests.get(url, headers=headers, timeout=10, verify=False)
response.raise_for_status() # 检查 HTTP 错误状态码
# 解析顶层 JSON
data = response.json()
# 遍历搜索结果,提取并解析 child_sku_options
for i, result in enumerate(data.get("results", [])):
options_str = result.get("child_sku_options")
if not isinstance(options_str, str) or not options_str.strip():
print(f"[跳过] 第 {i+1} 条结果无 child_sku_options 字段")
continue
try:
# 步骤1:HTML 解码 → 还原为标准 JSON 字符串
decoded_str = unescape(options_str)
# 步骤2:JSON 解析 → 转为 Python 对象
options_list = json.loads(decoded_str)
print(f"✅ 第 {i+1} 条结果解析成功,共 {len(options_list)} 个 SKU 选项:")
# 示例:打印首个选项的 child_sku 和 price
if options_list:
first = options_list[0]
print(f" - SKU: {first.get('child_sku', 'N/A')}, Price: ${first.get('price', 'N/A')}")
except json.JSONDecodeError as e:
print(f"❌ 解析第 {i+1} 条的 child_sku_options 失败:{e}")
print(f" 原始字符串(截取前100字符):{options_str[:100]}...")
except Exception as e:
print(f"⚠️ 其他异常:{e}")
except requests.exceptions.RequestException as e:
print(f"网络请求失败:{e}")
except json.JSONDecodeError as e:
print(f"顶层 JSON 解析失败:{e}")? 关键注意事项:
- ❌ 不要禁用 SSL 验证(verify=False)用于生产环境——它会带来安全风险。如遇证书问题,请更新 CA 证书或使用 requests.packages.urllib3.util.ssl_.DEFAULT_CIPHERS 调整 TLS 设置,而非关闭验证。
- ✅ 优先使用 response.json() 而非 json.loads(response.content),前者自动处理编码(如 UTF-8 BOM、Content-Type charset),更可靠。
- ✅ 始终校验字段是否存在(dict.get())、类型是否正确(isinstance(..., str)),避免 KeyError 或 TypeError。
- ? 若发现 unescape() 后仍解析失败,可用 print(repr(decoded_str)) 查看真实字符串内容,确认是否含不可见控制字符(如 \u2028 行分隔符),必要时用正则清洗。
通过这一“HTML 解码 + 二次 JSON 解析”的组合策略,你就能稳定提取 SearchSpring 等平台返回的嵌套结构化商品选项数据,彻底告别 \/ 和 " 带来的解析困扰。











