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 自动化真的很强!


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

相关推荐
hrhcode8 小时前
【LangGraph】六.多 Agent 协作:Subgraph 机制
python·ai·langchain·langgraph·ai框架
你的秋裤穿反了8 小时前
SCL 第一个程序
自动化
WL_Aurora8 小时前
Python 算法基础篇之栈和队列
python·算法
YJlio8 小时前
Windows Internals 10.5.3:ETW 架构详解,从事件产生到性能分析的完整链路
windows·笔记·python·stm32·嵌入式硬件·学习·架构
敲上瘾8 小时前
LangChain 结构化输出与流式传输
python·语言模型·langchain·aigc
.柒宇.8 小时前
AI 掘金头条项目-用户模块、收藏模块以及Redis和调用大模型实现
redis·python·fastapi·千问·qwen大模型
yexuhgu8 小时前
Golang如何做贪心算法_Golang贪心算法教程【速学】
jvm·数据库·python
EnCi Zheng8 小时前
M4-更新日志v0.1.3-Mermaid图表支持 [特殊字符]
python
2401_831419448 小时前
Redis如何实现多维度权重排序_利用ZSet分数计算进行优先级排列
jvm·数据库·python