公司动态
鼠标键盘脚本录下来就能重放,KeymouseGo 凭什么跨三个系统都不跑偏?
鼠标键盘脚本录下来就能重放KeymouseGo 凭什么跨三个系统都不跑偏【免费下载链接】KeymouseGo类似按键精灵的鼠标键盘录制和自动化操作 模拟点击和键入 | automate mouse clicks and keyboard input项目地址: https://gitcode.com/gh_mirrors/ke/KeymouseGo做过运维的人大多经历过这种崩溃同样的表单、同样的确定按钮一天要手工点几百次。直到我拿到 KeymouseGo 这个开源项目——一个类似按键精灵的鼠标键盘录制与自动化执行工具把操作完整录一遍存成脚本之后一键重放而且 Windows、Linux、macOS 三个平台通吃。作为一个经常和重复劳动打交道的人我更好奇的是另一件事它的代码是怎么组织的才能让录一遍、重放无限次这件事在三套差异极大的操作系统上都不出错先把问题摆清楚把手动变成自动难在哪我们首先要弄清楚录播这件事的技术难点到底在什么地方。表面看就是记录输入、模拟输入八个字实际上拆开有三层麻烦平台差异。Windows 走 Win32 消息钩子macOS 走 QuartzLinux 走 X11/Wayland三套底层 API 几乎没有共同点同一段录制逻辑没法直接复用。坐标漂移。脚本里存的是点(500, 400)换一台屏幕分辨率不同、DPI 缩放不同的电脑这个点就点歪了。时序精度。操作之间的等待、按键的按下和抬起都必须按毫秒级还原快了慢了都可能导致后续动作错位。在 KeymouseGo 之前业界的主流方案各有各的短板方案原理明显的坑AutoHotkey自研脚本语言手写指令要学一门新语言且基本只服务 WindowsSelenium浏览器 DOM 自动化只摸得到网页管不了桌面应用商用按键精灵类录制回放门槛低闭源、捆绑软件、脚本格式私有KeymouseGo录制→脚本→重放脚本可手改精度上限受系统事件粒度限制KeymouseGo 的解题思路很朴素用户不写一行代码把操作做一遍剩下的交给程序。但这个朴素背后是一套从事件抽象、坐标换算到执行器设计的完整工程方案。架构长什么样四层分离把平台差异关进笼子打开项目源码目录结构本身就说明了一切Event/、Recorder/、Util/、Plugin/四个包恰好对应四个职责边界清晰的层次。┌ 表现层PySide6 界面 / 命令行入口KeymouseGo.py ├ 脚本引擎层Util/Parser.py脚本→事件链表 │ Util/RunScriptClass.py执行线程与暂停/终止 ├ 事件抽象层Event/Event.py基类 │ Event/UniversalEvents.py、WindowsEvents.py ├ 平台适配层Recorder/WindowsRecorder.pyWin32 钩子 │ Recorder/UniversalRecorder.pypynput └ 插件层Plugin/Manager.py plugins/ 目录json5 清单图1主界面把录制、启动、热键、脚本列表这些高频操作收敛在一个面板里坐标显示是 v5.2 才加的小功能这里最值得琢磨的是平台适配层的设计取舍。Windows 版录制器用pyWinhook挂系统级钩子能拿到MessageName、Wheel、Extended这些底层细节所以 Windows 上支持录制鼠标轨迹、多显示器、侧键这些高级能力而 Linux/macOS 版退而求其次基于 pynput 做事件监听功能弱一些但换来的是同一套执行代码直接跑通。这不是偷懒而是刻意的 trade-off把高级能力集中在单一平台能用钩子的地方把兼容性作为跨平台实现的最高优先级。顺着这个思路往下看事件抽象层把所有平台的差异都收敛成同一种数据结构脚本引擎层根本不关心事件是从哪个系统录进来的——这正是整个项目最核心的设计。三个最值得拆开看的实现细节细节一事件抽象与百分比坐标一招解决跨分辨率跑偏事件基类只干两件事定义统一的初始化入口和提供可被打断的延时。真正有含金量的是坐标处理——录制端存的是相对坐标像素值除以屏幕宽高回放端再乘回去。class Event(metaclassABCMeta): def __init__(self, content): # 所有子类共用同一个初始化入口字典里取四个固定字段 for key in [delay, event_type, action_type, action]: setattr(self, key, content[key]) def sleep(self, thd): # 延时交给执行线程处理而不是自己傻等后面会讲为什么 thd.sleep(self.delay) abstractmethod def execute(self, thdNone): pass class UniversalEvent(Event): def execute(self, thdNone): self.sleep(thd) # 先等 delay 毫秒再执行动作 if self.event_type EM: # EM 鼠标事件 x, y self.action if self.action [-1, -1]: pass # [-1,-1] 是约定鼠标保持原位直接操作 else: # 兼容三种写法绝对像素 int、小数、0.26% 字符串 if not isinstance(x, int): x int(x * SW) # 百分比 → 屏幕像素动态适配分辨率 y int(y * SH) pyautogui.moveTo(x, y) if self.action_type mouse left down: pyautogui.mouseDown(buttonleft) elif self.action_type mouse left up: pyautogui.mouseUp(buttonleft)这段代码解决的是自动化工具最容易翻车的问题。如果脚本里存死 (500, 400)换台 2K 屏、或者 Windows 缩放从 100% 调到 125%点击位置立刻漂移。KeymouseGo 在录制端就把坐标除以屏幕宽高存成 0~1 的百分比回放端乘以当前屏幕尺寸还原分辨率怎么变都不影响落点。顺带用[-1, -1]这个哨兵值解决了一个更刁钻的场景某些对话框在操作过程中才弹出录制时根本不知道它在哪那就干脆在鼠标当前所在位置点。图2Windows 的 DPI 缩放正是坐标漂移的头号来源百分比坐标让脚本对缩放免疫细节二脚本执行器把 json5 变成一张单向链表KeymouseGo 的脚本是 json5 格式支持if/else、goto、sequence、subroutine这些控制结构。难点在于怎么把一个嵌套的 JSON 变成运行时的高效执行结构作者的选择是——不递归解释而是预编译成一张单向链表。class JsonObject: def __init__(self, content): self.content content self.next_object None # 顺序执行的下一个节点 self.next_object_if_false None # if 判断为假时走的分支 def link_objects(objects, target_object, label_maps, pending_dict): objects.reverse() # 倒着遍历next 指针一次接好 current_object None for content in objects: current_object JsonObject(content) if content.get(label): label_maps[content[label]] current_object # 登记标签供 goto 用 t content.get(type) if t in [event, subroutine, custom]: current_object.next_object target_object # 普通节点顺序串联 elif t sequence: current_object.content[events] \ ScriptParser.link_objects(content[events], None, ...) elif t if: current_object.next_object \ ScriptParser.link_objects(content[do], target_object, ...) current_object.next_object_if_false \ ScriptParser.link_objects(content[else], target_object, ...) elif t goto: # 目标标签可能还没遍历到先挂进 pending # 等整张链表建完再做第二遍补链否则前向跳转会失败 if label_maps.get(content[tolabel]): current_object.next_object label_maps[content[tolabel]] else: pending_dict[current_object] content[tolabel] target_object current_object return current_object这段代码的妙处在于倒序遍历。正着建链表时每个节点都不知道自己的后继是谁倒过来遍历天然就能把next指针接上。goto的前向引用用了一个两遍法第一遍先记录这个节点想跳去哪个还没见过的标签等所有节点建完、label_maps补全后再统一补链。这样一来运行时执行循环就退化成最简单的while current_object is not None一路往下走毫秒级的执行开销几乎等于零解释成本。细节三执行线程的可打断延时暂停和终止才没延迟这是我认为全项目最漂亮的一段代码。回放时每个事件之间要等delay毫秒最简单粗暴的做法是time.sleep(delay)——但那样的话你按 F9 想终止脚本就得眼睁睁看着当前这次延时睡完才响应体验极差。KeymouseGo 用 Qt 的条件变量实现了可打断的睡眠mutex QMutex() cond QWaitCondition() class RunScriptMeta: def pause(self): # 暂停拿锁后进入等待直到有人 wakeAll 才继续 mutex.lock() cond.wait(mutex) mutex.unlock() def sleep(self, msecs): # 带超时的等待既能睡满 delay又能被外部随时叫醒 mutex.lock() cond.wait(mutex, QDeadlineTimer(int(msecs))) mutex.unlock() def resume(self): # 终止/继续唤醒所有等待中的执行线程 mutex.lock() cond.wakeAll() mutex.unlock()执行线程循环里每一轮都调用wait_if_pause()而每次sleep走的也是同一个条件变量。于是暂停继续终止三个操作都变成了一次wakeAll再也不用等延时结束。这个设计把事件时序精确性毫秒级延时和用户控制的即时响应随时打断这对天然矛盾用约 20 行代码就调和了。如果你要基于这个项目做二次开发强烈建议先读懂这一段。工程实践那些藏在版本历史里的坑翻 Changelog 能看到不少真实踩坑记录比任何教程都实在。跨平台差点被放弃分层救回来了。v3.0 时作者因为 macOS 兼容性太差一度改回纯 Win32 API、只支持 Windows。直到 v5.1 才通过Windows 用钩子、其他平台用 pynput的双轨方案重新支持 Linux/macOS。这个反复说明一件事跨平台功能一定要从第一天就做抽象层否则后期返工成本是重建一个项目的量级。脚本编码是个隐形炸弹。解析器先按 utf8 读失败再回退 gbk两遍尝试才敢报错——就是因为真实用户会用各种 Windows 老编辑器存脚本编码错一次整个脚本就白录了。鼠标轨迹录制是精度换体积。录制器里有个mouse_interval_ms阈值默认不录鼠标移动轨迹调小可以连轨迹一起录但脚本体积和回放耗时都会成倍上涨。v5.1 还专门修了连点操作概率崩溃的问题——高频事件下线程安全和资源释放是重灾区。权限问题避不开。Linux 下部分环境必须 root 运行才能录到完整鼠标事件macOS 则要求把程序和终端都加进辅助功能白名单。这些在 README 里都写得明明白白属于别人替你踩过的清单。它能用来干什么以及往哪走抛开抽象架构落到具体场景KeymouseGo 最常见的三种用法是重复性桌面操作比如给多个账号批量签到、填固定格式的表单。输入是你手工做一遍流程是录制 → 设置执行次数 → F6 启动执行次数填 0 就是无限循环产出是全天候的自动操作。回归冒烟测试发布新版软件前把核心流程录成脚本用命令行模式./KeymouseGo script.txt -rt 3连跑三遍验证基础功能没坏F9 随时终止脚本天然可提交进版本库。数据录入流水线这类场景单靠录制不够但 json5 脚本可以手写循环和条件配合 EX 输入事件往表单里填变量再挂插件做判断脚本节点里的call字段可以调用插件注册的函数就能拼出一个轻量级 RPA。往后看插件系统的 manifest.json5 机制plugins/目录下每个插件一个清单、声明入口类和注册函数已经为生态留好了口子图像识别、OCR 这类插件只是时间问题AI 生成脚本、任务分发到多台机器执行也都是顺着这个架构自然生长出来的方向。最后说两句实在的KeymouseGo 最值得借鉴的地方是把平台差异和业务逻辑彻底切开了平台代码全部收敛在Recorder/和事件子类里而脚本引擎、坐标换算、插件机制对平台一无所知。录播模型让零基础用户 30 秒上手json5 脚本又给进阶用户留了手动编辑的后路这个坡度设计得很舒服。如果你只是被重复劳动折磨去 https://gitcode.com/gh_mirrors/ke/KeymouseGo 把仓库 clone 下来装好依赖直接跑KeymouseGo.py就能用如果你是开发者建议从录制一个十步左右的小脚本开始然后打开scripts/下生成的 json5 文件对照语法说明改一遍——当你把坐标改成百分比、把延时调成毫秒时就基本吃透了它的设计哲学。【免费下载链接】KeymouseGo类似按键精灵的鼠标键盘录制和自动化操作 模拟点击和键入 | automate mouse clicks and keyboard input项目地址: https://gitcode.com/gh_mirrors/ke/KeymouseGo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考