python模拟键盘 鼠标操作 通过ctypes调用Windows API实现底层输入模拟
Windows下Python自动键鼠模拟器源码详解
本文介绍一个基于Python的Windows键盘鼠标自动化模拟器,适合自动化测试、游戏辅助等场景。代码通过ctypes调用Windows API实现底层输入模拟。
实测案例:bongo cat刷点击
1. 主要功能
- 模拟键盘按键(按下、释放、完整按键)
- 模拟鼠标点击(左键、右键,可指定坐标)
- 支持绝对坐标定位
- 可自定义点击间隔和范围
2. 代码结构
2.1 InputSimulator类
负责封装所有输入模拟相关方法。
class InputSimulator:def __init__(self):self.user32 = ctypes.WinDLL('user32', use_last_error=True)# ... 定义各种常量 ...
主要方法
press_key(key_code)
:模拟按键按下release_key(key_code)
:模拟按键释放key_press(key_code, duration=0.1)
:完整按键(按下+延时+释放)click_mouse(x=None, y=None)
:模拟鼠标左键点击(可指定坐标)right_click_mouse(x=None, y=None)
:模拟鼠标右键点击(可指定坐标)
2.2 结构体定义
使用ctypes定义Windows API所需的结构体:
class MOUSEINPUT(ctypes.Structure):_fields_ = (("dx", wintypes.LONG),("dy", wintypes.LONG),("mouseData", wintypes.DWORD),("dwFlags", wintypes.DWORD),("time", wintypes.DWORD),("dwExtraInfo", ctypes.POINTER(wintypes.ULONG)))
# 还有 KEYBDINPUT 和 INPUT 结构体
2.3 示例用法
主程序部分演示了如何自动点击:
if __name__ == "__main__":import keyboardimport randomsimulator = InputSimulator()print("程序已启动,按Q键退出...")while True:if keyboard.is_pressed('q'):print("检测到Q键,程序退出")breakx = random.randint(1000, 1100)y = random.randint(700, 800)simulator.click_mouse(x, y)time.sleep(random.uniform(0.05, 0.1))
- 随机生成点击坐标
- 按Q键可随时退出
3. 技术要点
- ctypes调用user32.dll:实现底层输入模拟
- 支持绝对坐标:通过65535缩放映射屏幕坐标
- 结构体封装:与Windows API参数完全兼容
- 可扩展性强:可根据需要添加更多输入事件
4. 注意事项
- 仅支持Windows系统
- 需管理员权限
- 请勿在重要操作界面误用
5. 应用场景
- 自动化测试
- 游戏脚本
- 重复性办公自动化
- 系统集成测试
完整代码见下方:
点击展开查看完整代码import ctypes
import time
from ctypes import wintypesclass InputSimulator:def __init__(self):self.user32 = ctypes.WinDLL('user32', use_last_error=True)# Windows API 常量self.INPUT_MOUSE = 0self.INPUT_KEYBOARD = 1self.KEYEVENTF_KEYUP = 0x0002self.KEYEVENTF_UNICODE = 0x0004self.MOUSEEVENTF_LEFTDOWN = 0x0002self.MOUSEEVENTF_LEFTUP = 0x0004self.MOUSEEVENTF_RIGHTDOWN = 0x0008self.MOUSEEVENTF_RIGHTUP = 0x0010self.MOUSEEVENTF_MOVE = 0x0001self.MOUSEEVENTF_ABSOLUTE = 0x8000def press_key(self, key_code):"""模拟按键按下"""x = INPUT(type=self.INPUT_KEYBOARD,ki=KEYBDINPUT(wVk=key_code))self.user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))def release_key(self, key_code):"""模拟按键释放"""x = INPUT(type=self.INPUT_KEYBOARD,ki=KEYBDINPUT(wVk=key_code,dwFlags=self.KEYEVENTF_KEYUP))self.user32.SendInput(1, ctypes.byref(x), ctypes.sizeof(x))def key_press(self, key_code, duration=0.1):"""模拟完整的按键过程"""self.press_key(key_code)time.sleep(duration)self.release_key(key_code)def click_mouse(self, x=None, y=None):"""模拟鼠标点击参数:x: 鼠标x坐标(可选)y: 鼠标y坐标(可选)"""if x is not None and y is not None:screen_width = self.user32.GetSystemMetrics(0)screen_height = self.user32.GetSystemMetrics(1)x = int(x * 65535 / screen_width)y = int(y * 65535 / screen_height)move = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dx=x, dy=y, dwFlags=self.MOUSEEVENTF_MOVE | self.MOUSEEVENTF_ABSOLUTE))self.user32.SendInput(1, ctypes.byref(move), ctypes.sizeof(move))time.sleep(0.1)down = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dwFlags=self.MOUSEEVENTF_LEFTDOWN))self.user32.SendInput(1, ctypes.byref(down), ctypes.sizeof(down))time.sleep(0.1)up = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dwFlags=self.MOUSEEVENTF_LEFTUP))self.user32.SendInput(1, ctypes.byref(up), ctypes.sizeof(up))def right_click_mouse(self, x=None, y=None):"""模拟鼠标右键点击"""if x is not None and y is not None:screen_width = self.user32.GetSystemMetrics(0)screen_height = self.user32.GetSystemMetrics(1)x = int(x * 65535 / screen_width)y = int(y * 65535 / screen_height)move = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dx=x, dy=y, dwFlags=self.MOUSEEVENTF_MOVE | self.MOUSEEVENTF_ABSOLUTE))self.user32.SendInput(1, ctypes.byref(move), ctypes.sizeof(move))time.sleep(0.1)down = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dwFlags=self.MOUSEEVENTF_RIGHTDOWN))self.user32.SendInput(1, ctypes.byref(down), ctypes.sizeof(down))time.sleep(0.1)up = INPUT(type=self.INPUT_MOUSE,mi=MOUSEINPUT(dwFlags=self.MOUSEEVENTF_RIGHTUP))self.user32.SendInput(1, ctypes.byref(up), ctypes.sizeof(up))# 保持原有的结构体定义
class MOUSEINPUT(ctypes.Structure):_fields_ = (("dx", wintypes.LONG),("dy", wintypes.LONG),("mouseData", wintypes.DWORD),("dwFlags", wintypes.DWORD),("time", wintypes.DWORD),("dwExtraInfo", ctypes.POINTER(wintypes.ULONG)))class KEYBDINPUT(ctypes.Structure):_fields_ = (("wVk", wintypes.WORD),("wScan", wintypes.WORD),("dwFlags", wintypes.DWORD),("time", wintypes.DWORD),("dwExtraInfo", ctypes.POINTER(wintypes.ULONG)))class INPUT(ctypes.Structure):class _INPUT(ctypes.Union):_fields_ = (("mi", MOUSEINPUT),("ki", KEYBDINPUT))_anonymous_ = ("_input",)_fields_ = (("type", wintypes.DWORD),("_input", _INPUT))# 使用示例
if __name__ == "__main__":"""# 创建模拟器实例simulator = InputSimulator()# 示例:模拟按下A键simulator.key_press(0x41)# 示例:模拟鼠标点击simulator.click_mouse()# 示例:在指定位置右键点击simulator.right_click_mouse(100, 100)"""import keyboardimport randomsimulator = InputSimulator()print("程序已启动,按Q键退出...")while True:if keyboard.is_pressed('q'):print("检测到Q键,程序退出")break# 随机坐标(在1000-1100和700-800范围内)x = random.randint(1000, 1100)y = random.randint(700, 800)# # 模拟鼠标右键点击# simulator.right_click_mouse(x, y)# time.sleep(random.uniform(0.05, 0.1))# 模拟鼠标左键点击simulator.click_mouse(x, y)time.sleep(random.uniform(0.05, 0.1))