为什么自动化总在第三天失效:会话、Cookie 与设备信任的真实生命周期
现象:第一天好用,第三天失效
做浏览器自动化最常遇到的抱怨是这个:脚本昨天跑得好好的,今天一跑全是 401/403,登录页。你重新登录一次,又能跑,过两天再挂。
多数人第一反应是「脚本不稳定」,其实不是。是凭证的生命周期你没搞清。
一、三种凭证,三种寿命
登录之后浏览器/客户端手里其实握着好几样东西,它们的失效规则完全不同:
| 类型 | 特征 | 什么时候失效 |
|---|---|---|
| 会话 Cookie(Session Cookie) | 没有 Expires/Max-Age |
关闭浏览器就没了 |
| 持久 Cookie | 带 Expires |
到期或服务端主动作废 |
| 令牌(Token) | 独立的 AK/SK、OAuth refresh token | 按各自 TTL,通常可续期 |
关键在第一种。很多人以为「我登录过一次,浏览器就记住了」,但如果那个关键的 SessionID 是会话 Cookie,它只活在内存里,进程一退就没了——硬盘上根本找不到。
二、我实测的一组对照
用同一套自动化跑了两周,记录每次重启浏览器后的状态:
| 场景 | 结果 |
|---|---|
| 关浏览器后立刻重开(同一台机器) | 需要重新登录,但不需要短信验证码 |
| 同一会话内连续操作 | 正常,无过期 |
| 换一个浏览器配置目录 | 完全未登录 |
第一行值得展开:登录态是没了,但「这台设备可信」的标记还在。所以重新登录只要输账号密码,跳过了二次验证。
这说明两件事是分开存的:会话(在 Cookie 里,随进程消失)和信任关系(在服务端,绑设备指纹,能活很久)。
三、为什么这个区别很重要
如果你的自动化依赖「会话 Cookie」,那它天然只能在同一个浏览器进程活着的时候工作。一旦进程重启,就必须重新登录。
这意味着:
- 不能把「登录成功」当成一次性的初始化。每次启动都要检查登录态,不能假设它还活着;
- 不要试图把会话 Cookie 存下来复用。服务端通常会绑定 IP、UA、甚至设备指纹,换个上下文就失效,还会触发风控;
- 「信任设备」是值得主动做的。它能把重复登录的成本从「收短信」降到「输密码」,差别很大。
四、脚本该怎么处理过期
最容易被忽略的一点:凭证过期是正常分支,不是异常。
反面写法(我一开始就是这样):
if (!response.ok) {
throw new Error('请求失败: ' + response.status);
}
问题在于 401 和 500 被混在一起,日志里看不出「只是该重新登录了」,于是你会去查网络、查参数、查代理——方向全错。
正面写法是把状态显式区分:
if (response.status === 401 || isLoginPage()) {
log('凭证已失效,需要人工重新登录');
return EXIT_NEED_LOGIN; // 明确的退出码
}
给一个专门的退出码,上游调度器才能据此决定「是报警叫人,还是自动重试」。这比在日志里翻半天有效得多。
五、一个更省事的思路
如果目标系统支持长期凭证(比如 API Key、OAuth refresh token),优先走这条路,别去自动化网页登录。
网页登录的自动化有三个固有麻烦:验证码、风控、以及每次都要处理 UI 变动。而 API 凭证只需要处理「过期」这一种情况,而且通常可以续期。
反过来,如果只能用网页登录,那就要接受:这件事必须有人在旁边。把它设计成「半自动」——脚本负责干活,登录这一步交给人——反而更稳定,也不容易因为绕过风控把账号搞掉。
小结
自动化不稳定,八成不是代码问题,是你没搞清手里那几个凭证分别什么时候死。
三个动作能解决大部分困惑:查一次 Cookie 有没有 Expires;重启浏览器验证一次真实行为;把「需要登录」写成明确的退出状态而不是异常堆栈。
- 点赞
- 收藏
- 关注作者
评论(0)