FEATURED · 精选文章

基于Python与pynput的鼠标键盘录制回放工具实战

发布时间 / 2026/9/21 1:48:39
来源 / 创域科博编辑部
栏目 / 资讯中心
基于Python与pynput的鼠标键盘录制回放工具实战 简介面向自动化测试与脚本开发场景这款基于Python3与pynput的键盘鼠标操作录制执行工具可帮助测试人员与开发人员自动完成重复性操作支持脚本录制、宏命令生成、图形界面操作以及跨平台运行。压缩包内共包含10个文件整体体积仅23KB其中4个py源码文件构成核心功能模块2个md文档提供使用说明另有txt说明文件、docx附赠文档、license与gitignore工程配置目录结构清晰便于二次开发与功能扩展。目前已有151人学习浏览。内含完整工程源码与安装脚本录制执行模块采用类封装设计配合附赠的Word文档和文本说明可快速掌握安装部署、录制回放、代码生成的完整流程对于希望简化测试流程、提升重复操作效率的中高级测试工程师与脚本开发者是一款实用价值较高的轻量级工具。 做桌面软件测试的人大概都经历过这种时刻一个表单、一个向导、一个弹窗要在同一套操作上反复来回跑。手动点一遍两遍还能忍点二十遍的时候整个人基本就是想把电脑扔出去的状态。我半个月前就栽在这么一次重复回归上连续录入几十条测试数据结果手滑点错了一个下拉选项之前的动作全部作废只能重来。那次之后我下了决心搞一个基于 Python3 pynput 的键盘鼠标操作录制执行工具把手动重复劳动这件事彻底解决掉。这个工具能做什么一句话就能说清把你屏幕上真实发生的鼠标键盘操作完整录下来按原始节奏回放并且可以在录制结束后直接生成一份可阅读、可修改、可提交到代码仓库的 Python 脚本。它的典型用途是自动化测试脚本录制、重复操作模拟、宏命令生成以及日常工作效率提升。下面我会从事件监听原理、回放引擎设计、代码生成模块到跨平台兼容的细节逐一拆开讲内容偏实战适合正在做自动化测试、或者每天被大量重复界面操作折磨的开发者参考。1. 这个工具解决的不是录屏而是把操作变成资产1.1 录制回放和录屏视频的本质区别很多人第一次听到鼠标键盘操作录制第一反应是这不就是录屏吗。还真不是。录屏得到的是视频本质上是像素的快照而这个工具录制的是事件流——鼠标在哪个坐标按下了哪个键、键盘在什么时间点输入了什么内容、两次操作之间隔了多久。回放做的事情也不一样不是播放视频而是通过 pynput 的 Controller 把这些事件重新发送到系统里再次驱动界面产生真实的点击和输入。这个区别决定了工具的可用性。录屏只能给人看事件流却可以直接交给程序执行。我最初的目标就是做一个录一遍以后自动跑的工具而不是一个视频记录器所以整个设计从一开始就围绕着事件数据的采集、存储、回放和代码化来展开。1.2 为什么能生成代码比能回放更有价值市面上的录制回放工具并不少但很多都停留在录制后只能在它自己的界面里重新播放这个层面。一旦操作步骤想微调或者想接入自己的测试框架就非常痛苦。这个工具在录制之外加了代码生成能力相当于把一段操作变成了一份可以版本管理、可以 Code Review、可以参数化的测试资产。举个例子。录了一段打开软件、输入账号密码、点击登录的操作如果只有回放功能这段操作就是封闭的但如果能生成一段 Python 脚本就可以把账号密码提取成变量、把登录按钮的坐标提取成配置项再把这套动作包成一个函数丢进 pytest 里跑。录制只是用来快速获取操作基线真正的价值在于后续的加工而加工的前提是操作本身有代码形态。1.3 谁最需要它三类典型使用场景从我的实际使用情况看这三类人最需要这类工具自动化测试工程师用录制快速生成回归用例的初稿再手工修改完善比从零写 pynput 脚本省一半时间。需要大量重复数据录入的操作类岗位例如每天定时把一批测试数据填入内部系统操作路径完全一样。运维和开发人员经常要做界面巡检、冒烟验证、演示型操作这些都可以一键回放。这里我加一句提醒录制回放适合操作路径相对固定的场景如果页面元素的坐标经常变核心精力应该放在元素定位上而不是依赖坐标回放。这个工具的定位是快速基线生成和流程验证不是银弹。2. 录制的根基pynput 事件监听与跨平台的真相2.1 Listener 线程模型与回调写作纪律pynput 的监听机制本质上是在操作系统的输入事件链路上挂一个钩子。mouse.Listener 和 keyboard.Listener 分别工作在独立线程中一旦有全局输入事件发生就会触发你注册的回调函数。这里需要注意回调线程是共享的如果在回调里写了耗时操作比如把事件写入数据库、做图像处理后续事件就会被延迟处理录出来的时间戳就会失真。我的做法是回调函数里只做一件事把原始事件带时间戳塞进一个线程安全的队列再由另一个消费线程去处理落地。这样监听线程永远保持轻快事件不会卡顿。以下是监听器的骨架代码import queue import time from pynput import mouse event_queue queue.Queue() def on_click(x, y, button, pressed): event_queue.put({ type: click, x: x, y: y, button: button.name, pressed: pressed, ts: time.time() }) def start_listener(): listener mouse.Listener(on_clickon_click) listener.start() return listener注意回调里的时间戳必须用 time.time() 取绝对时间后续回放时再统一换算成相对起始时间的偏移。不要在回调里直接算相对时间因为队列一旦积压所有偏移就全错了。2.2 事件序列的数据抽象什么该记、什么不该记设计事件模型是整个工具最重要的决策之一。一开始我以为把鼠标移动的每一帧都录下来就行实际跑了一次才发现鼠标移动事件极其高频一秒钟能产生几十甚至上百条全部落盘会让数据量爆炸回放时也会非常卡顿。所以我采用了语义化录制策略鼠标移动不逐帧记录只在按下按键、释放按键、滚动滚轮时记录当时的坐标鼠标轨迹需要模拟的话单独开启采样并且做降频抽稀比如每 50 毫秒记录一个点。键盘事件则必须把按下和抬起分开记录同时保留按键的类型信息先是 Key.ctrl 这类虚拟键再是 c 这样的字符键否则组合键无法还原。清理之后的典型事件序列长这样[ {type: press, key: Key.ctrl, ts: 0.000}, {type: press, key: c, ts: 0.120}, {type: release, key: c, ts: 0.210}, {type: release, key: Key.ctrl, ts: 0.280}, {type: click, button: left, pressed: true, x: 1024, y: 386, ts: 1.452} ]每个事件的 ts 都统一换算成相对开始录制的毫秒数回放时只需要按时间差 sleep 就能还原真实节奏。2.3 跨平台兼容的边界权限和桌面环境标题里写跨平台兼容容易让人误解成代码写出来三个平台都能直接跑。实际情况是pynput 的上层 API 确实是跨平台的但底层机制完全不同——Windows 走的是系统钩子macOS 走的是 Quartz 事件注入Linux 依赖 X11/XRecord在 Wayland 会话下 XRecord 基本用不了。所以我在工具里写兼容性说明时一定会强调录制端依赖系统权限和桌面会话环境。macOS 需要给运行终端授予辅助功能和输入监控权限否则鼠标键盘事件捕获不到Linux 建议在 X11 会话下使用Wayland 下需要换方案或者走 XWayland 兼容层。这个差异不是 pynput 本身能解决的属于各平台安全模型的硬限制。3. 回放引擎时序控制比事件分发难十倍3.1 时间戳是回放节奏的灵魂录制时记录的时间戳回放时最多只能还原基本的操作节奏。为什么说基本因为实际操作里经常包含长停顿——操作者在思考、在等页面加载、在切窗口。如果回放完全按原始延时走一次回放可能要等四五分钟效率完全不能接受。回放引擎我实现了三种时序策略原始延时严格按记录的时间差走固定延时每个事件之间固定间隔适合快速演示倍速回放把原始延时乘以一个速度系数0.5 是慢放2 是快进。同时对超过 10 秒的超长停顿做封顶处理避免操作者中途去喝了杯水回放也陪着傻等。这些策略都可以在 GUI 界面里直接调整。3.2 回放循环的完整逻辑与终止机制回放的核心实现不复杂难点在可靠性回放过程中用户想紧急停止怎么办回放开始前要不要把目标窗口激活坐标在另一个分辨率下失效了怎么办我的回放循环大概长这样import time from pynput.mouse import Controller as MouseController from pynput.keyboard import Controller as KeyboardController def play_events(events, speed1.0, stop_eventNone): mouse MouseController() keyboard KeyboardController() last_ts None for ev in events: if stop_event is not None and stop_event.is_set(): print(已手动终止回放) break if last_ts is not None: delay (ev[ts] - last_ts) / speed delay min(delay, 10.0) time.sleep(delay) dispatch_event(mouse, keyboard, ev) last_ts ev[ts]这段代码里最不能省的是 stop_event 检查。我在回放线程里周期性检查这个线程安全的事件对象GUI 上的停止回放按钮就是把它 set 一下。真实项目里还加了按 F8 键全局终止的逻辑本质是回放前再挂一个临时的 keyboard.Listener。3.3 一次完整录放过程演示我拿 Windows 自带的计算器做过完整演示。先启动录制接着打开计算器依次按下 1、、2、然后停止录制。这个过程中工具记录了 8 个按键事件、4 个鼠标点击事件以及它们之间的时间间隔。回放时程序先等待 3 秒让用户切换到目标位置然后按记录顺序自动执行最终计算器窗口上稳定显示结果 3。这种先录制再回放的流程看着简单真正压测起来会发现很多边界问题回放时窗口被其他界面挡住了、点击坐标被分辨率缩放覆盖了、系统弹出了权限确认框打断了后续事件。这些问题没有任何一个能靠更精确的 sleep解决只能靠回放前检查、回放中容错、回放后校验层层加上去。4. 从事件序列到代码资产生成模块与GUI闭环4.1 模板化代码生成事件JSON如何变成可读脚本代码生成是让我自己最满意的部分。思路很简单把事件序列 JSON 喂给模板逐条翻译成 pynput 的 Controller 调用最后拼接成一个完整 Python 文件。生成的代码有三个要求——可读、可改、无额外依赖。所以生成的不是一坨堆在一起的代码而是有函数划分、有时间注释的结构化脚本from pynput.mouse import Button, Controller as MouseController from pynput.keyboard import Key, Controller as KeyboardController import time mouse MouseController() keyboard KeyboardController() def prepare(): # 录制开始前的缓冲等待人工切换到目标窗口 time.sleep(3) def main(): prepare() # 第一步按下 Ctrl keyboard.press(Key.ctrl) # 第二步按下 c keyboard.press(c) time.sleep(0.09) keyboard.release(c) keyboard.release(Key.ctrl) # 第三步点击坐标 (1024, 386) mouse.position (1024, 386) time.sleep(0.18) mouse.press(Button.left) mouse.release(Button.left) if __name__ __main__: main()生成代码用的是字符串模板加循环没有上 jinja2。原因是不想让工具自身引入太多依赖而且事件类型总共就六种if-elif 完全够用。模板里唯一需要小心的是按键名称映射pynput 的 Key 枚举和字符串之间的对应关系在不同版本有小差异生成器里需要做一层白名单映射。4.2 生成脚本挂进 pytest 的最小接法生成脚本要进自动化测试流程最朴素的做法是把它包成一个普通函数然后被 pytest 用例调用。我通常把生成的 main() 再套一层操作分组比如命名成 submit_form()、drag_file()在 pytest 里这样写import time from generated_actions import submit_form def test_form_submit_twice(): submit_form() time.sleep(1) assert check_result_appears() is True当然这种录制生成的脚本更适合做冒烟测试和回归基线替代不了需要复杂断言的正式测试用例。断言部分还是得自己写比如检查某个窗口是否打开、某个页面元素是否出现。录制帮你省掉的是把几十步点击写成代码的时间。4.3 Tkinter GUI 与 PyInstaller 打包的注意点GUI 我选的是 Tkinter理由非常朴素Python3 自带、跨平台、做这种工具界面完全够用。界面一共分四个区域控制区录制/停止/回放/生成代码按钮、参数区回放速度、延时策略、状态区当前录制还是回放、事件总数、日志区实时输出事件和错误。写 GUI 时最值得注意的坑是线程。录制监听线程、回放线程、代码生成线程都不能直接操作 Tk 控件否则界面会假死甚至崩溃。我的做法是让后台线程把消息塞进 queueGUI 线程用 root.after() 定期消费队列并刷新界面。打包时用 PyInstaller需要留意 pynput 在打包后可能因为权限或签名问题无法注入系统钩子Windows 上偶尔要管理员身份运行macOS 上打包出来的应用第一次启动还是要去系统设置里授权辅助功能。5. 实测踩坑坐标漂移、组合键乱序与权限墙5.1 坐标漂移DPI缩放和分辨率变化怎么破坐标漂移是我踩过最深的坑。在 1920x1080、缩放 100% 的屏幕上录得好好的脚本换到一台 2K 屏、缩放 150% 的机器上回放所有点击位置全偏了。原因很简单pynput 拿到的鼠标坐标是以物理像素为单位的而操作系统默认会按 DPI 缩放把逻辑坐标映射成物理坐标不同显示器的缩放比例不一样坐标就全乱了。后来我在回放前增加了坐标校正参数用户可以手动输入录制时的分辨率和缩放比例回放时按当前系统的 DPI 做一次线性换算。对付跨机器回放这个办法不能说百分百精准但能覆盖大多数日常场景。真正要求高的场景我建议不要依赖绝对坐标改成配合图像识别或者窗口内相对定位。5.2 组合键录制顺序修饰键的按下与抬起组合键是很隐蔽的雷区。录制 CtrlC 这类快捷键时键盘事件的真实顺序是先按下 Ctrl再按下 C然后抬起 C最后抬起 Ctrl。如果生成代码时只记录了按下了 C回放就变成了单独按 C完全不是复制。解决方式就是在监听阶段老老实实把 press 和 release 分开记录并且把修饰键和普通键的按下、抬起都保留。生成代码时按时间顺序组装就能得到正确的组合键调用。还有一个边角情况有些键盘在按住修饰键时会连续发送 auto-repeat 事件监听器需要根据 pressed 状态和 key 是否变化做去重不然回放时会出现一堆多余的按键抖动。5.3 权限墙macOS辅助功能与Linux Wayland跨平台最折磨人的是权限和环境兼容问题。macOS 上pynput 的监听器必须拿到辅助功能和输入监控权限而且这两个权限是分给具体可执行文件的——你在终端里授权了换一个 IDE 运行就可能又要重新授权。排查这个问题时最典型的症状是脚本运行时既不报错、也不产生任何事件整个监听静默失败。我第一次遇到时排查了大半天最后才发现只是权限没开。Linux 那边要重点确认桌面会话类型。X11 会话下一切正常Wayland 会话下因为安全协议限制全局事件钩子基本拿不到系统输入。如果目标机器只有 Wayland我建议要么切回 X11/XOrg 会话要么改用 xdotool 这类工具配合或者干脆换一套基于可访问性接口的方案。5.4 两个隐蔽的性能问题GUI阻塞和回调耗时最后说两个很隐蔽、但一踩就中招的问题。第一个是 GUI 阻塞回放是耗时操作如果直接在主线程里调用 play_events()界面会立刻无响应用户想点停止按钮都没机会。回放必须放到后台线程停止机制用 Event 对象而不是全局标志位。第二个是监听回调耗时。前面说过回调里不能干重活但还要补充的是哪怕只打印一行日志到控制台在高频鼠标移动面前也可能成为性能瓶颈。所以我给工具设置了调试日志开关默认只打印事件摘要录制时按需打印完整事件。这两个问题不解决工具在低频操作下看起来一切正常一遇到高频操作就立刻露馅。最后再分享一点我自己的体会。这个工具从最初的几百行脚本一路迭代到带 GUI、带代码生成、带倍速回放的完整结构最深的感悟是录制回放类工具的核心竞争力不在 pynput 的 API 调用上而在事件模型的设计上——录什么、不录什么、怎么处理时序、怎么容错把这些想清楚回放和代码生成都是水到渠成的事。如果看完你也想自己写一个我的建议很直接先做一个命令行版本跑通录制和回放确认能满足真实业务需求之后再加 GUI 和代码生成每一步都用实际业务操作去压测别只在演示用例上验证一遍就以为万事大吉。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻