Python 自动化微信消息发送:`uiautomation` 与 `uiautomation2` 实战指南

Python 自动化微信消息发送:uiautomationuiautomation2 实战指南

在日常工作和生活中,我们可能需要重复地给某些微信联系人或群组发送相同的消息,例如每日问候、定时提醒、信息通知等。手动操作不仅效率低下,还容易出错。幸运的是,借助 Python 的自动化库,我们可以轻松实现微信消息的自动发送。

本文将介绍两个强大的 UI 自动化库:uiautomation (针对 Windows 平台) 和 uiautomation2 (针对 Android 平台),并通过实例代码演示如何使用它们来自动化微信PC版和安卓版的消息发送。

本文目标

  1. 了解 uiautomation 库及其在 Windows 微信自动化中的应用。
  2. 了解 uiautomation2 库及其在 Android 微信自动化中的应用。
  3. 掌握通过代码定位微信UI元素并执行操作的基本方法。
  4. 学习编写简单的 Python 脚本实现微信消息自动发送。

准备工作

通用准备:

  • Python 环境: 确保你已安装 Python 3.x。
  • 微信客户端:
    • 对于 uiautomation:安装最新版的微信 PC 客户端。
    • 对于 uiautomation2:在 Android 手机或模拟器上安装最新版的微信 App。

1. uiautomation (Windows PC 版微信)

  • 安装库:
    pip install uiautomation
    
  • 辅助工具 (可选但推荐):
    • Inspect.exe: Windows SDK 自带的工具,用于查看UI元素的属性。
    • UI Spy (UISpy.exe): 类似的UI元素查看工具。
    • automation.py -h 查看 uiautomation 自带的元素查看器用法。 (如 python -m uiautomation -t 3)

2. uiautomation2 (Android 版微信)

  • 安装库:
    pip install uiautomation2 weditor
    
    weditor 是一个基于 Web 的 UI 查看器,非常方便。
  • Android 环境:
    • ADB (Android Debug Bridge): 确保 ADB 已安装并配置到系统环境变量。你可以从 Android SDK Platform Tools 下载。
    • 手机/模拟器开启开发者选项和 USB 调试。
  • 初始化 uiautomation2
    在手机上安装 atx-agent (通常 uiautomation2 会尝试自动安装)。连接手机到电脑,并运行:
    python -m uiautomator2 init
    
    确保手机已通过 USB 连接并授权调试。

一、使用 uiautomation 自动发送 PC 版微信消息

uiautomation 是一个基于 Windows UI Automation API 的 Python 模块,可以方便地自动化 Windows 桌面应用程序。

核心思路:

  1. 定位到微信主窗口。
  2. 在搜索框中输入联系人名称。
  3. 点击搜索结果中的联系人,进入聊天界面。
  4. 定位到消息输入框,输入消息。
  5. 点击发送按钮。

示例代码:

import uiautomation as auto
import time
import pyperclip # 用于处理中文输入

def send_wechat_message_pc(contact_name: str, message: str):
    """
    使用 uiautomation 给 PC 版微信发送消息。
    确保微信已登录并在前台。
    """
    print(f"准备向PC微信联系人 '{contact_name}' 发送消息: '{message}'")

    # 1. 获取微信主窗口
    # 通常微信窗口的 ClassName 是 'WeChatMainWndForPC'
    # 你可以使用 Inspect.exe 或 automation.py -t 3 来确认
    wechat_window = auto.WindowControl(ClassName='WeChatMainWndForPC')

    if not wechat_window.Exists(timeout=5):
        print("错误:未找到微信主窗口。请确保微信已登录并打开。")
        return

    # 将窗口置顶,方便操作 (可选)
    wechat_window.SetTopmost(True)
    time.sleep(0.5)

    # 2. 定位搜索框并点击,然后输入联系人
    # 搜索框通常 AutomationId 是 'searchEdit' 或 Name 是 '搜索'
    # 使用 auto.EditControl(searchDepth=10, Name='搜索') 或其他属性定位
    # 技巧:先点击一下左侧的“聊天”列表,确保焦点在聊天列表区域
    try:
        chat_list_btn = wechat_window.ButtonControl(Name='聊天')
        if chat_list_btn.Exists(0,0):
            chat_list_btn.Click(simulateMove=False)
            time.sleep(0.2)
    except Exception:
        pass # 可能已经在聊天界面

    search_box = wechat_window.EditControl(Name='搜索') # PC微信更新后,搜索框可能在主窗口的子控件中
    if not search_box.Exists(2,1):
        print("错误:未找到搜索框。")
        wechat_window.SetTopmost(False)
        return

    search_box.Click(simulateMove=False)
    time.sleep(0.5)
    # search_box.SetValue(contact_name) # SetValue 对于某些应用输入中文可能存在问题
    pyperclip.copy(contact_name)
    auto.SendKeys('{Ctrl}v') # 使用Ctrl+V粘贴,兼容中文
    time.sleep(1) # 等待搜索结果

    # 3. 点击搜索结果中的联系人
    # 搜索结果通常在 "会话" Pane 下的 ListItemControl
    # 注意:这里需要根据实际情况调整,可能需要更精确的定位
    # 可以使用 contact_name 来定位 ListItemControl
    contact_item = wechat_window.ListItemControl(Name=contact_name, searchDepth=15) # 增加搜索深度
    if not contact_item.Exists(2,1):
        print(f"错误:未在搜索结果中找到联系人 '{contact_name}'。请检查名称是否准确或手动打开聊天窗口。")
        # 尝试按回车键,因为第一个搜索结果往往是目标
        auto.SendKeys('{Enter}')
        time.sleep(1)
        # 再次检查当前聊天对象是否正确 (可选,较复杂)
    else:
        contact_item.Click(simulateMove=False)
        time.sleep(1) # 等待聊天窗口加载

    # 4. 定位消息输入框并输入消息
    # 输入框通常是 EditControl,Name 可能是 "输入" 或为空,需要用其他属性辅助
    # 通常在当前聊天窗口的最后一个 EditControl
    input_box = wechat_window.EditControl(searchDepth=20) # 通常是最后一个EditControl,可以加更精确的条件
    if not input_box.Exists(2,1): # 有时候输入框没有Name,只能通过其他方式定位,如ClassName='RichEditComponent'
        # 尝试一种更通用的定位方式:寻找当前窗口中最后一个EditControl
        edits = wechat_window.GetChildren()
        target_edit = None
        for child in edits:
            if child.ControlTypeName == 'EditControl': # 迭代查找
                # 通常输入框在比较靠下的位置
                # print(f"Found Edit: {child.Name}, {child.BoundingRectangle}")
                target_edit = child # 简单取最后一个(可能不准,需要调试)

        if target_edit:
            input_box = target_edit
        else:
            print("错误:未找到消息输入框。")
            wechat_window.SetTopmost(False)
            return

    input_box.Click(simulateMove=False)
    time.sleep(0.5)
    # input_box.SetValue(message)
    pyperclip.copy(message)
    auto.SendKeys('{Ctrl}v')
    time.sleep(0.5)

    # 5. 点击发送按钮
    # 发送按钮通常是 ButtonControl,Name 是 "发送"
    send_button = wechat_window.ButtonControl(Name='发送')
    if not send_button.Exists(2,1):
        print("错误:未找到发送按钮。尝试直接按回车发送。")
        auto.SendKeys('{Enter}') # 如果找不到发送按钮,尝试直接回车
    else:
        send_button.Click(simulateMove=False)

    print(f"消息已尝试发送给 '{contact_name}'。")
    wechat_window.SetTopmost(False) # 取消置顶

if __name__ == '__main__':
    # 确保微信PC版已登录并打开
    # 首次运行前,最好手动操作一遍,观察微信的响应,并用Inspect.exe等工具确定控件名称
    target_contact = "文件传输助手"  # 替换为你要发送的联系人名称,确保名称在微信中完全一致
    message_to_send = "你好,这是一条来自Python的自动消息!(PC)"

    # 等待几秒,给你切换到微信的时间
    print("请在5秒内确保微信窗口是激活状态...")
    time.sleep(5)

    send_wechat_message_pc(target_contact, message_to_send)

使用 uiautomation 的注意事项:

  • 控件属性变化: 微信版本更新可能会导致控件的 Name, AutomationId, ClassName 等属性发生变化,脚本可能需要随之调整。
  • 定位精度: searchDepth 参数可以帮助在复杂的UI结构中找到元素。有时需要组合多个属性来唯一定位一个控件。
  • 中文输入: SetValue() 方法直接输入中文可能存在问题,推荐使用 pyperclip 配合 SendKeys('{Ctrl}v') 进行粘贴。
  • 延时: 在点击、输入等操作后加入适当的 time.sleep(),等待UI响应,避免操作过快导致失败。
  • 窗口状态: 确保微信窗口是激活的,或者在代码中先激活它。

二、使用 uiautomation2 自动发送 Android 版微信消息

uiautomation2 是一个强大的 Android UI 自动化框架,它通过 Python 连接到 Android 设备(真实手机或模拟器)上运行的 uiautomator2 server (通常是 atx-agent),进而控制设备上的应用。

核心思路:

  1. 连接到 Android 设备。
  2. 启动微信 App (如果未启动)。
  3. 点击微信顶部的搜索按钮。
  4. 在搜索框中输入联系人名称,并点击搜索结果。
  5. 在聊天界面,定位输入框并输入消息。
  6. 点击发送按钮。

辅助工具:weditor

启动 weditor 非常简单,连接手机后,在命令行运行:

python -m weditor

它会在浏览器中打开一个页面,可以实时查看手机屏幕,并获取UI元素的 resourceId, text, className, description 等属性,极大方便了元素定位。

示例代码:

import u2 as uiautomator2
import time

def send_wechat_message_android(device_serial: str, contact_name: str, message: str):
    """
    使用 uiautomation2 给 Android 版微信发送消息。
    确保手机已通过 adb 连接,并且微信已登录。
    """
    print(f"准备向Android微信联系人 '{contact_name}' 发送消息: '{message}'")

    try:
        # 1. 连接设备
        if device_serial:
            d = uiautomator2.connect(device_serial) # 连接指定设备
        else:
            d = uiautomator2.connect() # 自动连接当前连接的第一个设备
        print(f"已连接到设备: {d.device_info}")

        # 可选:解锁屏幕 (如果屏幕锁定了)
        # d.unlock() # 需要 ATX 应用支持
        # d.screen_on()

        # 2. 启动微信 (如果应用已在后台,会切换到前台;如果未运行,会启动)
        # 微信的包名通常是 com.tencent.mm
        d.app_start("com.tencent.mm", stop=True) # stop=True 表示如果已运行,先停止再启动,确保是全新状态
        print("微信已启动,等待几秒加载...")
        time.sleep(5) # 等待微信完全加载

        # 3. 点击微信主界面的搜索按钮
        # 通过 weditor 找到搜索按钮的 resourceId 或 description
        # 示例 resourceId: "com.tencent.mm:id/f8y" (此ID会变,请用weditor确认)
        # 或者通过 description: "搜索"
        if d(description="搜索", className="android.widget.RelativeLayout").exists(timeout=10):
            d(description="搜索", className="android.widget.RelativeLayout").click()
        elif d(resourceId="com.tencent.mm:id/lq").exists(timeout=5): # 另一种可能的搜索图标ID
             d(resourceId="com.tencent.mm:id/lq").click()
        else:
            print("错误:未找到微信主界面的搜索按钮。")
            return
        time.sleep(1)

        # 4. 在搜索框输入联系人名称
        # 搜索输入框的 resourceId: "com.tencent.mm:id/bhn" (此ID会变)
        # 或者通过 text: "搜索"
        search_input_field = d(text="搜索", className="android.widget.EditText")
        if not search_input_field.exists(timeout=5):
            # 如果上面那个找不到,可能是上一个点击后直接进入的搜索页面,其ID不同
            search_input_field = d(resourceId="com.tencent.mm:id/khf") # 假设这是搜索页面的输入框
            if not search_input_field.exists(timeout=3):
                print("错误:未找到搜索输入框。")
                return
        
        search_input_field.set_text(contact_name)
        time.sleep(2) # 等待搜索结果出现

        # 点击搜索结果中的联系人
        # 通常联系人显示为 TextView,其 text 属性为 contact_name
        # 注意:如果搜索结果有多个同名,这里会点击第一个匹配的
        # 可能需要更复杂的逻辑来区分,比如通过父容器的某些特征
        contact_item = d(text=contact_name, className="android.widget.TextView")
        # 有时候联系人结果在一个更复杂的列表中,可以尝试更具体的路径
        # 例如:d(resourceId="com.tencent.mm:id/cat").child(text=contact_name)
        if contact_item.exists(timeout=5):
            contact_item.click()
        else:
            print(f"错误:未找到联系人 '{contact_name}' 的搜索结果。")
            # 尝试直接点击第一个搜索结果项(如果结构固定)
            # d(resourceId="com.tencent.mm:id/gbh").click() # 假设这是第一个结果的固定ID
            return
        time.sleep(2) # 等待进入聊天界面

        # 5. 在聊天界面输入消息
        # 输入框的 resourceId: "com.tencent.mm:id/b2t" (此ID会变)
        chat_input_field = d(resourceId="com.tencent.mm:id/auj", className="android.widget.EditText") # 这是一个常见的聊天输入框ID
        if not chat_input_field.exists(timeout=5):
            print("错误:未找到聊天界面的消息输入框。")
            return
        
        chat_input_field.set_text(message)
        time.sleep(1)

        # 6. 点击发送按钮
        # 发送按钮的 resourceId: "com.tencent.mm:id/ay5" (此ID会变)
        # 或者通过 text: "发送"
        send_button = d(text="发送", className="android.widget.Button")
        if not send_button.exists(timeout=5):
            print("错误:未找到发送按钮。")
            return
        send_button.click()
        
        print(f"消息已尝试发送给 '{contact_name}'。")
        time.sleep(1)

        # 可选:返回主屏幕或关闭微信
        # d.press("back") # 按一次返回
        # d.press("home") # 按Home键
        # d.app_stop("com.tencent.mm") # 关闭微信

    except Exception as e:
        print(f"发生错误: {e}")
    finally:
        if 'd' in locals() and d.running():
             print("操作完成,可以考虑关闭应用或断开连接。")
             # d.app_stop("com.tencent.mm") # 停止微信
             # uiautomator2.disconnect() # 断开所有连接,但通常不需要显式调用

if __name__ == '__main__':
    # 确保手机通过USB连接并已授权USB调试
    # 使用 adb devices 查看你的设备序列号,如果只有一个设备,可以填 None
    # my_device_serial = "emulator-5554"  # 替换为你的设备序列号,或 None
    my_device_serial = None # 自动选择

    target_contact_android = "文件传输助手"  # 替换为你要发送的联系人名称
    message_to_send_android = "你好,这是一条来自Python uiautomator2的自动消息!(Android)"

    send_wechat_message_android(my_device_serial, target_contact_android, message_to_send_android)

使用 uiautomation2 的注意事项:

  • 元素ID变化: Android 应用(包括微信)更新后,UI元素的 resourceId 极易发生变化。textdescription (content-desc) 和 className 相对稳定一些,但也不是绝对的。强烈建议使用 weditor 仔细检查并选择最可靠的定位符组合。
  • 多设备: 如果连接了多台设备,需要在 uiautomator2.connect("设备序列号") 中指定。
  • 屏幕状态: 确保屏幕是点亮的,并且设备已解锁。代码中可以加入 d.screen_on()d.unlock() (需要特定服务支持,如ATX)。
  • 隐式等待与显式等待: uiautomator2 的很多操作都有隐式等待 (如 d(...).exists(timeout=...))。但对于UI加载、动画等,仍需使用 time.sleep() 进行显式等待。
  • 权限问题: 确保 atx-agent 在手机上有足够的权限执行操作。
  • 网络状况: 自动化操作依赖于App的正常响应,网络不佳可能导致超时或失败。

三、选择哪个库?uiautomation vs uiautomator2

  • uiautomation

    • 平台: 仅限 Windows。
    • 对象: PC 版微信客户端。
    • 优点: 对于 Windows 桌面应用自动化较为直接,不需要手机或模拟器。
    • 缺点: 依赖 Windows UI Automation API,控件属性可能随微信PC版更新而频繁变动。需要PC保持开机且微信登录。
  • uiautomation2

    • 平台: Android (真机或模拟器)。
    • 对象: Android 版微信 App。
    • 优点: 功能强大,weditor 工具非常好用,社区活跃。可以控制整个 Android 系统。
    • 缺点: 需要配置 ADB 环境,手机/模拟器需要开启 USB 调试。元素ID也可能变化,但通常有更多属性可供选择。

选择建议:

  • 如果你需要在 Windows PC 上自动化操作 PC版微信,选择 uiautomation
  • 如果你需要在 Android 设备 (或模拟器) 上自动化操作 Android版微信,或者需要更广泛的移动端自动化能力,选择 uiautomation2

四、重要提示与最佳实践

  1. UI稳定性是最大的挑战: 微信(或其他任何应用)的UI界面会随着版本更新而改变。这意味着你辛辛苦苦写好的定位元素的规则(如 Name, AutomationId, resourceId)随时可能失效。

    • 应对策略:
      • 尽量使用相对稳定的属性 (如 ClassName 结合层级关系,description 等)。
      • 编写更具弹性的定位逻辑 (例如,如果一个ID找不到,尝试另一个备用ID)。
      • 定期检查和更新你的脚本。
      • 学会使用 Inspect.exe (Windows) 和 weditor (Android) 快速定位新版本的元素。
  2. 适当延时 time.sleep() 自动化脚本执行速度远快于人的操作和应用的响应速度。在点击、输入、页面跳转等操作后,务必加入适当的延时,给应用足够的时间加载和响应,否则很容易因为元素未出现而报错。延时时间需要根据实际情况调整。

  3. 错误处理 try-except UI 自动化非常容易出错(元素找不到、应用崩溃等)。使用 try-except 包裹关键操作,可以使你的脚本更健壮,至少在出错时能优雅地退出或记录日志,而不是直接崩溃。

  4. 清晰的日志: 在脚本的关键步骤打印日志 (例如 “正在搜索联系人XXX…”, “消息输入框已找到”, “发送成功/失败”),便于调试和追踪问题。

  5. 小步快跑,逐步调试: 不要一口气写完所有代码。每完成一小步功能(如打开微信、定位搜索框),就运行测试,确保无误后再继续下一步。

  6. 遵守平台规则,避免滥用:

    • 不要用于发送垃圾信息或进行骚扰。
    • 过于频繁的自动化操作可能会被微信安全机制检测到,导致账号功能受限甚至封号。 请谨慎使用,控制发送频率和内容。
    • 本文仅作技术探讨和学习之用,滥用脚本产生的一切后果由使用者自行承担。

总结

通过 uiautomationuiautomation2 这两个库,我们可以有效地实现PC端和Android端微信消息的自动发送。虽然UI元素定位是这类自动化脚本中最具挑战性的部分,但掌握了元素查看工具和基本的定位方法后,你就能应对大部分场景。

记住,自动化脚本的维护是一个持续的过程。随着微信版本的迭代,你可能需要定期调整你的脚本。希望本文能为你打开微信自动化的大门,探索更多有趣和实用的应用场景!


希望这篇博客文章对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值