我是一个开广告制作公司的,不是程序员。公司里从设计到出货全是活,我每天真正能坐在电脑前的时间很碎。WorkBuddy 客户端每天有签到积分和宠物旅行积分,加起来一天 100 出头,不多,但断一天就断连续登录,连续档位越高差得越多。
于是我让 AI 帮我把这件事彻底自动化:每天 09:30 一个定时任务,客户端自己醒来、自己签到、自己派猫出门、回来自己领。听起来像十行脚本,实际做了三周,踩了八个坑,其中一个差点点进付费页面。
这篇写的是跑通之后怎么让它稳定。每个坑按"错误现象、根因、修法"记下来。
第一反应是用浏览器自动化去操作网页版。但签到入口在 Electron 桌面客户端里,网页版没有;猫猫旅行虽然在网页,但需要已登录的会话。
所以最终方案是三层:
PrintWindow 把客户端窗口内容抓成位图(不是屏幕截图);三层都跑在本地,不依赖任何第三方付费服务,Windows 自带 OCR 就够用

现象:用最常规的 GetWindowDC + BitBlt 抓 Electron 窗口,得到的图 100% 纯白;想走无障碍接口(UIA)读控件树,FindAll 返回空数组。
根因:Electron 用 GPU 合成渲染,窗口内容不在 GDI 表面上,普通 BitBlt 抓不到;同时它默认不导出无障碍树,除非显式开启。
修法:改用 PrintWindow(hwnd, hdcMem, 2),标志位 PW_RENDERFULLCONTENT = 2 才能让窗口把自己"画"到内存 DC 上。核心就这几行:
python hdc = user32.GetWindowDC(hwnd) mem = gdi32.CreateCompatibleDC(hdc) bmp = gdi32.CreateCompatibleBitmap(hdc, w, h) gdi32.SelectObject(mem, bmp) user32.PrintWindow(hwnd, mem, 2) # 关键:flag=2 gdi32.GetDIBits(mem, bmp, 0, h, buf, byref(bmi), 0)
附带坑:窗口最小化或在后台时,PrintWindow 会抓到全黑帧。所以脚本里加了一条铁律——整帧平均亮度 < 15 就当"这一帧不可信",直接跳过,绝不基于黑图做判断。宁可今天不领,也不能乱点。
现象:OCR 结果永远是乱码,0礻见營销生成… 这种。我一度以为 Windows 中文 OCR 不行,准备换第三方接口。
根因:跟 OCR 一点关系都没有。PowerShell 往管道写 stdout 时用的是本地代码页(GBK),Python 侧按 UTF-8 解码,中文必然整片乱码。改之前我甚至已经在比价云 OCR 了。
修法:两头都钉死 UTF-8。
```powershell
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 $OutputEncoding = [System.Text.Encoding]::UTF8 ```
```python
try: out = out.decode("utf-8") except UnicodeDecodeError: out = out.decode("gbk", errors="replace") ```
修完之后实测,中文识别准得出乎意料——"积分包购买全面开放""做任务赢积分好礼"都能一字不差读出来。
这条教训最贵:报错信息会把人骗到完全错误的方向上。看到"乱码"先怀疑编码,别怀疑识别能力。
现象:脚本点位没错、点击也发出去了,但当天积分没到账。查日志发现根本没有签到请求,反而打开了一个网页。
根因:左侧栏那张卡片是一个轮播位。三屏内容共用同一位置、同一颗黑底白字按钮:
轮播屏 | 按钮文案 | 点了会怎样 |
|---|---|---|
每日签到 | 立即领取 | 正确,+100 |
做任务赢积分 | 立即前往 | 开浏览器个人中心 |
积分包购买 | 限时加购 | 弹付费购买面板 |
三颗按钮颜色、尺寸、坐标完全一致,只有文案不同。原来的做法是"找黑色实心块 → 点它",从根上就是错的:它在语义上是盲的。而且最坏情况不是少领 100 分,是误点"限时加购"。
修法:把"找按钮"降级为"读语义"。每 2 秒抓一次卡片 → 裁标题区 → 放大 3 倍 → OCR → 关键词分类:
claim:签到 / 打卡 / 领取 / 加油 / 每日danger:购买 / 加购 / 积分包 / 任务 / 前往 / 开通 / 升级 / 续费 / 兑换 / 盲盒unknown:其余只有 claim 才点,danger 和 unknown 一律不点,继续等下一屏。因为轮播一圈大约 10 秒,所以循环上限给到 420 秒,足够覆盖多轮。
另外加了一条前置保险:先查今天到底领没领。读客户端日志里的签到成功事件,已领就直接退出,一个像素都不点。
python if already_checked_in_today(): print("OK: 今日已领取") return 0
我给这一条起了个名字:自动化里最贵的不是"能不能点对",是"点错了会怎样"。凡是可能触发付费、提交、删除的入口,必须走语义白名单,不能走位置

现象:脚本正常跑,截图正常,点击也"成功"了,但界面毫无反应。
根因:后台进程直接调 SetForegroundWindow 会被系统拒绝,前台窗口其实还是企业微信。所有模拟点击都落在企业微信的对应坐标上——那次运气好,落在空白区,没造成任何操作。
修法:用 ALT 键"借"一次前台权限,并且校验结果:
python user32.keybd_event(0x12, 0, 0, 0) # ALT down user32.SetForegroundWindow(hwnd) user32.keybd_event(0x12, 0, 0x0002, 0) # ALT up if user32.GetForegroundWindow() != hwnd: # 兜底:最小化再还原,实测必成 user32.ShowWindow(hwnd, 6); time.sleep(1.0) user32.ShowWindow(hwnd, 9); time.sleep(1.5) user32.ShowWindow(hwnd, 3); time.sleep(1.0)
通用教训:所有"让窗口到前台"的调用都要读回状态验证,不能假设成功。SetForegroundWindow 返回值不能信,要拿 GetForegroundWindow() 跟目标句柄比。
现象:同样的相对坐标,有时点中有时点偏。
根因:双屏 DPI 缩放不一致。窗口停在副屏(125% 缩放)时,相对坐标换算出来的物理像素跟主屏(100%)差一截。
修法:脚本第一步就 SetProcessDpiAwareness,再把窗口先挪回主屏左上角、再最大化,然后在主屏坐标系里算点击位置。顺序不能反——先最大化再挪,最大化会发生在副屏。
现象:猫猫旅行脚本突然报"未定位到宠物卡片按钮",但页面明明是好的。
根因:客户端 5.8.3 把主按钮底色从纯黑改成了中灰 #8D8D8D(亮度约 141)。旧代码判据是 亮度 < 80,全部漏检。没有报错,只是找不到——这种失败最难查。
修法:定位不再依赖具体颜色值,改成"实心 + 低饱和 + 成块":
```python
if lum < 180 and (max(r,g,b) - min(r,g,b)) < 24: ... ```
黑、中灰、深蓝都能认出来。颜色只用来判状态(亮度 > 115 视为置灰不可点),不再用来做定位。
通用教训:UI 自动化里,颜色值是最脆的耦合。改版只改配色是产品经理最常干的事,而你的脚本会静默失效

现象:卡片一直显示"领取礼物",脚本每次点完就退出,猫再也派不出去。
根因:点"领取礼物"只是弹出信件弹窗"Buddy 满载而归啦~",真正加积分要再点弹窗里的"领取 7 积分"。而且弹窗右侧还有一颗黑色按钮"快来做同款吧",点了会打开别人的作品——又一处"看起来一样、语义完全不同"的陷阱。
修法:写一个 complete_claim_modal():定位弹窗里的青色实底按钮(判据用 G - R > 55,跟黑色按钮区分开),反色放大 4 倍 OCR 校验文案含"领取",再过一遍付费词黑名单,才点。领完递归回主流程重新判断状态。
现象:领完之后弹窗不消失,按钮变成灰色"已领取",底部 X 和 Esc 都关不掉。下一轮脚本把弹窗里的黑按钮误当成宠物按钮,OCR 读不出文案,直接放弃。
修法:领奖成功后不找关闭按钮,直接 Ctrl+R 刷新页面并等 7 秒,再递归判断。复位比重试更可靠——找关闭按钮是在跟产品设计赌运气,刷新是确定性的。

第 3 条是后来才想明白的:一开始我配了三条自动化(签到、旅行早班、旅行晚班),结果发现每条自动化都要起一次独立的 Agent 会话,消耗的是会话额度,不是积分本身。合并成一条、晚班直接删掉,成本立刻降到三分之一。
整套东西没有用任何商业 RPA,全是 Python + ctypes + Windows 自带 OCR + PowerShell,一个人边用边改,三周收敛。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。