Python + PyAutoGUI 实现一键清理:从 OpenCV 图像识别到“按键精灵“的自动化之路

前言

上篇文章说到我装了 148 个 Skills 到 CC Switch 里,想清理却发现根本没有批量删除功能。没办法,只能自己动手写脚本。

这篇文章记录了我的自动化方案演进过程------从一开始想用 OpenCV 搞图像识别,到最后发现一个简单的 PyAutoGUI 脚本就能搞定。 有时候最土的方案反而是最好的方案。


需求很明确

CC Switch 的删除操作分两步:

  1. 点击删除按钮

  2. 弹出确认框后,点击确认

148 个 Skills,每个都要重复这两个动作。手动点 296 下?疯了吧。


方案一:OpenCV 图像识别(想多了)

我的第一反应是用 OpenCV 做图像识别,自动找到删除按钮和确认按钮。

思路大概是这样的:

python

复制代码
import cv2
import pyautogui
import numpy as np

# 截屏
screenshot = pyautogui.screenshot()
screen = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)

# 加载按钮模板
delete_template = cv2.imread('delete_button.png')
confirm_template = cv2.imread('confirm_button.png')

# 模板匹配
res = cv2.matchTemplate(screen, delete_template, cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

# 点击
if max_val > 0.8:
    pyautogui.click(max_loc[0], max_loc[1])

但很快我就发现这个方案太蠢了:

  1. 需要截图模板:不同分辨率、不同主题下按钮长得不一样,模板匹配可能失灵

  2. 窗口位置会变:CC Switch 窗口每次打开位置可能不同

  3. 杀鸡用牛刀:我就点两个固定位置的按钮,至于上 CV 吗?

  4. 开发时间太长:调模板匹配参数、处理各种边界情况,比手动删还费时间

果断放弃。


方案二:PyAutoGUI + 全局热键(最终方案)

冷静下来一想,我要做的其实就是一个"按键精灵":

  1. 先手动把鼠标移到删除按钮上,记录坐标

  2. 再手动把鼠标移到确认按钮上,记录坐标

  3. 让脚本循环点击这两个位置

就这么简单,根本不需要图像识别。


技术选型

用途
pyautogui 模拟鼠标点击、获取鼠标位置
keyboard 全局热键监听
json 保存配置,下次直接用

安装:

bash

复制代码
pip install pyautogui keyboard

完整代码

python

复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
简单点击机器人
快捷键获取鼠标位置,自动循环点击
"""

import pyautogui
import time
import json
import os
import sys

try:
    import keyboard
except ImportError:
    print("请先安装: pip install keyboard")
    sys.exit(1)

SAVE_FILE = "click_points.json"

# 全局状态
delete_pos = None
confirm_pos = None
running = False

def log(msg):
    print(f"[{time.strftime('%H:%M:%S')}] {msg}")

def save_config():
    data = {"delete": delete_pos, "confirm": confirm_pos}
    with open(SAVE_FILE, 'w') as f:
        json.dump(data, f)
    log(f"配置已保存")

def load_config():
    global delete_pos, confirm_pos
    if os.path.exists(SAVE_FILE):
        with open(SAVE_FILE, 'r') as f:
            data = json.load(f)
        delete_pos = data.get("delete")
        confirm_pos = data.get("confirm")
        if delete_pos:
            log(f"已加载: 删除按钮 {delete_pos}")
        if confirm_pos:
            log(f"已加载: 确认按钮 {confirm_pos}")
        return True
    return False

def get_mouse_pos():
    """获取当前鼠标位置"""
    x, y = pyautogui.position()
    return (x, y)

def set_delete_pos():
    """设置删除按钮位置"""
    global delete_pos
    delete_pos = get_mouse_pos()
    log(f"删除按钮位置: {delete_pos}")
    save_config()

def set_confirm_pos():
    """设置确认按钮位置"""
    global confirm_pos
    confirm_pos = get_mouse_pos()
    log(f"确认按钮位置: {confirm_pos}")
    save_config()

def do_click():
    """执行一次点击流程"""
    global running
    if not delete_pos:
        log("请先设置删除按钮位置 (F1)")
        return
    
    # 点击删除
    pyautogui.click(delete_pos[0], delete_pos[1])
    time.sleep(0.3)
    
    # 确认
    if confirm_pos:
        pyautogui.click(confirm_pos[0], confirm_pos[1])
    else:
        pyautogui.press('enter')
    
    time.sleep(0.3)

def start_clicking():
    """开始自动点击"""
    global running
    
    if running:
        return
    
    if not delete_pos:
        log("请先设置删除按钮位置 (F1)")
        return
    
    running = True
    log("开始自动点击...")
    
    count = 0
    while running:
        do_click()
        count += 1
        if count % 10 == 0:
            log(f"已点击 {count} 次")
        time.sleep(0.5)
    
    log(f"停止,共点击 {count} 次")

def stop_clicking():
    """停止点击"""
    global running
    running = False

def toggle_clicking():
    """切换开始/停止"""
    if running:
        stop_clicking()
    else:
        import threading
        t = threading.Thread(target=start_clicking)
        t.daemon = True
        t.start()

def main():
    print("=" * 50)
    print(" 简单点击机器人")
    print("=" * 50)
    print()
    print("快捷键:")
    print("  F1 = 设置删除按钮位置 (鼠标移到位置后按F1)")
    print("  F2 = 设置确认按钮位置 (鼠标移到位置后按F2)")
    print("  F3 = 开始/停止 自动点击")
    print("  F4 = 退出程序")
    print()
    print("使用步骤:")
    print("  1. 把鼠标移到删除按钮上,按 F1")
    print("  2. 手动点一下删除,等确认弹窗出来")
    print("  3. 把鼠标移到确认按钮上,按 F2")
    print("  4. 按 F3 开始自动点击")
    print("  5. 再按 F3 停止")
    print()
    
    # 加载配置
    load_config()
    
    # 注册热键
    keyboard.add_hotkey('f1', set_delete_pos)
    keyboard.add_hotkey('f2', set_confirm_pos)
    keyboard.add_hotkey('f3', toggle_clicking)
    keyboard.add_hotkey('f4', lambda: os._exit(0))
    
    log("热键已注册,等待操作...")
    
    # 保持运行
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        pass

if __name__ == "__main__":
    main()

使用方法

快捷键 功能
F1 记录删除按钮位置
F2 记录确认按钮位置
F3 开始/停止 自动点击
F4 退出程序

操作步骤:

text

复制代码
步骤1: 把鼠标移到删除按钮上 → 按 F1
步骤2: 手动点一下删除,等确认弹窗出来
步骤3: 把鼠标移到确认按钮上 → 按 F2
步骤4: 按 F3 开始自动点击
步骤5: 再按 F3 停止

运行效果:

text

复制代码
==================================================
 简单点击机器人
==================================================

快捷键:
  F1 = 设置删除按钮位置 (鼠标移到位置后按F1)
  F2 = 设置确认按钮位置 (鼠标移到位置后按F2)
  F3 = 开始/停止 自动点击
  F4 = 退出程序

[21:30:00] 热键已注册,等待操作...
[21:30:05] 删除按钮位置: (1856, 320)
[21:30:10] 确认按钮位置: (960, 540)
[21:30:15] 开始自动点击...
[21:30:45] 已点击 10 次
[21:31:15] 已点击 20 次
[21:31:20] 停止,共点击 22 次

核心设计思路

1. 为什么不用图像识别?

一句话:坐标固定的场景下,图像识别纯属多余。

图像识别适合按钮位置会变化的场景(比如网页上滚动加载的内容)。但 CC Switch 的窗口位置是你自己打开的,删除按钮和确认按钮的位置基本固定。这种情况下,手动标一次坐标比调半天 CV 参数快得多。

2. 多线程点击

python

复制代码
def toggle_clicking():
    if running:
        stop_clicking()
    else:
        import threading
        t = threading.Thread(target=start_clicking)
        t.daemon = True
        t.start()

点击循环在独立线程中运行,不阻塞主线程,这样才能随时按 F3 停止。

3. 配置持久化

位置信息自动保存到 click_points.json,下次打开脚本直接用,不用重新标记。


方案对比总结

方案 复杂度 适用场景 本次评价
手动点 10个以内 太累
OpenCV 图像识别 按钮位置不确定 杀鸡用牛刀
PyAutoGUI + 热键 按钮位置固定 ✅ 最合适

扩展应用

这个脚本不只是用来删 Skills,还可以用于:

场景 说明
批量删除文件 任何需要反复确认删除的场景
游戏挂机 设置技能按钮位置,自动释放技能
表单填写 设置输入框位置,自动粘贴内容
数据采集 设置翻页按钮位置,自动翻页

注意事项

  1. 管理员权限:Windows 下全局热键可能需要管理员权限运行

  2. 安全停止:除了 F3,也可以把鼠标快速移到屏幕左上角强制停止(PyAutoGUI 默认开启 FAILSAFE)

  3. 点击间隔:代码里默认 0.5 秒一次,可根据需要调整

  4. DPI 缩放:如果点击位置偏移,可能是高 DPI 屏幕缩放问题,右键脚本 → 属性 → 兼容性 → 更改高 DPI 设置


结语

这次经历最大的感悟就是:

写自动化脚本最忌讳"想太多"。

明明一个土办法 5 分钟就能搞定的事情,非得上 OpenCV、机器学习、深度学习三件套,结果调参调到怀疑人生。很多时候最简单的方案就是最好的方案。

代码不到 150 行,但解决了实际问题。Python 自动化真的很强!


如果这篇文章对你有帮助,欢迎点赞收藏!有问题欢迎评论区讨论。

相关推荐
用户8356290780519 小时前
Python 实现 PDF 文件加密与解密方法
后端·python
用户8356290780519 小时前
使用 Python 冻结与拆分 Excel 窗格教程
后端·python
你好潘先生17 小时前
别再记命令了,用 yeero do 说句人话就能跑脚本,而且不烧 token
服务器·python·命令行
Agent_大师17 小时前
WebSocket 行情重连成功,K线缺口不会自动消失
python
荣码17 小时前
LLM结构化输出:让AI返回JSON而不是废话,我踩了4个坑
java·python
copyer_xyf18 小时前
FastAPI 如何连接 MySQL
后端·python
apocelipes1 天前
常用编程语言和库的正则表达式性能对比
c语言·c++·python·性能优化·golang·开发工具和环境
用户8356290780511 天前
使用 Python 在 PDF 中创建与管理书签
后端·python
程序员徐公1 天前
Claude Code 是怎么悄悄认出中国用户的
claudecode
MeixianAgent2 天前
Python 回测数据入口怎么验?历史 K 线入库前先做 5 个检查
后端·python