ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

[python]windows下模拟鼠标键盘输入:用 TaoToken 统一 Key 打通自动化脚本配置

[python]windows下模拟鼠标键盘输入:用 TaoToken 统一 Key 打通自动化脚本配置 1. Windows 下 Python 模拟鼠标键盘输入到底难在哪如果你在 Windows 上写过桌面自动化脚本大概率绕不开win32api、win32con、ctypes这几个库。它们能做的事情很直接把光标移到指定坐标、按下左键、松开左键、模拟一次键盘敲击。听起来简单但真正落到项目里问题往往不在“怎么点”而在“脚本怎么稳定地拿到它需要的那套配置”。我先把场景说清楚。假设你有一批重复性的桌面操作打开某个客户端、在固定位置点击按钮、输入一段编号、再点确认。用 Python 写一个循环配合SetCursorPos和mouse_event几分钟就能跑起来。可一旦这个脚本要接入更复杂的流程——比如让脚本在关键节点调用一个模型来判断“当前界面是不是预期状态”或者把识别结果交给一个编码助手生成下一步动作——你就需要一套统一的接口配置Base URL、API Key、Model ID。这三样东西如果散落在各个脚本里改一次就要翻遍整个项目。这就是我写这篇的出发点。标题里提到的 TaoToken在这里扮演的角色不是“替代 win32api”而是把脚本里需要调用的模型接口统一成一份 Key、一个地址。你依然用win32api做鼠标键盘模拟但脚本里那些需要“动脑”的环节通过统一配置去请求模型。一次配置多个脚本复用。适合谁看三类人第一类是做桌面自动化、批量操作的 Python 开发者手里已经有一堆mouse_click、key_input函数第二类是想给自动化脚本加上“智能判断”能力但不想每个脚本都重写一遍请求逻辑第三类是刚接触 Windows 模拟输入想从零搭一个能跑通的最小示例。下面我会从环境准备讲到可复制配置再到一次真实的模拟输入验证最后把常见报错摊开说。先明确一个边界本文所有操作都在 Windows 本机完成不涉及任何网络访问方式的改动。TaoToken 的接入就是标准的 HTTP 接口调用你在脚本里用requests或httpx发请求即可。官网地址是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 地址是https://taotoken.net/api这两个记一下后面配置里会用到。2. 前置准备win32api 环境与 TaoToken Key 获取2.1 安装 pywin32 并验证Windows 下模拟鼠标键盘最常用的组合是pywin32。它提供了win32api、win32con、win32gui三个模块分别负责 API 调用、常量定义、窗口操作。安装命令很简单pip install pywin32装完之后别急着写脚本先验证一下能不能正常导入。打开 Python 交互环境执行import win32api import win32con from ctypes import windll print(win32con.MOUSEEVENTF_LEFTDOWN) print(win32con.KEYEVENTF_KEYUP)如果输出2和2说明环境没问题。这里解释一下这两个常量的含义MOUSEEVENTF_LEFTDOWN的值是0x0002表示按下鼠标左键KEYEVENTF_KEYUP的值也是0x0002表示松开按键。它们数值相同但用途不同别混用。有个坑要提前说如果你用的是 64 位 Pythonpywin32装完后偶尔会出现ImportError: DLL load failed。这通常是pywin32_postinstall没跑。解决办法是找到 Python 安装目录下的Scripts文件夹执行python pywin32_postinstall.py -install跑完重启终端即可。这一步不做后面所有win32api调用都会失败。2.2 获取 TaoToken Key 与模型信息脚本里需要调用模型接口时你得先有一个可用的 Key。访问https://taotoken.net/api-keys带上 UTM 参数?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content在控制台里创建一个 API Key。创建时注意两点一是 Key 只在创建时完整显示一次复制后妥善保存二是可以给 Key 起个名字比如desktop-automation方便后面区分用途。拿到 Key 之后你还需要确认两样东西Base URL 和 Model ID。Base URL 统一用https://taotoken.net/apiModel ID 根据你要用的模型来填。如果你不确定用哪个可以先在模型对话页面https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content试一下确认模型能正常响应再把 Model ID 抄到配置里。这里强调一个原则Key、Base URL、Model ID 这三件套要写进配置文件不要硬编码在脚本里。原因很实际——你可能有多个脚本今天用这个模型明天换那个模型硬编码意味着每个文件都要改。配置文件改一处所有脚本生效。2.3 目录结构建议我习惯把自动化项目组织成这样desktop_auto/ ├── config/ │ ├── config.toml │ └── settings.json ├── scripts/ │ ├── mouse_keyboard.py │ └── smart_action.py └── requirements.txtconfig.toml放接口相关的配置settings.json放脚本运行参数比如坐标、延时、重试次数。两个文件分开是因为接口配置通常不随业务逻辑变而运行参数经常调。下面第三节给出这两个文件的可复制骨架。3. 可复制配置config.toml 与 settings.json 骨架3.1 config.toml 完整内容TOML 格式在 Python 里用tomllib3.11或tomli读取可读性好适合放接口配置。下面这份可以直接复制把your_api_key_here换成你自己的 Key# config/config.toml # TaoToken 接口统一配置 # 官网: https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content [api] base_url https://taotoken.net/api api_key your_api_key_here timeout 30 max_retries 3 [model] default_model claude-sonnet-4-20250514 fallback_model gpt-4o-mini temperature 0.2 max_tokens 1024 [automation] mouse_move_delay 0.05 key_press_delay 0.05 click_hold_delay 0.02几个参数说明一下。timeout 30是请求超时秒数桌面自动化场景下网络请求不宜等太久30 秒足够。max_retries 3是失败重试次数配合后面的排错章节用。temperature 0.2是因为自动化场景需要稳定输出不需要模型发挥创造力。mouse_move_delay和key_press_delay是模拟输入之间的间隔太小容易被目标程序忽略太大拖慢整体速度0.05 秒是实测比较稳的值。3.2 settings.json 完整内容JSON 放运行参数结构清晰改起来直观{ window: { target_title: 记事本, activate_before_action: true }, coordinates: { click_point: [1024, 470], input_area: [800, 300] }, input: { test_string: ~!#$a%^d*(s)_s{}f|\:h?, use_auto_case: true }, logging: { level: INFO, file: logs/automation.log } }target_title是目标窗口标题脚本启动时先激活这个窗口避免点到别的程序上。click_point和input_area是屏幕坐标你可以用AutoScriptWriter.exe这类工具获取或者用win32gui.GetCursorPos()实时打印。test_string里故意放了一堆上档键字符用来验证key_autinput函数能不能正确识别大小写和符号。3.3 读取配置的 Python 代码配置写好了得有代码读它。下面这段同时读 TOML 和 JSON返回一个统一的配置对象import json import tomllib from pathlib import Path def load_config(base_dirconfig): base Path(base_dir) with open(base / config.toml, rb) as f: toml_cfg tomllib.load(f) with open(base / settings.json, r, encodingutf-8) as f: json_cfg json.load(f) return {api: toml_cfg[api], model: toml_cfg[model], automation: toml_cfg[automation], runtime: json_cfg} if __name__ __main__: cfg load_config() print(cfg[api][base_url]) print(cfg[runtime][coordinates][click_point])跑一下能打印出https://taotoken.net/api和[1024, 470]就说明配置读取正常。注意tomllib需要 Python 3.11 及以上如果你用的是 3.10 或更低把import tomllib换成import tomli as tomllib并pip install tomli。4. 验证请求一次完整的模拟鼠标键盘输入4.1 模拟输入核心函数先把 excerpt 里那套函数整理成可用的模块。核心逻辑不变但加上配置读取和日志import time import win32api import win32con from ctypes import windll VK_CODE { backspace: 0x08, tab: 0x09, enter: 0x0D, shift: 0x10, ctrl: 0x11, alt: 0x12, esc: 0x1B, spacebar: 0x20, a: 0x41, b: 0x42, c: 0x43, d: 0x44, e: 0x45, f: 0x46, g: 0x47, h: 0x48, i: 0x49, j: 0x4A, k: 0x4B, l: 0x4C, m: 0x4D, n: 0x4E, o: 0x4F, p: 0x50, q: 0x51, r: 0x52, s: 0x53, t: 0x54, u: 0x55, v: 0x56, w: 0x57, x: 0x58, y: 0x59, z: 0x5A, 0: 0x30, 1: 0x31, 2: 0x32, 3: 0x33, 4: 0x34, 5: 0x35, 6: 0x36, 7: 0x37, 8: 0x38, 9: 0x39, : 0xBB, ,: 0xBC, -: 0xBD, .: 0xBE, /: 0xBF, : 0xC0, ;: 0xBA, [: 0xDB, \\: 0xDC, ]: 0xDD, : 0xDE } VK_SHIFT_MAP { A: a, B: b, C: c, D: d, E: e, F: f, G: g, H: h, I: i, J: j, K: k, L: l, M: m, N: n, O: o, P: p, Q: q, R: r, S: s, T: t, U: u, V: v, W: w, X: x, Y: y, Z: z, ): 0, !: 1, : 2, #: 3, $: 4, %: 5, ^: 6, : 7, *: 8, (: 9, : , : ,, _: -, : ., ?: /, ~: , :: ;, {: [, |: \\, }: ], : } def mouse_move(x, y): windll.user32.SetCursorPos(x, y) def mouse_click(xNone, yNone, delay0.05): if x is not None and y is not None: mouse_move(x, y) time.sleep(delay) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0) win32api.mouse_event(win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0) def key_autinput(text, delay0.05): for c in text: if c in VK_SHIFT_MAP: base VK_SHIFT_MAP[c] win32api.keybd_event(VK_CODE[shift], 0, 0, 0) win32api.keybd_event(VK_CODE[base], 0, 0, 0) win32api.keybd_event(VK_CODE[shift], 0, win32con.KEYEVENTF_KEYUP, 0) win32api.keybd_event(VK_CODE[base], 0, win32con.KEYEVENTF_KEYUP, 0) elif c in VK_CODE: win32api.keybd_event(VK_CODE[c], 0, 0, 0) win32api.keybd_event(VK_CODE[c], 0, win32con.KEYEVENTF_KEYUP, 0) time.sleep(delay)这段代码和 excerpt 里的逻辑一致但做了两点改进一是把VK_CODE精简到常用键避免字典过长影响阅读二是key_autinput统一处理上档键和下档键不需要再分三个函数。4.2 接入 TaoToken 做智能判断现在加一个环节脚本在点击之前先请求模型判断“当前是否应该执行点击”。这个判断逻辑可以很简单比如把当前窗口标题发给模型让它返回yes或no。请求代码用requestsimport requests def ask_model(prompt, cfg): url f{cfg[api][base_url]}/v1/messages headers { x-api-key: cfg[api][api_key], anthropic-version: 2023-06-01, content-type: application/json } payload { model: cfg[model][default_model], max_tokens: cfg[model][max_tokens], temperature: cfg[model][temperature], messages: [{role: user, content: prompt}] } for attempt in range(cfg[api][max_retries]): try: resp requests.post(url, headersheaders, jsonpayload, timeoutcfg[api][timeout]) resp.raise_for_status() data resp.json() return data[content][0][text] except requests.RequestException as e: print(fattempt {attempt 1} failed: {e}) time.sleep(1) return None注意base_url后面拼的是/v1/messages这是 Anthropic 兼容格式。如果你用的模型走 OpenAI 兼容格式路径改成/v1/chat/completions请求体结构也要相应调整。Model ID 从配置里读不要写死。4.3 完整验证脚本把上面几块拼起来写一个verify.pyimport time import win32gui from config_loader import load_config from input_sim import mouse_click, key_autinput from model_client import ask_model def main(): cfg load_config() target cfg[runtime][window][target_title] hwnd win32gui.FindWindow(None, target) if hwnd: win32gui.SetForegroundWindow(hwnd) time.sleep(0.5) print(factivated window: {target}) else: print(fwindow not found: {target}) return prompt f当前窗口标题是 {target}是否应该执行点击操作只回答 yes 或 no。 answer ask_model(prompt, cfg) print(fmodel answer: {answer}) if answer and yes in answer.lower(): x, y cfg[runtime][coordinates][click_point] mouse_click(x, y) time.sleep(0.3) key_autinput(cfg[runtime][input][test_string]) print(simulation done) else: print(skipped by model decision) if __name__ __main__: main()跑这个脚本之前先打开记事本把窗口标题确认为“记事本”或者改settings.json里的target_title。运行后你会看到窗口被激活模型返回判断然后鼠标移动到(1024, 470)点击接着自动输入那串包含大小写和符号的测试字符串。如果记事本里出现了~!#$a%^d*(s)_s{}f|:h?说明整条链路通了。5. 常见报错排查401、local proxy failed、reading choices5.1 401 Unauthorized这是最常见的报错原因通常有三个。第一Key 没填对。检查config.toml里的api_key是不是完整复制了有没有多余空格。第二请求头字段名写错。Anthropic 格式用x-api-keyOpenAI 格式用Authorization: Bearer key两者不能混。第三Key 被禁用或过期。去控制台确认一下 Key 状态。排查方法用curl单独测一次排除脚本干扰curl -X POST https://taotoken.net/api/v1/messages \ -H x-api-key: your_api_key_here \ -H anthropic-version: 2023-06-01 \ -H content-type: application/json \ -d {model:claude-sonnet-4-20250514,max_tokens:64,messages:[{role:user,content:hi}]}如果curl能通而脚本不通问题在脚本的请求构造如果curl也报 401问题在 Key 本身。5.2 local proxy failed这个报错通常出现在请求库尝试走系统代理时。桌面自动化脚本一般不需要代理所以最直接的办法是在请求里显式禁用session requests.Session() session.trust_env False resp session.post(url, headersheaders, jsonpayload, timeout30)trust_env False会让requests忽略环境变量里的代理设置。如果你用的是httpx对应写法是httpx.Client(trust_envFalse)。注意这里说的是禁用代理不是配置代理两者方向相反别搞混。5.3 reading choices 相关报错如果你用的是 OpenAI 兼容格式返回体里会有choices字段。报错KeyError: choices通常意味着返回结构和你预期的不一样。可能原因一是请求路径写成了/v1/messages但用的是 OpenAI 格式返回体里没有choices二是模型返回了错误信息resp.json()里是{error: {...}}而不是正常结构。排查时先把原始返回打出来print(resp.status_code) print(resp.text[:500])看到实际返回内容问题就清楚了。如果是路径和格式不匹配把/v1/messages改成/v1/chat/completions请求体里的messages结构保持不变但响应解析要改成data[choices][0][message][content]。5.4 OAuth 相关提示有些工具链会提示 OAuth 认证比如 Claude Code 或某些 CLI 工具。如果你在脚本里看到 OAuth 相关报错说明该工具期望的是 OAuth 流程而不是 API Key。这种情况下检查你用的工具是否支持 API Key 模式。TaoToken 的接入以 API Key 为主配置时确认base_url和api_key都填对不要混入 OAuth 的 token 字段。5.5 模拟输入无效脚本跑完没报错但鼠标没动、键盘没输入通常是这几个原因目标窗口没有获得焦点SetForegroundWindow失败坐标超出了当前屏幕分辨率keybd_event的按键码不对比如把VK_CODE[a]写成了0x41之外的値。排查时加日志把每一步的返回值打出来print(win32gui.GetForegroundWindow()) print(win32api.GetCursorPos())对比预期值很快能定位。6. 一次配置多脚本复用把 Key 管理收拢到一处写到这里核心链路已经跑通了。回头看整个方案的关键不在于mouse_event或keybd_event本身——这些 API 十几年没变过——而在于把接口配置从脚本里抽出来收拢到config.toml和settings.json两个文件。你新增一个自动化脚本时只需要load_config()不用再复制一遍 Key 和 Base URL。如果你后面要跑更长时间的编码任务或 Agent 流程可以了解一下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content它适合需要持续调用模型的场景。日常调试模型响应用模型对话页面https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content就够了。Key 管理在控制台https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。最后留一个实用技巧把config.toml加入.gitignore只提交一份config.toml.example里面用占位符代替真实 Key。这样团队协作时不会误传密钥新人克隆项目后复制一份改个名就能跑。这个习惯看起来小但能省掉很多麻烦。
返回列表