首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >让 WorkBuddy 自己领积分:# 用 Python + OCR 驯服 Electron 桌面客户端:一次「每日签到」自动化实战

让 WorkBuddy 自己领积分:# 用 Python + OCR 驯服 Electron 桌面客户端:一次「每日签到」自动化实战

原创
作者头像
用户12683097
发布2026-08-11 13:09:43
发布2026-08-11 13:09:43
1330
举报

用 Python + OCR 驯服 Electron 桌面客户端:一次「每日签到」自动化实战

一、背景:为什么一个简单的"签到"这么难

很多桌面软件(尤其是基于 Electron / Chromium 的跨平台客户端)都内置了"每日签到领积分"这样的运营功能。点一下按钮,积分到账——动作本身很简单,但当你想把它自动化时,会发现它处处是坑:

  • 窗口是 GPU 渲染的,常规截图一片黑;
  • 系统开了 150% 缩放,坐标忽而对忽而错;
  • pyautogui / mouse_event 模拟点击,光标明明移到了按钮上,却毫无反应
  • 双显示器下窗口跨屏,截图函数直接卡死;
  • 配置成定时任务后,第二天发现它"偷偷"跑了不止一次。

本文记录我用 Python 把一个 Electron 客户端的"每日签到"做成稳定、幂等、可定时触发的自动化任务的完整过程。其中 90% 的工作量都不在"点按钮"本身,而在和 Windows 图形子系统"斗智斗勇"。


二、整体思路

目标很明确:让脚本在每天固定时间,无需人工干预地完成:

技术选型:

环节

方案

窗口定位

EnumWindows + 进程路径匹配

截图

PrintWindow 失败回退 mss

按钮识别

RapidOCR 多尺度文本检测

鼠标点击

PostMessage(WM_LBUTTONDOWN/UP) + 客户区坐标

健壮性

空闲检测、锁屏检测、幂等判断

定时触发

平台自动化任务(每天 02:00 单次)


三、第一关:截图——PrintWindow 为什么一片黑?

最直觉的截图方式是用 PrintWindow 把窗口内容"画"到一张内存位图上:

对普通 Win32 窗口这招很好用,但对 Electron / Chromium 渲染的窗口,GPU 合成层不走 GDI,于是你拿到的是一张纯黑(或纯白)位图(均值 arr.mean() < 3 即可判定)。

解决:回退到屏幕抓取(直接读显示器帧缓冲),mss 库就能用:

坑点mss.grab 传入的坐标必须是物理像素且落在某个显示器范围内,否则它会无限阻塞(不报错、不返回)。下一节讲怎么处理。


四、第二关:坐标体系与 DPI 陷阱

这是最容易让人"算错坐标"的地方。我的环境是系统缩放 150%(DPI=144)。

结论先行GetWindowRectmss.grab 返回的坐标,全程都是物理像素,和窗口在屏幕上的真实位置一致;而 SetCursorPos / PostMessage 客户端坐标也接受物理像素,不需要再乘 1.5

我曾一度"自作聪明"地加了一层缩放:

验证方法很简单:用 OCR 在截图上找窗口标题栏文字,得到它的真实像素位置,再和 GetWindowRect 对比——两者一致,证明根本不存在逻辑→物理的换算,那层 ×1.5 是画蛇添足。

多显示器裁剪(关键):我的窗口矩形是 (474,129)-(3600,1965),而主屏只有 2560×1440 宽。窗口横跨到右侧副屏之外,mss.grab 因为区域超出可见范围而永久卡死。必须先把抓取区域裁到所有显示器的并集内:

再加一层线程超时,防止任何意外阻塞拖垮整个任务:

实测加上这两步后,跨屏抓取从"卡死 2 分钟"变成"约 30ms 返回"。


五、第三关(核心突破):SetCursorPos 点了等于没点

这是整个过程最关键的发现,也是本文最想分享给读者的经验。

前面所有准备就绪后,我用最"标准"的方式点击头像:

结果:光标确实挪到了头像上方,但菜单从不弹出。我扫了 20 多个坐标点(头像左缘、中心、右缘、上 5px/下 5px……)全部无效。

原因:mouse_event / SendInput 这类 "合成输入" 走的是系统的"低级输入队列",而 Electron 的渲染进程对来自输入法/辅助功能的合成事件有过滤或不响应的情况(尤其在窗口未处于"前台输入焦点"或被 accessibility 层拦截时)。

解法:绕过输入队列,直接把鼠标消息投递到目标窗口过程

第一次尝试就弹出了头像菜单——这就是整条链路打通的瞬间。

划重点:PostMessage 用的是客户区坐标(相对窗口客户区左上角),而 SetCursorPos 用屏幕坐标。务必用 ScreenToClient 转换,否则坐标会整体偏移一个窗口边框/标题栏的尺寸。

实测有效坐标(供参考,实际以你自己的 OCR 为准)

操作

屏幕坐标

客户区坐标

左下角用户头像

(574, 1912)

(100, 1783)

菜单项「签到领积分>」

(886, 1272)

(412, 1143)

面板「立即领取」

(587, 1826)

(113, 1697)


六、按钮识别:让 OCR 只认"对的"按钮

截图有了,点在哪?用 RapidOCR 做文本检测,但要注意防误命中

  • 侧边栏常驻的"每日领取 Buddy 加油..."对话标题里也含"签到/领取",会干扰;
  • 聊天记录里用户发的图片标注「立即领取 (红框)」不是按钮。

所以识别时要限定搜索区域 + 精确关键词 + 优先精确匹配

经验:把"头像→菜单"的搜索区限制在小窗格内、用连续词「加油站」而非「签到」(后者会误中同名对话),命中率显著提升。


七、健壮性:别在错误的时间瞎点

定时任务最大的不确定性是——触发时电脑可能在锁屏、屏保,或你正用着鼠标

  1. 空闲检测:用户 2 分钟内动过键鼠就跳过(避免抢走操作):
  2. 锁屏/屏保判定:通过当前输入桌面名判断(Winlogon=密码锁,Screen-saver=带密码屏保,Default=正常):
  3. 幂等:点击前先 OCR 判断是否已「已领取」,是则直接结束;点击后再复检,避免重复领取。

八、落地:配置定时自动化任务

脚本稳定后,用平台自带的自动化任务能力挂上调度即可。核心配置要点:

  • 工作目录指向脚本所在目录(buddy-points);
  • 触发命令直接调用解释器跑脚本:
  • 执行频率:每天 02:00 单次(凌晨窗口,机器多半空闲、锁屏自愈逻辑也能兜住);
  • 模型:交给轻量模型执行,无需人工值守。

(小插曲:我一度在系统里看到了两个同名签到任务——旧的残留注册没清干净,导致同一脚本被调度了两次、白天还弹窗打扰。最终删掉陈旧任务、保留唯一的 02:00 任务才干净。教训:重建自动化任务后,务必确认旧的注册被彻底移除,否则会出现"重复触发 + 记忆分裂"。


九、踩坑清单(直接抄)

  1. Electron 窗口 PrintWindow 黑屏 → 用 mss 屏幕抓取。
  2. mss.grab 跨屏卡死 → 先把区域裁到显示器并集,再加线程超时。
  3. DPI 缩放坐标错乱GetWindowRect/mss/PostMessage 都是物理像素,别乱乘缩放。
  4. SetCursorPos+mouse_event 点了没反应 → 换 PostMessage(WM_LBUTTONDOWN/UP) + 客户区坐标
  5. OCR 误命中聊天/标题文字 → 限定搜索区域 + 精确关键词 + 短文本优先。
  6. 定时任务重复触发 → 删除陈旧任务注册;脚本内做幂等与空闲/锁屏保护。

十、结语

"每日签到"看似是个体力活,真正难的从来不是"点一下",而是在 Windows 图形子系统、DPI、多显示器、Electron 渲染架构、输入安全模型之间找到那条能稳定走通的路

本文最核心的一条经验是:对 Electron/Chromium 客户端,别迷信 pyautogui/mouse_eventPostMessage + 客户区坐标才是可靠之选。 配合 mss 截图与 OCR 定位,你就能把任意桌面客户端的"点点点"流程,改造成一个安静、幂等、凌晨自己跑的自动化任务。

祝你的积分,天天到账。🪙

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

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

目录
  • 用 Python + OCR 驯服 Electron 桌面客户端:一次「每日签到」自动化实战
    • 二、整体思路
    • 三、第一关:截图——PrintWindow 为什么一片黑?
    • 四、第二关:坐标体系与 DPI 陷阱
    • 五、第三关(核心突破):SetCursorPos 点了等于没点
      • 实测有效坐标(供参考,实际以你自己的 OCR 为准)
    • 六、按钮识别:让 OCR 只认"对的"按钮
    • 七、健壮性:别在错误的时间瞎点
    • 八、落地:配置定时自动化任务
    • 九、踩坑清单(直接抄)
    • 十、结语
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档