PyChrome模拟keyDown/keyUp/keyPress键盘事件异常求助
解决PyChrome中CDP键盘事件模拟异常问题
问题现象
down方法无法按住指定按键up方法无法释放已按住的按键press方法无法正确模拟组合键(如Control+T无法打开新标签页)
问题根源
- 键映射缺失关键修饰键:当前
key_mapping中没有Control、Meta等修饰键的配置,导致组合键无法被识别。 - 事件参数不完整:CDP的
Input.dispatchKeyEvent需要完整参数(如modifiers、autoRepeat、isComposing等)才能触发系统级键盘行为,仅传递code、keyCode不足以让浏览器识别按键按住状态。 - 组合键逻辑未同步修饰键状态:模拟组合键时未跟踪修饰键的激活状态,浏览器无法识别为组合操作。
修复方案
1. 补全键映射配置
添加修饰键和常用功能键的完整映射:
key_mapping = { "Backspace": {"code": "Backspace", "key": "Backspace", "keyCode": 8, "windowsVirtualKeyCode": 8}, "Tab": {"code": "Tab", "key": "Tab", "keyCode": 9, "windowsVirtualKeyCode": 9, "text": "\t"}, "Enter": {"code": "Enter", "key": "Enter", "keyCode": 13, "windowsVirtualKeyCode": 13, "text": "\r"}, "Escape": {"code": "Escape", "key": "Escape", "keyCode": 27, "windowsVirtualKeyCode": 27}, "Space": {"code": "Space", "key": " ", "keyCode": 32, "windowsVirtualKeyCode": 32, "text": " "}, "Delete": {"code": "Delete", "key": "Delete", "keyCode": 46, "windowsVirtualKeyCode": 46}, "Control": {"code": "ControlLeft", "key": "Control", "keyCode": 17, "windowsVirtualKeyCode": 17, "modifiers": 2}, "Meta": {"code": "MetaLeft", "key": "Meta", "keyCode": 91, "windowsVirtualKeyCode": 91, "modifiers": 1024}, "Shift": {"code": "ShiftLeft", "key": "Shift", "keyCode": 16, "windowsVirtualKeyCode": 16, "modifiers": 1}, "Alt": {"code": "AltLeft", "key": "Alt", "keyCode": 18, "windowsVirtualKeyCode": 18, "modifiers": 4}, "A": {"code": "KeyA", "key": "a", "keyCode": 65, "windowsVirtualKeyCode": 65, "text": "a"}, "B": {"code": "KeyB", "key": "b", "keyCode": 66, "windowsVirtualKeyCode": 66, "text": "b"}, "C": {"code": "KeyC", "key": "c", "keyCode": 67, "windowsVirtualKeyCode": 67, "text": "c"}, "D": {"code": "KeyD", "key": "d", "keyCode": 68, "windowsVirtualKeyCode": 68, "text": "d"}, "E": {"code": "KeyE", "key": "e", "keyCode": 69, "windowsVirtualKeyCode": 69, "text": "e"}, "F": {"code": "KeyF", "key": "f", "keyCode": 70, "windowsVirtualKeyCode": 70, "text": "f"}, "G": {"code": "KeyG", "key": "g", "keyCode": 71, "windowsVirtualKeyCode": 71, "text": "g"}, "H": {"code": "KeyH", "key": "h", "keyCode": 72, "windowsVirtualKeyCode": 72, "text": "h"}, "I": {"code": "KeyI", "key": "i", "keyCode": 73, "windowsVirtualKeyCode": 73, "text": "i"}, "J": {"code": "KeyJ", "key": "j", "keyCode": 74, "windowsVirtualKeyCode": 74, "text": "j"}, "K": {"code": "KeyK", "key": "k", "keyCode": 75, "windowsVirtualKeyCode": 75, "text": "k"}, "L": {"code": "KeyL", "key": "l", "keyCode": 76, "windowsVirtualKeyCode": 76, "text": "l"}, "M": {"code": "KeyM", "key": "m", "keyCode": 77, "windowsVirtualKeyCode": 77, "text": "m"}, "N": {"code": "KeyN", "key": "n", "keyCode": 78, "windowsVirtualKeyCode": 78, "text": "n"}, "O": {"code": "KeyO", "key": "o", "keyCode": 79, "windowsVirtualKeyCode": 79, "text": "o"}, "P": {"code": "KeyP", "key": "p", "keyCode": 80, "windowsVirtualKeyCode": 80, "text": "p"}, "Q": {"code": "KeyQ", "key": "q", "keyCode": 81, "windowsVirtualKeyCode": 81, "text": "q"}, "R": {"code": "KeyR", "key": "r", "keyCode": 82, "windowsVirtualKeyCode": 82, "text": "r"}, "S": {"code": "KeyS", "key": "s", "keyCode": 83, "windowsVirtualKeyCode": 83, "text": "s"}, "T": {"code": "KeyT", "key": "t", "keyCode": 84, "windowsVirtualKeyCode": 84, "text": "t"} }
2. 修改Keyboard类实现
完善事件参数,添加修饰键状态跟踪:
import sys class Keyboard: """Handles keyboard interactions on the page""" def __init__(self, tab: Tab): self._tab = tab self._pressed_modifiers = 0 # 跟踪当前按住的修饰键位掩码 def type(self, text: str): """Simulates typing text character by character.""" for char in text: self._pressed_modifiers = 0 # 重置修饰键状态,避免干扰输入 self._dispatch_key_event("keyDown", text=char) self._dispatch_key_event("keyUp", text=char) def down(self, key: str): """Holds down a specified key without releasing it.""" key_data = self._get_key_data(key) if not key_data: return # 更新修饰键掩码 if "modifiers" in key_data: self._pressed_modifiers |= key_data["modifiers"] # 补充CDP要求的默认参数 event_params = { "autoRepeat": False, "isKeypad": False, "isComposing": False, "modifiers": self._pressed_modifiers, **key_data } self._dispatch_key_event("keyDown", **event_params) def up(self, key: str): """Releases a held key.""" key_data = self._get_key_data(key) if not key_data: return # 更新修饰键掩码 if "modifiers" in key_data: self._pressed_modifiers &= ~key_data["modifiers"] # 补充CDP要求的默认参数 event_params = { "autoRepeat": False, "isKeypad": False, "isComposing": False, "modifiers": self._pressed_modifiers, **key_data } self._dispatch_key_event("keyUp", **event_params) def press(self, combination: str): keys = combination.split("+") if not keys: return # 按下所有修饰键 for key in keys[:-1]: self.down(key) # 按下并释放主按键 main_key = keys[-1] key_data = self._get_key_data(main_key) if key_data: event_params = { "autoRepeat": False, "isKeypad": False, "isComposing": False, "modifiers": self._pressed_modifiers, **key_data } self._dispatch_key_event("keyDown", **event_params) self._dispatch_key_event("keyUp", **event_params) # 释放所有修饰键 for key in reversed(keys[:-1]): self.up(key) def _dispatch_key_event(self, event_type: str, **kwargs): """Dispatches a key event to the tab.""" self._tab.call_method( "Input.dispatchKeyEvent", type=event_type, _timeout=5, **kwargs ) def _get_key_data(self, key: str): """Helper method to retrieve the appropriate key data from the key mapping.""" normalized_key = self._normalize_key(key) key_data = key_mapping.get(normalized_key) if not key_data: print(f"Warning: Key '{key}' not found in key_mapping.") return key_data @staticmethod def _normalize_key(key: str) -> str: """Normalizes key names for compatibility with the key mapping.""" key = key.capitalize() if key == "Ctrl": return "Control" elif key == "Cmd": return "Meta" # Mac下Cmd对应Meta elif key == "Controlormeta": # 自动根据系统选择修饰键 return "Meta" if sys.platform == "darwin" else "Control" return key
3. 关键修复点说明
- 补全修饰键映射:添加
Control、Meta等修饰键的modifiers位掩码,这是CDP识别修饰键的核心参数。 - 跟踪修饰键状态:用
_pressed_modifiers变量记录当前激活的修饰键,确保后续事件的参数同步。 - 完善事件参数:补充CDP要求的默认参数,让浏览器正确识别事件类型。
- 组合键逻辑优化:复用
down和up方法处理修饰键,保证状态一致性。
测试验证
调用keyboard.press("Control+T")可正常打开新标签页;调用down("Control")后执行type("t")也能触发组合键行为;up("Control")可正确释放按键。
内容的提问来源于stack exchange,提问作者Aniket
相关产品推荐
相关产品推荐

