不靠 Hook、不装插件,纯 PC 端 UI 自动化模拟真人操作,一条 Python 脚本搞定微信批量群发。
加我微信:llike620
一、痛点:个人微信批量发送为什么难?
做私域运营、客户通知、活动推广时,经常需要给多个微信好友批量发消息。官方途径无非几种:
| 方案 | 问题 |
|---|---|
| 企业微信 API | 需要企业认证,个人号无法使用 |
| 微信 PC Hook | 注入版不稳定,版本一更新就挂,封号风险高 |
| 按键精灵类工具 | 缺乏智能判断,遇到弹窗就卡死 |
| 手动一个个发 | 几十上百个好友,手都点麻了 |
有没有一种方案,不侵入微信进程、不修改客户端,又能稳定批量发送?
答案就是:UI 自动化——用 Python 驱动 PC 微信,完全模拟真人的鼠标点击和键盘输入。微信看到的就是”一个人在操作”,封号风险降到最低。
二、核心原理:Windows UI Automation

整个链路不经过任何微信内部接口,全部走 Windows 系统级的 UI 自动化协议,和真人操作屏幕的效果完全一致。
三、核心函数剖析:send_message
这是整条脚本的灵魂,完整模拟了一个人给好友发消息的全部动作。
3.1 第一步:激活微信窗口
try:
uiaAPI = uia.WindowControl(Name="微信")
uiaAPI.SwitchToThisWindow()
uiaAPI.MoveToCenter()
except Exception as e:
pyautogui.hotkey('ctrl', 'alt', 'w')
uiaAPI = uia.WindowControl(Name="微信")
uiaAPI.SwitchToThisWindow()
uiaAPI.MoveToCenter()
这里有两层策略:
- 正常路径:直接通过窗口标题”微信”获取控制句柄,切到前台、居中
- 兜底路径:如果微信被最小化或句柄失效,按
Ctrl+Alt+W快捷键强制呼出
注意:
Ctrl+Alt+W不是微信自带快捷键,需要配合其他工具(如 AutoHotkey)设置,或将微信固定到任务栏第一位后用Win+1替代。
3.2 第二步:搜索联系人
uiaAPI.SendKeys('{Ctrl}f', waitTime=0)
searchBox = uiaAPI.EditControl(Name='搜索')
pyperclip.copy(nickname)
searchBox.SendKeys('{Ctrl}a', waitTime=0)
searchBox.SendKeys('{Ctrl}v', waitTime=0)
time.sleep(1)
三个关键设计:
- 用剪贴板而不是逐字输入——
pyperclip.copy()复制昵称,Ctrl+V粘贴,避免中文输入法干扰和特殊字符问题 - 先
Ctrl+A全选再粘贴——覆盖搜索框中可能残留的上次搜索内容 time.sleep(1)等待搜索结果加载——模拟真人等待列表刷新的停顿
3.3 第三步:点击联系人 + 异常清理
listItem = uiaAPI.ListItemControl(Name=nickname)
if listItem.Exists():
listItem.Click()
time.sleep(1)
# 如果昵称搜不到,微信会弹出一个提示窗,这里关掉它
extraWnd = uia.PaneControl(ClassName="Chrome_WidgetWin_0", Name="微信")
if extraWnd.Exists():
extraWnd.SendKeys('{Alt}{F4}')
这里有一个巧妙的设计:如果联系人不存在(拼写错误、已删除好友等),微信会弹出”未找到”的浮窗。脚本检测到这个异常窗口后,用 Alt+F4 关掉,避免阻碍下一条消息。
3.4 第四步:定位输入框 + 发送
inputBox = uiaAPI.EditControl(Name=nickname)
if not inputBox.Exists():
print(f"未找到 {nickname} 输入框")
return
inputBox.Click()
pyperclip.copy(content)
inputBox.SendKeys('{Ctrl}a', waitTime=1)
inputBox.SendKeys('{Ctrl}v', waitTime=1)
inputBox.SendKeys('{Enter}', waitTime=0)
微信聊天输入框的 Name 属性恰好是当前聊天对象的昵称,所以通过 EditControl(Name=nickname) 就能精确定位。
发送流程再次全用剪贴板:Ctrl+A 清空 → Ctrl+V 粘贴 → Enter 发送。
3.5 第五步:文件发送(特殊场景)
if os.path.isfile(content):
window.ButtonControl(Name="发送文件").Click()
uploadBtn = window.EditControl(Name="文件名(N):")
uploadBtn.SendKeys('{Ctrl}v', waitTime=0)
time.sleep(2)
uploadBtn.SendKeys('{Enter}', waitTime=0)
dragAttachWnd = uia.WindowControl(ClassName='DragAttachWnd')
time.sleep(1)
edit.SendKeys('{Enter}', waitTime=0)
当 content 是一个本地文件路径时,走文件发送流程:
- 点击”发送文件”按钮
- 在弹出的文件选择对话框中粘贴路径
- 等待文件加载(
sleep(2)) - 确认发送
四、完整时序:模拟真人的”思考停顿”
把以上步骤串联起来,每条消息的完整时间线是这样的:Syntax error in textmermaid version 11.4.1
总耗时约 4~6 秒/条,这个节奏和真人打字、搜索、发送的速度非常接近,大幅降低被风控的概率。
五、完整主循环
since_id = 0
while True:
try:
resp = requests.get(f"{BASE_URL}/watchLiveComments",
params={"since_id": since_id}, timeout=10)
data = resp.json()
if data.get("status") == "ok":
comments = data.get("data", [])
since_id = data.get("latest_id")
for c in comments:
nickname = c.get('nickname')
content = c.get('content')
send_message(nickname, content)
except Exception as e:
print(f"Error: {e}")
time.sleep(2)
设计要点:
- 游标模式:用
since_id记录最后处理的 ID,每次只拉增量消息,不重复发送 - 异常健壮:任何一条失败不影响后续,
try/except包裹整个循环 - 2 秒轮询间隔:平衡实时性和服务器压力
六、技术选型一览
| 组件 | 选型 | 角色 |
|---|---|---|
| UI 自动化引擎 | uiautomation | 操控微信窗口和控件 |
| 剪贴板 | pyperclip | 中文内容粘贴(避免输入法问题) |
| 快捷键 | pyautogui | Ctrl+Alt+W 呼出微信 |
| 消息源 | SCRM 后端 watchLiveComments API | 增量获取待发送消息 |
| 运行环境 | Windows + Python 3.8+ | PC 微信必须在 Windows 运行 |
七、避坑指南
- 微信窗口不要最小化到托盘——最小化时 UI 树可能不可见,建议保持窗口打开状态
- 昵称必须精确匹配——包括 emoji、空格、特殊符号,差一个字符都搜不到
- 发送期间不要操作鼠标键盘——UI 自动化占用焦点,人为干扰会导致发送失败
- 频率控制:每条消息约 5 秒,批量发送时不要调太快
- 屏幕不要锁屏/息屏——锁屏后 UI Automation 无法定位控件
八、部署运行
# 1. 安装依赖
cd python
pip install -r requirements.txt
# 2. 确保 SCRM 后端已启动
# http://127.0.0.1:8089
# 3. 运行群发脚本
python batch_send_wechat.py
或者直接用配套的 start_watch_monitor.bat 双击启动。
九、总结
这套方案的核心思想很简单:用代码模拟人的手。
不做注入、不改客户端、不碰微信内存,就老老实实操控 UI 控件,该点就点、该等就等。虽然速度比不上 Hook 方案,但胜在稳定、安全、不封号。对于日发几百条的私域运营场景来说,完全够用。
完整的项目还包括 SCRM 管理后台、企业微信 Hook 版自动回复、扣子知识库接入等模块,感兴趣可以翻翻前面的文章。