当前位置: 首页 > news >正文

鼠标键盘脚本录下来就能重放,KeymouseGo 凭什么跨三个系统都不跑偏?

鼠标键盘脚本录下来就能重放,KeymouseGo 凭什么跨三个系统都不跑偏?

【免费下载链接】KeymouseGo类似按键精灵的鼠标键盘录制和自动化操作 模拟点击和键入 | automate mouse clicks and keyboard input项目地址: https://gitcode.com/gh_mirrors/ke/KeymouseGo

做过运维的人大多经历过这种崩溃:同样的表单、同样的"确定"按钮,一天要手工点几百次。直到我拿到 KeymouseGo 这个开源项目——一个类似按键精灵的鼠标键盘录制与自动化执行工具,把操作完整录一遍存成脚本,之后一键重放,而且 Windows、Linux、macOS 三个平台通吃。作为一个经常和重复劳动打交道的人,我更好奇的是另一件事:它的代码是怎么组织的,才能让"录一遍、重放无限次"这件事在三套差异极大的操作系统上都不出错?

先把问题摆清楚:把"手动"变成"自动",难在哪

我们首先要弄清楚:录播这件事的技术难点到底在什么地方。表面看就是"记录输入、模拟输入"八个字,实际上拆开有三层麻烦:

  • 平台差异。Windows 走 Win32 消息钩子,macOS 走 Quartz,Linux 走 X11/Wayland,三套底层 API 几乎没有共同点,同一段录制逻辑没法直接复用。
  • 坐标漂移。脚本里存的是"点(500, 400)",换一台屏幕分辨率不同、DPI 缩放不同的电脑,这个点就点歪了。
  • 时序精度。操作之间的等待、按键的按下和抬起,都必须按毫秒级还原,快了慢了都可能导致后续动作错位。

在 KeymouseGo 之前,业界的主流方案各有各的短板:

方案原理明显的坑
AutoHotkey自研脚本语言,手写指令要学一门新语言,且基本只服务 Windows
Selenium浏览器 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.py(Win32 钩子) │ Recorder/UniversalRecorder.py(pynput) └ 插件层:Plugin/Manager.py + plugins/ 目录(json5 清单)

图1:主界面把"录制、启动、热键、脚本列表"这些高频操作收敛在一个面板里,坐标显示是 v5.2 才加的小功能

这里最值得琢磨的是平台适配层的设计取舍。Windows 版录制器用pyWinhook挂系统级钩子,能拿到MessageNameWheelExtended这些底层细节,所以 Windows 上支持录制鼠标轨迹、多显示器、侧键这些高级能力;而 Linux/macOS 版退而求其次,基于 pynput 做事件监听,功能弱一些,但换来的是同一套执行代码直接跑通。这不是偷懒,而是刻意的 trade-off:把高级能力集中在单一平台(能用钩子的地方),把兼容性作为跨平台实现的最高优先级。

顺着这个思路往下看,事件抽象层把所有平台的差异都收敛成同一种数据结构,脚本引擎层根本不关心事件是从哪个系统录进来的——这正是整个项目最核心的设计。

三个最值得拆开看的实现细节

细节一:事件抽象与百分比坐标,一招解决"跨分辨率跑偏"

事件基类只干两件事:定义统一的初始化入口,和提供可被打断的延时。真正有含金量的是坐标处理——录制端存的是"相对坐标"(像素值除以屏幕宽高),回放端再乘回去。

class Event(metaclass=ABCMeta): 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, thd=None): pass class UniversalEvent(Event): def execute(self, thd=None): 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(button='left') elif self.action_type == 'mouse left up': pyautogui.mouseUp(button='left')

这段代码解决的是自动化工具最容易翻车的问题。如果脚本里存死 (500, 400),换台 2K 屏、或者 Windows 缩放从 100% 调到 125%,点击位置立刻漂移。KeymouseGo 在录制端就把坐标除以屏幕宽高存成 0~1 的百分比,回放端乘以当前屏幕尺寸还原,分辨率怎么变都不影响落点。顺带用[-1, -1]这个哨兵值解决了一个更刁钻的场景:某些对话框在操作过程中才弹出,录制时根本不知道它在哪,那就干脆"在鼠标当前所在位置点"。

图2:Windows 的 DPI 缩放正是坐标漂移的头号来源,百分比坐标让脚本对缩放免疫

细节二:脚本执行器把 json5 变成一张"单向链表"

KeymouseGo 的脚本是 json5 格式,支持if/elsegotosequencesubroutine这些控制结构。难点在于:怎么把一个嵌套的 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),仅供参考

http://www.cnnetsun.cn/news/4019674.html

相关文章:

  • 微信聊天记录怎么永久保存?一份实测把HTML、Word、CSV三种导出讲透
  • RISC-V后端开发入门:基于becoming-a-compiler-engineer项目的实践指南
  • 零基础学习网站建设:从入门到精通的实战指南,带你轻松搭建个人网站
  • 分类信息网站建设方案:从零开始打造高转化本地生活服务平台的实战指南
  • 建筑物防雷设计的计算方法和设计要求
  • 兰州商城网站建设怎么避坑?深度解析本地电商转型痛点与实战指南
  • 无锡高端网站建设:不只是代码堆砌,更是品牌价值的数字化升维
  • 如何用不到1MB的免费防休眠工具NoSleep,一键让Windows告别中途“打瞌睡“?
  • 眉山网站建设公司如何选择?揭秘专业团队背后的服务逻辑与避坑指南
  • 手机版网站建设方案,移动端流量变现核心战略,揭秘低成本高转化实战指南
  • 深耕用户心智与商业转化,揭秘2024年深圳精品网站建设的核心逻辑与实战心得
  • 2024年宁波网站建设公司排名大揭秘:如何避开隐形坑位选出真正懂你的合作伙伴
  • 做网站别只顾着好看:揭秘宁波营销型网站建设如何让流量变销量的真相
  • 湖南营销网站建设怎么做?揭秘本地企业如何通过数字化突围实现业绩翻倍
  • 从零开始掌握php网站建设教程:适合新手的小白进阶指南与实战避坑秘籍
  • 当游戏修改器也需要被修改:Wand-Enhancer 免费解锁高级功能的完整指南
  • 1篇看懂RevokeMsgPatcher:微信QQ防撤回工具免费搞定,顺带解锁多开玩法
  • Oracle 19c数据库从零安装到基础运维:Linux环境实战指南
  • 揭秘holehe的核心依赖:httpx与BeautifulSoup如何打造强大的邮箱检测工具
  • holehe配置迁移工具:从旧版到新版的设置自动转换
  • 揭秘金融行业网站建设背后的信任构建与安全防线
  • 天津滨海新区网站建设多少钱,专业公司如何帮中小企业打造高转化官网
  • 淘宝优惠券网站建设:从0到1打造高转化返利平台的实战指南与深度思考
  • 陕西省城乡建设厅网站:指尖上的便民神器,带你一键解锁陕西城乡发展新脉络
  • 建设一个网站需要哪些方面的开支:给准备建站老板们的真心话,别再被坑了
  • 网站建设合同 费用明细深度解析:如何避免隐形收费陷阱并签订一份负责任的建站协议
  • 深度解析黑龙江省建设银行网站如何助力百姓财富增值与金融服务升级
  • 网站建设需要编程吗小白建站到底要不要写代码
  • 揭秘上海市建设工程交易管理中心网站:从新手小白到资深操盘手的进阶指南与深度解析
  • 深圳优秀网站建设定制:从设计到开发全流程深度解析