Token过期导致401错误时,应动态管理Token生命周期:用时间戳缓存过期时间、每次请求前调用获取函数、动态注入headers,避免硬编码或定时任务依赖。

Token过期导致401错误时,不能只重试请求
爬虫跑着跑着突然大量返回 401 Unauthorized,不是网络问题,也不是IP被封,而是 Authorization 请求头里的 Token 失效了。这时候单纯加 time.sleep() 或换 User-Agent 没用——鉴权凭据本身已作废。核心思路是:把 Token 当成一个有生命周期的变量,而不是写死在 headers 字典里。
- Token 通常带
expires_in字段(单位秒),或由 JWT 的exp声明决定有效期 - 硬编码 Token 或每次请求都重新登录,既不安全也不高效
- 必须区分「获取 Token」和「使用 Token」两个阶段,且前者不能嵌在主爬取循环里
用独立函数封装 Token 获取逻辑,并缓存有效期
把登录/换 token 的过程抽成一个可复用函数,关键是要记录它什么时候会过期,避免重复请求认证接口。比如调用 OAuth2 的 /oauth/token 接口后,立刻解析响应中的 expires_in,算出绝对过期时间戳存起来。
import time
import requests
<p>_token_cache = {"value": None, "expires_at": 0}</p><p>def get_access_token():
if time.time() < _token_cache["expires_at"]:
return _token_cache["value"]</p><pre class="brush:php;toolbar:false;">resp = requests.post("https://api.example.com/oauth/token", data={
"grant_type": "client_credentials",
"client_id": "xxx",
"client_secret": "yyy"
})
data = resp.json()
_token_cache["value"] = data["access_token"]
_token_cache["expires_at"] = time.time() + data["expires_in"] - 30 # 提前30秒刷新
return _token_cache["value"]
仅使用 Python 将 Claw Code 虚拟化框架与 OpenClaw 集成,提供 184 种工具和 200 多条命令,用于安全分析和代码质量...
立即学习“Python免费学习笔记(深入)”;
- 减去 30 秒是为了留出网络延迟和处理时间,防止刚用就过期
- 不要用
datetime.now() + timedelta,浮点时间戳更易比较 - 多线程环境下需加锁,但简单脚本用单线程+全局变量足够
在 requests headers 中动态注入最新 Token
每次发请求前调用 get_access_token(),而不是初始化一次后反复用。注意别在 session 初始化时就写死 headers,否则后续 token 更新不会生效。
session = requests.Session()
# ❌ 错误:headers 写死,token 不会更新
# session.headers.update({"Authorization": f"Bearer {token}"})
<h1>✅ 正确:每次请求前生成 headers</h1><p>def make_api_call(url):
headers = {
"Authorization": f"Bearer {get_access_token()}",
"Content-Type": "application/json"
}
return session.get(url, headers=headers)
- 如果用
scrapy,应在start_requests或make_requests_from_url中动态构造headers - 若依赖
requests.Session的连接复用,headers 动态构造不影响性能,底层 TCP 连接仍可复用 - 某些 API 要求
Authorization是Bearer xxx,有些是Token xxx,看文档确认格式
定时任务只用于兜底,不替代实时校验
用 APScheduler 或系统 crontab 定期刷新 Token,听起来稳妥,但实际容易出问题:任务失败没通知、执行时间漂移、多个进程竞争写同一缓存文件。它更适合做「保底刷新」,而非主要机制。
- 优先靠「按需获取 + 缓存过期时间」机制运行;定时任务只是防止极端情况(如程序长时间没发起请求,缓存时间 drifted)
- 若真要用 APScheduler,用
BackgroundScheduler启动一个单独线程,每 10 分钟调用一次get_access_token()即可 - 切忌让定时任务直接修改全局 headers 字典——
requests.Session.headers是引用类型,多线程下可能引发竞态
Token 的本质是状态,而状态管理最容易被忽略的点,就是没定义清楚「谁负责更新」「何时失效」「失效后如何降级」。缓存时间戳比轮询或固定间隔更准,也比每次请求都登录更省资源。真正麻烦的从来不是怎么拿新 token,而是怎么让所有发出去的请求都用上它,且不互相干扰。

















