API分页拉取报错'NoneType' object is not iterable
报错核心原因
- 字段取值逻辑存在漏洞:
data.get('Referral', [])仅当Referral这个key完全不存在于响应字典中时,才会返回默认空列表[]。如果API返回的响应里Referralkey存在,但值为null(对应Python的None类型),get方法会直接返回None,对None做遍历就会抛出你看到的TypeError: 'NoneType' object is not iterable。这也是代码偶发报错的直接原因:请求页码在有效范围内时接口返回正常列表结构,代码可正常运行;一旦请求超出总页数、或遇到接口临时异常,Referral字段就可能返回null触发报错。 - 分页逻辑是没有终止条件的死循环:代码里没有判断分页结束的逻辑,会一直递增页码无限请求,必然会触发接口返回非预期结构。而且你写的空列表判断逻辑完全无效:空列表本身不会进入for循环,写在循环内部的
if len(data['Referral']) == 0永远不会执行,同时print(done)里的done变量从未定义,真走到这行还会触发NameError。 - 缺少基础的请求和响应校验:没有判断HTTP请求状态码、没有处理JSON解析失败的场景、也没有请求重试机制,遇到网络波动、接口限流、鉴权临时失效时,很容易拿到非预期的响应内容直接崩溃。
- 后续的
referrals.extend(data['Referral'])逻辑同样没有做类型校验,就算遍历那行不报错,Referral为None时调用extend也会抛出同样的类型错误。
稳定运行修复方案
- 给请求层加异常捕获和重试机制:单页请求失败时自动重试,超时、状态码异常时不要直接崩溃,重试多次失败再终止本次任务。
- 统一做字段类型校验:取
Referral字段时,不仅要判断key是否存在,还要判断值的类型是不是列表,只要不是列表就直接终止分页,避免遍历/extendNone类型报错。 - 增加明确的分页终止条件:当当前页返回的列表长度为0时,直接跳出循环结束拉取,不要无限请求下一页。
- 清理无效死代码,去掉未定义的变量引用,所有取值逻辑统一做兜底处理。
修复后的完整代码如下:
import requests import schedule import time def eRelo_schedule(): parameters = { "CreatedAfterUTC": "2020-01-01T00:00:00", "RecordsPerPage": 2000 } headers = { "Content-Type" : "application/json", "Authorization": "AUTHORIZATION_TOKEN", } referrals = [] pagenum = 1 max_retry = 3 retry_sleep = 2 while True: url = f"https://restapi.erelocation.net/api/v1/GetReferrals?PageNum={pagenum}" print(f"正在请求: {url}") # 请求重试逻辑 resp = None for retry_cnt in range(max_retry): try: resp = requests.get(url, headers=headers, params=parameters, timeout=30) resp.raise_for_status() break except Exception as e: print(f"第{pagenum}页请求失败,第{retry_cnt+1}次重试,错误: {str(e)}") time.sleep(retry_sleep) if retry_cnt == max_retry - 1: print(f"第{pagenum}页重试{max_retry}次均失败,终止本次拉取任务") return referrals # 响应格式校验 try: data = resp.json() except Exception as e: print(f"第{pagenum}页响应不是合法JSON格式,错误: {str(e)},终止拉取") return referrals # 字段类型兜底校验 current_page = data.get('Referral') if not isinstance(current_page, list): print(f"第{pagenum}页返回数据结构异常,已到分页末尾或接口报错,终止拉取") break # 空列表判定为拉取完成 if len(current_page) == 0: print(f"所有分页拉取完成,总计获取{len(referrals)}条数据") break referrals.extend(current_page) print(f"第{pagenum}页拉取完成,获取{len(current_page)}条数据") pagenum += 1 # 后续可在这里追加数据存储逻辑,比如转DataFrame存CSV、写数据库等 return referrals if __name__ == "__main__": # 脚本启动时先执行一次,无需等待24小时 eRelo_schedule() schedule.every(24).hours.do(eRelo_schedule) while True: schedule.run_pending() time.sleep(1)
内容的提问来源于stack exchange,提问作者Wanthouser1
相关产品推荐
相关产品推荐

