首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >派猫猫旅行巡检自动化- 从建任务到成功落地的完整复盘

派猫猫旅行巡检自动化- 从建任务到成功落地的完整复盘

原创
作者头像
用户12683097
发布于 2026-09-29 09:33:03
发布于 2026-09-29 09:33:03
30
举报

派猫猫旅行巡检自动化-

从建任务到成功落地的完整复盘

关键词:定时自动化 · Bearer 鉴权 · Cookie 鉴权 · Chrome DevTools Protocol · App-Bound Encryption · Keycloak JWT · 运行期内存取证

时间跨度:2026-09-01 → 2026-09-29 | 适用读者:做过网页签到、定时脚本、浏览器自动化的开发者

封面:WorkBuddy 成长计划 · 派猫猫旅行入口

一句话结论把"每天手动点网页领积分"这件小事做成无人值守的定时任务,中途撞上 401 鉴权故障;排查一圈后确认真因是脚本缺 Authorization: Bearer 凭据,而不是平台风控;最终用「令牌直连 + 进程内存取证」把链路做稳,并已实测积分真实到账。

目录

一、建任务:背景与目标

二、故障:过程中出现的具体问题

三、排查思路与解决方案(含经验与警示)

四、成果:当前已成功实现

附录:关键判断命令(备查)

一、建任务:背景与目标

1.1 需求从哪来

平台的成长中心里有一个叫「派猫猫旅行」的小玩法:把虚拟宠物派出去旅行,到达目的地后回来能领积分。积分本身不大,但"每天要卡着时间点去点三次"(派出 → 查看是否到达 → 领取)实在琐碎,属于典型的"懒人自动化"需求。

图 A1 任务入口:客户端头像 → 成长计划 → 派猫猫旅行

1.2 玩法机制:它不是一个静态按钮

先要把玩法摸清楚,才能定自动化的状态机。据实拍页面,这个玩法的三条关键规则是:

· 可选地点:咖啡馆、古镇客栈、健身房、商场店铺等多个地点,每次随机一个;

· 随机 1~4 小时:从"确定派出"到"回来"的时长不固定,这是自动化的核心难点——不能派出后立刻领取;

· 回来可领奖励:旅行结束后宠物带回见闻和奖励(积分),需要再次进入页面领取。

图 A2 玩法机制:可选地点、随机 1~4 小时、回来可领奖励

1.3 目标定义

目标

验收标准

无人值守

每天 4 次巡检(08:15 / 12:30 / 16:45 / 21:00)+ 1 次凭据续期(05:40),无人工介入

幂等不漏不重

状态机驱动:arrived → 领取积分;traveling → 跳过,不重复派出;daily_limit_reached → 跳过;idle → 随机派出一个地点

不碰 GUI

优先纯 HTTP 直连,不起浏览器、不截图 OCR、不模拟点击

可追溯

每轮结果写入日志与状态快照,异常按固定口径报告;不自动重试、不代替登录

1.4 技术选型:三层降级架构

定时任务(05:40 续期 / 08:15 / 12:30 / 16:45 / 21:00)

└─ run_travel.py 统一入口:令牌优先,失败自动降级

├─ travel_auto_token.py 主通道:取 Bearer 令牌,urllib 直连(无浏览器 / 无 cookie)

├─ travel_auto_web.py 备用:cookie + Chrome UA 纯 HTTP 直连

└─ run_travel_cdp.py 兜底:离屏 Edge,页面内 fetch 代发

接口路径统一为 https://www.workbuddy.cn/activity/growth/buddy/travel/{status,depart,claim}(脚本内不带 /v2/ 前缀)。

图 1 现行鉴权模型与三层降级架构

二、故障:过程中出现的具体问题

从 09-01 到 09-29,链路一共被打断五次,问题逐层递进。

2.1 第一道坎(09-01):浏览器自动化通道冷启动即失效

· 现象:首跑即 state=error。自动化用专用 chrome-profile 冷启动打开成长中心,页面被重定向到 Keycloak 微信扫码登录页,拿不到任何业务状态。

· 排查:cookie 加密前缀是 v10(非 v11,说明没有 app-bound 加密封装层),自动化也能读到明文 KEYCLOAK_SESSION="copilot/68d9b511-…"——即"解密没问题"。

· 真因:这份 session 在服务端已经过期(其来源浏览器被关闭后会话失活)。凭据物理存在 ≠ 服务端认账。

· 影响:当日余下 3 回在无人介入前必然全错。

2.2 第二阶段(09-02 ~ 09-15):cookie 直调跑通,但凭据本身很脆

对策是彻底绕开浏览器:读 wb_cookies.txt 的会话 cookie,配 Chrome UA 纯 HTTP 直调三个接口。09-02 首跑即成功,方案看起来找到了。这段跑了 14 天,期间共 14 次领取成功、合计 +56 分,还有 2 次判断 idle 主动派出。但隐患一直在:

· 09-08 12:30 ~ 09-09 07:17:连续多轮 ERROR auth_expired HTTP 401,cookie 中途失效;07:23 重新拷贝 cookie 后恢复。

也就是说,凭据的生命周期完全不受控——它什么时候失效、失效后脚本如何自愈,当时都没有答案。

2.3 第三阶段(09-16 ~ 09-17):全链路 401

· 09-16 08:15 起,巡检在调 status 接口这一步就直接 ERROR auth_expired HTTP 401,连 state 都拿不到,自然无从派出与领取;

· 09-17 自 07:56 起连续 4 回报同一 401,排除偶发;

· 现象很"干净"但也最迷惑人:脚本侧全 401,手动在浏览器里打开同一页面却一切正常——第一反应就像"被风控认出来了,真人没事"。

这个误判是后面所有弯路的起点,详见第三部分。

2.4 第四阶段(09-25):兜底通道也失效

切到 Edge CDP 通道后,包装器自动跑了 refresh_cookies.py(用扩展导出 10 条明文 cookie 注入 CDP profile,落盘校验 workbuddy cookie=11 条),但重跑仍然 401。

根因:默认 Edge profile 里 www.workbuddy.cn 的会话在服务端已失效——cookie 物理存在但服务端拒绝,refresh 只是复制了一份"死 cookie",无法自愈。

2.5 第五阶段(09-28 ~ 09-29):令牌通道被"加密信封"打断

已经在走令牌主通道了,09-29 08:15 那一轮却报 no_credentials:

· 桌面端把 auth.accessToken 从明文字符串改成了字段级信封加密:{"$wbEncrypted":1,"envelope":"…"}(AES-256-GCM,主密钥运行时注入、磁盘上不留 keyblob);

· 原脚本仍按明文字符串去读,读到的是 dict → 直接判定"凭据缺失";

· 该轮自动降级到备用 cookie 通道才跑通(traveling,幂等跳过)。

2.6 问题清单

时间

现象

错误

当时判断

实际性质

09-01

冷启动被重定向到扫码登录页

state=error

浏览器 profile 坏了

凭据(session)服务端已过期

09-08/09-09

多轮 401,重拷 cookie 后恢复

HTTP 401

偶发抖动

凭据生命周期不受控

09-16 ~ 09-17

status 接口即 401,连读 4 回

auth_expired HTTP 401

误判为风控指纹封锁

脚本缺 Bearer 凭据

09-25

兜底通道 refresh 后仍 401

HTTP 401

通道选型有误

兜底 profile 会话服务端失效

09-29

令牌通道报凭据缺失

no_credentials

客户端未登录

落盘令牌改为加密信封

图 2 故障根因:缺 Bearer 凭据为主因,原"风控 + Cookie 过期"降级为误判

三、排查思路与解决方案

3.1 第一轮思路(误判):认定"平台上了风控指纹封锁"

被"脚本 401、手动正常"带偏后,方向定成了"如何让请求看起来像真浏览器",于是走了 A—E 五条弯路。

图 3 排查时间线(09-15 故障爆发 → 09-26 根因更正 → 09-29 修复定稿)

方案 A:--remote-debugging-port=9222 挂上已登录的 Chrome(临时救急)

# 保持这个 Chrome 一直开着,自动化通过 CDP 连上去复用会话

chrome.exe --remote-debugging-port=9222 --user-data-dir="你的默认数据目录"

改动最小、立刻能用,但把"无人值守"退化成"有人值守"(关机即 401);且现代 Chrome 自 137 起硬性拒绝在默认 profile 上开调试端口(报错 DevTools remote debugging requires a non-default data directory),连救急都不稳。

方案 B:专用 CDP 调试目录 + 扩展导出 Cookie

脚本每次离屏自启一个独立调试目录的浏览器(非默认目录,端口能开),再把默认 profile 的登录态 cookie 喂给它。读明文 cookie 的稳妥做法是在浏览器内部用扩展的 chrome.cookies API 读、POST 给本地服务、最后 CDP Storage.setCookies 注入。

默认 profile(你登录着) 专用调试目录(脚本自启、离屏)

└ 本地扩展读明文 cookie ──POST──▶ 本地接收服务 ──CDP setCookies──▶ 注入并落盘

方向不错,但命门是 --load-extension——Chrome 品牌版已删除该开关(见方案 E)。

方案 C:直接 DPAPI 解密 Cookie 库(失败)

跳过浏览器直接读 Cookies 数据库解密。主密钥能解,但 cookie 值(v20,AES-GCM)解密抛 InvalidTag:实际用的是 app_bound_encrypted_key,密钥与"真实 profile 路径"绑定,脚本即便指向同一物理文件,只要浏览器不以内核路径打开,ABE 就判定密钥不匹配。

方案 D:junction / 符号链接伪装 profile 路径(危险,已酿事故)

想用 mklink /J 把默认 profile 链接成"看着像非默认"的目录。结果灾难:浏览器算出的 profile 路径是 junction 路径,与 ABE 绑定的真实路径不符,解密失败后按机制清空了整个 Cookies 库——实测默认 profile 里所有网站的登录态全部清零,且无 Cookies.bak 可恢复。

【警示】这条与技术误判无关,绝对成立任何试图用"假路径"骗过 Chromium 安全机制的方案,都会触发 ABE 的销毁逻辑。永远不要对浏览器默认 profile 做 junction / 软链 / 假 --user-data-dir。

方案 E:改用 Microsoft Edge(兜底通道)

关键发现:Google Chrome 自 137 起删除了 --load-extension,--disable-features=DisableLoadExtensionCommandLineSwitch 的变通自 142 起失效;该移除仅针对 Chrome 品牌版,Edge / Chromium / Chrome for Testing 不受影响。装 Edge 后 CDP 通道确实能跑,但它后来在 09-25 因默认 profile 会话服务端失效而失效(见 2.4)。

3.2 第二轮思路(真因定位,09-26):抓包比对,问题是"缺凭据"不是"被风控"

停下来做最基本的一件事——看响应到底是什么:

curl -i https://www.workbuddy.cn/activity/growth/buddy/travel/status

# → 401 Authorization Required(APISIX/3.9.1),只是"缺凭据"

curl -i -H "Authorization: Bearer <token>" <同一 URL>

# → 200 OK

· 同一 URL、同一网络、同一 UA:无凭据 → 401;带 Bearer → 200;

· 所谓"唯独浏览器能 200",只是因为只有浏览器里带着登录态(含令牌),与 TLS / HTTP2 指纹无关。

真因确认:接口靠 Authorization: Bearer <accessToken>(Keycloak OIDC JWT)鉴权,不是 HttpOnly Session Cookie。09-16 起的 401 是脚本缺 Bearer 凭据,不是平台风控升级。

修复方向因此收敛成一条:让脚本自己拿到并带上有效 Bearer 令牌。"绕过风控""刷新 Cookie"全是南辕北辙。

落地为方案 F:令牌直连——令牌由桌面端落盘在 %LOCALAPPDATA%\CodeBuddyExtension\Data\Public\auth\workbuddy-desktop.info 的 auth.accessToken(expiresIn≈55 天,桌面端运行时自动刷新,同含 auth.domain、account.uid),脚本读取后以 Authorization: Bearer <token> 直连,无浏览器、无 CDP、无 cookie。

3.3 第三轮思路(09-28 ~ 09-29 加固):从"静态读文件"转向"运行期取内存"

方案 F 只用了一天就被打断:09-28 起桌面端把 accessToken 改成信封加密落盘,静态读不到明文。思路上做了一次转弯:

关键突破密钥拿不到,但客户端发 HTTPS 请求时手里必然有明文。所以不去解密信封,直接从进程运行期内存里取。

方案 G:进程内存取证(现行主通道)

· 手段:只读扫描 WorkBuddy.exe / node.exe 的可读内存,抓取活体明文 JWT。不注入、不修改、不重启客户端,因此不会中断会话;

· 明文长度可反推:AES-GCM 无填充 → 密文长度 = 明文长度,据此得知 accessToken 1323 B、refreshToken 700 B;再用 nickname(2 汉字 = 6 B)、phoneNumber(11 B)交叉校验,这个启发式可靠;

· 多候选校验:内存里可能抓到多个候选串,逐个调 status 接口验证,第一个返回 200 且 code=0 的才采用;同一令牌同时出现在 PID 11324 / 22200 / 24240 三个进程中,是"真令牌"的强特征;

· 落盘与兼容:命中后写入临时文件供本轮使用、用完即清;若磁盘令牌仍是明文(旧版),直接走原逻辑,向后兼容;

· 部署:新增 travel_auto/fetch_token_from_memory.py;travel_auto_token.py 增加 load_token_from_memory() 并把取令牌改为磁盘 → 内存两级,状态快照新增 token_src 字段用于诊断。

这不是孤例,而是客户端层面的系统性变更:同一根因也命中另一条自动化(Buddy 加油站每日签到)——原脚本按明文字符串拼接 accessToken,遇到信封后直接 TypeError 崩溃;修复同样改用运行期内存取证(新建 checkin_headless_token.py,不动原脚本、不动登录态),实测当日领取成功。两条自动化因此共用同一套"信封不可解 → 内存取明文"的兜底范式。

3.4 方案对比一览

方案

无人值守

是否解决真因(缺 Bearer)

主要代价 / 风险

结论

A. 挂已登录 Chrome(端口常开)

否,需长期开浏览器

否(靠浏览器带令牌)

占资源、易断;新版 Chrome 默认 profile 端口被拒

仅救急

B. 专用 CDP 目录 + 扩展导出

是

否(仍在绕 cookie)

依赖 --load-extension

误判下的探索

C. DPAPI 直接解密

是(理论)

否

被 ABE 拦截(InvalidTag)

不可行

D. junction 伪装路径

—

—

清空全部 Cookie,不可逆

严禁

E. 改用 Edge(CDP 兜底)

是

被动(浏览器带令牌)

依赖 Edge 登录态,未登录即失效

保留为第三级兜底

F. Bearer 令牌直连(读磁盘明文令牌)

是

是

落盘改加密信封后失效

09-26~09-28 主通道

G. 令牌内存取证(磁盘 → 进程内存两级)

是

是

需桌面端常驻;扫描约 25~35 秒

现行主通道

3.5 现行链路

run_travel.py(统一入口,三级降级)

1) travel_auto_token.py → 取令牌:磁盘明文 → 进程内存取证(命中即用)

2) travel_auto_web.py → 备用:cookie + Chrome UA 纯 HTTP

3) run_travel_cdp.py → 兜底:离屏 Edge,页面内 fetch

前置条件:桌面端须常驻运行(内存里才有明文令牌)。客户端未运行时,自动降级到备用 / 兜底通道,不影响巡检本身。

安全与健壮性边界:

· 内存读取只读、不注入、不修改、不重启,不导出任何内容到外部;

· 401 自愈关闭浏览器实例时,改用 CDP Browser.close + 按 --user-data-dir 精确匹配,不再按进程名全杀浏览器(避免误伤正在使用的窗口)。

图 4 现行方案:令牌直连优先 + 三级降级兜底

3.6 经验与警示

1. "脚本 401、手动正常"先分清是「缺凭据」还是「真风控」。这次把"非浏览器客户端 401"直接归为风控,绕了一大圈。判据很简单:带头带对凭据还是 401 才是风控,带上就 200 就是缺凭据。先看响应体再下结论。

2. "取凭据"和"执行业务"要解耦。续期与巡检分开调度,是无人值守能成立的前提。

3. 凭据存储格式会变,别把读取方式写死。明文 → 信封加密只用了一天就发生了;健壮的取法应有多级来源(磁盘 → 内存 → cookie),并记录来源字段便于事后定位。

4. ABE 极其危险(与本次误判无关,绝对成立)。"路径不符即清空 Cookie"的机制意味着任何伪装路径都会清零登录态且无备份。永远用真实 profile 路径启动浏览器。

5. 浏览器是兜底,不是首选。当带凭据的纯 HTTP 就能 200 时,把浏览器留给真正需要它的场景——更轻、更稳。

四、成果:当前已成功实现

4.1 闭环验证:不是"页面显示成功",而是账户真的到账

先手动跑一次、再自动跑一次,两轮都拿到真实积分——两次都是"咖啡馆"地点,领取 +6 与 +5:

图 记录 1 / 记录 2 2026-08-31(第一次,手动)+6;2026-09-01(第二次,自动)+5

积分明细(实证):账户积分流水中可查到对应的两笔「官方活动发放」记录,与上面两次旅行一一对应。

图 积分明细:两笔官方活动发放记录

两轮合计 11 积分,到账可查——闭环成立。

图 A3 闭环验证:第一次手动、第二次自动,合计到账 11 积分

4.2 持续运行战绩(据 travel_auto.log 与巡检记忆统计)

阶段

通道

结果

领取

09-02 ~ 09-15

cookie 直调(Chrome UA)

首跑即通,14 次领取成功,2 次主动派出

+56 分

09-18 ~ 09-24

Edge CDP

09-19 起连续 6 天领取成功(+10 / +6 / +10 / +7 / +5 / +7)

+45 分

09-26 ~ 09-29

Bearer 令牌直连

派出 1 次(健身房)、其余轮次正常幂等跳过

稳定

合计自动化领取 101 分(不含 09-09 07:23 的一轮手动领取 +8,未计入)。

4.3 修复后的实测验证(09-29 08:53)

修复当天即以 --dry-run 端到端验证:

· 磁盘令牌 → 检测为加密信封,WARN 跳过;

· 进程内存提取命中 1323 字节 JWT(exp=2026-11-22,剩余约 54 天)→ 写入临时文件;

· 以该 Bearer + Chrome UA 直连 /status → 200,state=traveling;

· 入口判定"令牌通道成功(无浏览器)",退出码 0;临时文件已清理,状态已落盘。

4.4 当前形态与待办

已实现:三级降级链路落地;令牌主通道复活(不再依赖浏览器 cookie 导出,也不再受单个 cookie 30 天有效期约束);异常有固定报告口径,不自动重试、不代替登录;两条自动化(派猫猫旅行 / Buddy 加油站签到)共用同一套内存取证范式。

待优化(如实记录):

· 每天 05:41 紧接 05:40 续期后的那一轮曾多次 401:令牌通道先失败、回退的 CDP 兜底又因兜底 profile 未登录而失效。推测是 05:41 时令牌文件尚未就绪。建议把该轮巡检延后到令牌刷新完成之后,或保持兜底 profile 登录态。该轮常因额度 / 状态无需动作,业务影响小,属待核项。

· 内存扫描 25~35 秒,对每天 4 次的频次可接受,但若进程增多需关注耗时;

· 360 安全卫士可能弹出"进程内存读取"类提示——脚本只读、不注入,无副作用,可放行。

附录:关键判断命令(备查)

# 0) 判断是"缺凭据"还是"真风控"(本次定真因的关键判据)

# curl -i https://www.workbuddy.cn/activity/growth/buddy/travel/status

# → 401 Authorization Required(APISIX)= 只是缺凭据

# curl -i -H "Authorization: Bearer <token>" 同一 URL

# → 200 = 真因是缺 Bearer;仍 401 = 才是令牌失效 / 真风控

# 1) 验证某浏览器是否仍支持 --load-extension

# 临时 profile 启动 + 开 CDP 端口,查 Target.getTargets 是否出现 chrome-extension://<id>/sw.js

# Edge 153 出现;Chrome 153 不出现(仅内置组件扩展)

# 2) 验证默认 profile 能否开调试端口

# Chrome 153 报错:DevTools remote debugging requires a non-default data directory

# 专用(非默认)目录则可正常监听 9222

# 3) 读 Cookie 库过期时间(Chrome / Edge 通用,无需解密)

# Cookies 库:<User Data>/Default/Network/Cookies

# expires_utc 为 1601-01-01 起微秒;>0 为到期时间,=0 为 SESSION

# 4) 令牌落盘格式变化时先看结构再写代码

# 明文 → "eyJ...";加密 → {"$wbEncrypted":1,"envelope":"..."}

# 命中后者时静态无法还原,转运行期内存取证

本文所涉排查均在本机隔离环境内完成,未对平台做任何异常访问;Cookie 读取均在本地浏览器进程内、经用户已授权的扩展完成;Bearer 令牌仅从本机桌面端凭据文件或本地进程内存中读取,不外传。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档