
本文讲解如何在 python 中安全访问 api 返回的嵌套 json 字段(如 `normaltime`、`period1` 等),通过 `.get()` 方法优雅处理缺失键,防止 `keyerror` 中断程序,并提供默认值(如 `999`)或合理回退逻辑。
在调用 SofaScore 等第三方体育 API 时,常见问题之一是:部分比赛数据尚未生成(例如未开赛或已取消),导致 homeScore 或 awayScore 对象中缺少 normaltime、period1 等字段。直接使用 game['homeScore']['normaltime'] 会触发 KeyError: 'normaltime',程序立即崩溃。
根本原因在于——Python 字典的方括号语法 d[key] 要求键必须存在,否则抛出异常;而更健壮的做法是使用 .get(key, default) 方法:它在键不存在时返回 None(或你指定的默认值),而非报错。
以下为优化后的核心实践:
✅ 正确做法:逐层使用 .get() 安全取值
对任意可能缺失的嵌套层级(如 game → homeScore → normaltime),均应链式调用 .get(),并设置合理默认值。例如:
homescore = game.get("homeScore", {}).get("normaltime", 999)
awayscore = game.get("awayScore", {}).get("normaltime", 999)
homehalf = game.get("homeScore", {}).get("period1", 0) # 上半场比分默认为 0
awayhalf = game.get("awayScore", {}).get("period1", 0)⚠️ 注意命名冲突:避免覆盖内置函数
原代码中使用 round = game['roundInfo']['round'] 将变量命名为 round,这会覆盖 Python 内置函数 round(),可能导致后续数值四舍五入等操作异常。应改用 game_round 或 round_name 等语义化名称。
✅ 安全提取赛事轮次信息(含多级 fallback)
roundInfo 本身也可能为空,因此需先检查其存在性,再取子字段:
round_info = game.get("roundInfo")
if round_info:
round_name = round_info.get("name") or f"Round {round_info.get('round', '?')}"
else:
round_name = "Not Scheduled"? 完整修复示例(含时间格式化与错误兜底):
from datetime import datetime
import requests
import json
url = "https://api.sofascore.com/api/v1/sport/football/scheduled-events/2024-02-01"
headers = {
"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"origin": "https://www.sofascore.com",
"referer": "https://www.sofascore.com/",
}
response = requests.get(url, headers=headers)
jsondata = response.json()
for game in jsondata.get("events", []):
try:
league = game.get("tournament", {}).get("name", "Unknown League")
hometeam = game.get("homeTeam", {}).get("name", "N/A")
awayteam = game.get("awayTeam", {}).get("name", "N/A")
# 安全获取比分(缺失时返回 999)
homescore = game.get("homeScore", {}).get("normaltime", 999)
awayscore = game.get("awayScore", {}).get("normaltime", 999)
homehalf = game.get("homeScore", {}).get("period1", 0)
awayhalf = game.get("awayScore", {}).get("period1", 0)
# 安全获取开始时间
timestamp = game.get("startTimestamp")
time_str = datetime.fromtimestamp(timestamp).strftime("%H:%M") if timestamp else "TBD"
# 安全获取轮次
round_info = game.get("roundInfo", {})
round_name = round_info.get("name") or f"Round {round_info.get('round', '?')}"
print(f"{league} | {hometeam} - {awayteam} ({homehalf}-{awayhalf}) {homescore}-{awayscore} | {time_str} | {round_name}")
except Exception as e:
print(f"[Warning] Failed to parse game: {e}")
continue? 关键总结:
- ✅ 永远对第三方 API 响应做防御性编程:假设任何字段都可能缺失;
- ✅ 优先使用 .get(key, default) 替代 ['key'];
- ✅ 对嵌套结构(如 game['homeScore']['normaltime'])务必分步 .get(),并在中间层提供空字典 {} 作为 fallback;
- ✅ 避免使用 round、list、str 等内置名作变量;
- ✅ 添加 try...except 包裹单条记录解析,确保一条失败不影响整体流程。
这样即可稳定运行,无论数据是否完整。










