如何避免JSON返回NoneType导致Python代码报错并提升鲁棒性
问题:Python处理REST API返回JSON的空值异常优化
场景与问题
通过REST API获取如下JSON格式数据:
{ "itineraryIntent": { "itineraryId": 2446, "userNodes": [ { "user": { "id": "-2", "givenName": "Wiliam Never", "preferredAirport": "", "travelerGroupId": null, "externalId": null, "externalId2": null, "checkCCFieldForTraveler": false, "notificationEmailOverride": null, "paxType": 0, "title": "Dr", "middleName": null, "dateOfBirth": "1980-01-15T00:00:00Z", "phoneNumber": null, "tsaInfo": null, "source": "RequestEndPoint", "allowBookingGuestTraveler": null, "gdsTravelerProfileContainsFirstName": false, "profilesFound": { "Agency": null, "Corporate": null, "Traveler": null }, "userId": "-2", "firstName": "Wiliam", "lastName": "Never" }, "elements": [] } ] } }
原提取代码如下,80%场景可用,但偶尔因details["itineraryIntent"]["userNodes"]为null,抛出TypeError: 'NoneType' object is not subscriptable异常:
details = response.json() checkIntent = details["itineraryIntent"]["userNodes"][0]["elements"] if len(checkIntent) == 0: print("No intent found, nothing is classified") return False else: return True
此前采用固定sleep 10秒的临时方案,但多数场景仅需4-5秒即可获取有效响应,需优化代码实现鲁棒性,无需依赖固定sleep。
优化方案
核心思路
- 安全取值:避免直接下标访问,用
dict.get()逐层获取字段并设置默认值,防止空值或不存在的键引发异常 - 智能重试:替代固定sleep,采用递增延迟的重试策略,适配不同场景的响应速度
- 全面异常捕获:覆盖网络请求、JSON解析等多种异常,避免程序崩溃
优化后代码
import time import requests def check_itinerary_intent(response): # 逐层安全获取字段,设置默认值避免空值异常 itinerary_intent = response.json().get("itineraryIntent", {}) user_nodes = itinerary_intent.get("userNodes", []) # 校验userNodes是否有效 if not user_nodes: return False # 安全获取第一个节点的elements elements = user_nodes[0].get("elements", []) if len(elements) == 0: print("No intent found, nothing is classified") return False return True def fetch_and_check_intent(api_url, max_retries=3, initial_delay=4): retries = 0 while retries < max_retries: try: # 发送请求并校验HTTP状态码 response = requests.get(api_url) response.raise_for_status() # 检查intent是否有效 intent_result = check_itinerary_intent(response) if intent_result: return True # 若intent为空但userNodes有效,直接返回结果 return False except (requests.exceptions.RequestException, ValueError) as e: print(f"处理错误: {str(e)}") # 仅当userNodes为空时重试,采用递增延迟 retries += 1 delay = initial_delay + (retries - 1) print(f"userNodes未就绪,{delay}秒后重试(第{retries}/{max_retries}次)") time.sleep(delay) print("重试次数耗尽,未获取有效数据") return False
优化点说明
- 安全取值:用
dict.get()替代直接下标访问,对每个层级的字段设置默认值(空字典/空列表),彻底避免None下标访问异常和KeyError - 智能重试:采用递增延迟策略(首次4秒,后续每次加1秒),既适配多数场景的快速响应,也给少数需要更长等待的请求留出时间,替代低效的固定10秒sleep
- 职责拆分:将数据校验和请求重试逻辑拆分,代码更清晰易维护
- 异常覆盖:捕获网络请求错误(如连接超时、HTTP错误)和JSON解析错误,确保程序稳定运行
内容的提问来源于stack exchange,提问作者Rustom Contractor
相关产品推荐
相关产品推荐

