Flask页面刷新时循环请求Discord API返回KeyError故障如何解决
故障原因
- 核心触发原因是Discord Bot API触发了速率限制:你当前代码每个用户ID要发起2次完全重复的GET请求查询用户信息,请求量直接翻倍,短时间内频繁刷新/定时请求会很快触达Discord的接口调用频次限制,此时接口返回的是速率限制报错响应,结构体中不存在
username、discriminator字段,直接硬取字段就会抛出KeyError。请求间隔越短,触发速率限制的速度越快,和你观测到的报错规律完全匹配。 - 缺少请求异常校验逻辑:代码未对Discord接口的返回状态码、响应结构做任何校验,除了速率限制外,Token失效、用户ID无效、网络波动等场景都会导致返回结构不符合预期,直接取字段必然报错。
- 可选排查点:Flask默认以多线程模式运行,模块级全局变量如果在多个请求中同时被修改,可能出现变量内容被篡改的情况,也可能间接导致取到异常的userid值进而请求失败。
修复方案
1. 优化接口请求逻辑,合并重复请求
同一个用户ID只发起一次请求,直接取全量需要的字段,同时添加本地缓存避免重复查询,从根源上降低请求频次:
import time # 提前初始化缓存,可放在模块顶部,避免重复查询 user_cache = {} CACHE_EXPIRE = 3600 # 缓存有效期1小时,可自行调整 # 原第一个循环优化后逻辑 for uid in useridlist[:5]: # 取前5个,和原range(0,5)逻辑一致 user_tag = "" # 先查缓存,有有效数据直接用 if str(uid) in user_cache and time.time() - user_cache[str(uid)]['update_time'] < CACHE_EXPIRE: user_tag = user_cache[str(uid)]['tag'] else: # 每个用户只发一次请求 resp = requests.get( f"https://discord.com/api/v9/users/{uid}", headers={"Authorization": "Bot BOTTOKEN"} ) # 校验响应状态 if resp.status_code != 200: # 遇到速率限制可先重试,或者直接兜底用uid展示 if resp.status_code == 429: retry_after = resp.json().get('retry_after', 1) time.sleep(retry_after) # 重试一次,也可以跳过重试直接走兜底逻辑 resp = requests.get( f"https://discord.com/api/v9/users/{uid}", headers={"Authorization": "Bot BOTTOKEN"} ) # 请求还是失败就用uid兜底,避免报错 user_tag = f"UID:{uid}" else: user_data = resp.json() # 用get方法取值,避免字段缺失抛出KeyError username = user_data.get('username', '未知用户') discriminator = user_data.get('discriminator', '0000') user_tag = f"{username}#{discriminator}" # 写入缓存 user_cache[str(uid)] = { 'tag': user_tag, 'update_time': time.time() } userIDtoName.append(user_tag)
2. 优化后续循环逻辑,添加兜底校验
对coins字典的取值也添加容错逻辑,避免用户ID不存在时抛出KeyError:
# 取三个列表的最小长度作为循环边界,避免索引越界 loop_len = min(5, len(useridlist), len(userIDtoName)) for j in range(loop_len): user_coins = coins.get(str(useridlist[j]), 0) coinslist.append(user_coins) loop_len = min(5, len(userIDtoName), len(coinslist)) for l in range(loop_len): leaderboardlist.append(f"{userIDtoName[l]} => {coinslist[l]}")
3. 可选优化(规避全局变量线程安全问题)
如果存在多请求并发场景,建议不要使用模块级全局变量存储本次请求的临时数据,改为在请求函数内部初始化userIDtoName、coinslist、leaderboardlist变量,避免多线程互相篡改数据。
内容的提问来源于stack exchange,提问作者TheGiorno
相关产品推荐
相关产品推荐

