❤️UI自动化轻松解决微信手工群发消息的烦恼❤️
source link: https://blog.csdn.net/as604049322/article/details/119941984
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
大家好,我是小小明。
来自杨秀璋老师的灵魂拷问:弱弱问句,这个库爬取数据,微信每日定时发送新闻也可以的吧?
杨老师表示之前用Itchat自动群发消息,但是现在网页版微信不能登录了,需要找个替代品,不然手工发送太浪费时间。
不知道有没有还不认识杨老师的,这是他的CSDN链接:https://blog.csdn.net/Eastmount
对于这个问题,UI自动化测试工具uiautomation,表示实在是太适合啦。
对uiautomation相关基础还不了解的童鞋,请查看下面这两篇文章:
《️❤️对比PyWinAuto和uiautomation实现微信联系人自动采集❤️》
注意:本文所测试的PC微信版本是当前最新的3.3.5.42:
今天我们的目标是自动群发消息,即批量向多个会话发送消,向单个会话发消息已经在第一篇文章中演示过。
1️⃣:向前N个置顶会话发送消息
查看对应UI对象:
搜索并获取微信窗口的控制器,激活窗口后,获取会话列表对象:
import uiautomation as auto
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
sessionListControl = wechatWindow.ListControl(Name='会话')
我们先实现对前N个置顶的会话批量发送消息。这种非常适合每天都需要向固定几个群批量发消息的需求。
💎确保会话列表已经到开头
在发消息前,需要先保证会话列表已经移动到顶部,避免发错了对象:
wechatWindow.SetActive()
session = sessionListControl.GetFirstChildControl()
last = session.Name
session.GetNextSiblingControl().Click()
while 1:
sessionListControl.SendKeys("{UP 10}")
session = sessionListControl.GetFirstChildControl()
if last == session.Name:
break
last = session.Name
上述代码表示先单击第二个可见会话(第一个可见会话可能点击不到),然后按10次向上方向键之后比较当前可视列表的首个元素名称是否不再发生变化,发生变化则再按10次向上方向键,直到名称不再发生变化为止。
看看效果:
💎打字机输入
假设我们的爬虫爬取到的新闻文本内容如下:
news = """8月25日
中国石油大庆油田有限责任公司宣布
大庆油田古龙页岩油勘探
取得重大战略性突破
新增石油预测地质储量12.68亿吨
标志着我国页岩油勘探开发
取得重大突破
----来自于UI自动化程序测试(请忽略)
"""
经测试,直接发送以上文本无法进行换行,于是编写以下方法将所有的换行符替换成回车按键标记:
def chars2keys(chars):
# 将所有的换行符替换为回车键
return chars.replace("\n", "{ENTER}")
chars2keys(news)
'8月25日{ENTER}中国石油大庆油田有限责任公司宣布{ENTER}大庆油田古龙页岩油勘探{ENTER}取得重大战略性突破{ENTER}新增石油预测地质储量12.68亿吨{ENTER}标志着我国页岩油勘探开发{ENTER}取得重大突破{ENTER}{ENTER}----来自于UI自动化程序测试(请忽略){ENTER}'
测试输入:
edit = wechatWindow.EditControl(Name='输入')
edit.SendKeys(chars2keys(news))
可以看到已经顺利的可以输入换行符。
关于特殊按键可以参考:https://docs.microsoft.com/zh-cn/previous-versions/dotnet/netframework-1.1/k3w7761b(v=vs.80)
测试过程为了减少对被测用户的打扰,我加入发完测试消息自动撤回的功能。
💎发送并撤回
发送并撤回的实现代码:
# 发送
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
sendButton.Click()
# 撤回消息
messages = wechatWindow.ListControl(Name='消息')
message = messages.GetLastChildControl()
message.Click()
message.RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "撤回":
menu_item.Click()
这时我们就可以向N个置顶会话批量挨个发送消息了。
💎批量发送
批量发送的的完整代码(含撤回,正式使用时可以注释掉):
edit = wechatWindow.EditControl(Name='输入')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
messages = wechatWindow.ListControl(Name='消息')
n = 5
childs = sessionListControl.GetChildren()[:-1]
print("当前视图最大可发送会话个数为", len(childs))
sessions = childs[:n]
for session in sessions:
print(session.Name)
session.Click()
edit.SendKeys(chars2keys(news))
# 发送
sendButton.Click()
# 撤回消息
message = messages.GetLastChildControl()
# message.Click()
message.RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "撤回":
menu_item.Click()
目前最大只支持向可视范围内的会话发送消息,未实现翻页。能向多少个置顶会话发送消息,取决于你的屏幕大小。
测试向前5个置顶会话发送消息:
💎完整对置顶会话发信息实现代码
上面的发送方式有点像打字机的效果,但实际上我们采用复制粘贴的方式会快很多。下面我们整理一下,最终完整代码如下:
import uiautomation as auto
def sessionList2Top():
session = sessionListControl.GetFirstChildControl()
last = session.Name
session.GetNextSiblingControl().Click()
while 1:
sessionListControl.SendKeys("{UP 10}")
session = sessionListControl.GetFirstChildControl()
if last == session.Name:
break
last = session.Name
def recall_lastmessage():
message = messages.GetLastChildControl()
message.Click()
message.RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "撤回":
menu_item.Click()
news = """8月25日
中国石油大庆油田有限责任公司宣布
大庆油田古龙页岩油勘探
取得重大战略性突破
新增石油预测地质储量12.68亿吨
标志着我国页岩油勘探开发
取得重大突破
----来自自动化测试程序,如有打扰请见谅~
"""
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
sessionListControl = wechatWindow.ListControl(Name='会话')
sessionList2Top()
edit = wechatWindow.EditControl(Name='输入')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
messages = wechatWindow.ListControl(Name='消息')
n = 5
childs = sessionListControl.GetChildren()[:-1]
print("当前视图最大可发送会话个数为", len(childs))
sessions = childs[:n]
for session in sessions:
print(session.Name)
session.Click()
# 发送消息
auto.SetClipboardText(news)
edit.SendKeys('{Ctrl}v')
sendButton.Click()
# 撤回消息
# recall_lastmessage()
最终测试一下,已移除重复元素:
2️⃣:基于搜索向指定用户/群发送消息
实际使用中,发现上述处理逻辑存在较大的BUG,如果在对置顶群挨个发消息的过程,某个群有其他人发消息,就会导致会话列表顺序发生变化,从而出现漏发或错发。
针对这种情况,我们比较精准的发消息的方式是基于搜索进行发送。
首先分别获取搜索框、输入框、消息列表和发送按钮4个UI组件:
import uiautomation as auto
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
search = wechatWindow.EditControl(Name='搜索')
edit = wechatWindow.EditControl(Name='输入')
messages = wechatWindow.ListControl(Name='消息')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
然后编写以下方法:
import time
def send2name(name, txt, wait_time=0.1):
search.Click()
auto.SetClipboardText(name)
edit.SendKeys('{Ctrl}v')
# 等待微信索引搜索跟上
time.sleep(wait_time)
search.SendKeys("{Enter}")
auto.SetClipboardText(txt)
edit.SendKeys('{Ctrl}v')
sendButton.Click()
测试一下:
name = "小小明"
txt = "小小明你好,收到这条消息说明你的程序已经成功---来自自动化测试程序"
send2name(name, txt)
可以看到能够精准的发送指定用户:
再搜索指定群名试一下(加入自动撤回):
def recall_lastmessage():
message = messages.GetLastChildControl()
message.Click()
message.RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "撤回":
menu_item.Click()
name = "三人行"
txt = f"{name}收到这条消息,说明搜索发送无误"
send2name(name, txt)
recall_lastmessage()
有了上述方法,想给任何人发消息都可以精准都搜索发送了。
需要批量发送,只需要指定名称列表循环处理即可。
3️⃣:转发当前窗口消息到指定用户
大多数时候我们都是要发送完全相同的消息到多个群,使用微信的转发功能会更快,可以先将消息发送给文件传输助手后,然后再进行转发,下面演示如何实现转发。
先获取微信的四大组件:
import uiautomation as auto
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
search = wechatWindow.EditControl(Name='搜索')
edit = wechatWindow.EditControl(Name='输入')
messages = wechatWindow.ListControl(Name='消息')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
然后选中当前界面的最后一条消息打开转发界面:
message = messages.GetLastChildControl()
message.RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "转发":
menu_item.Click()
break
获取转发窗口中几个重要的UI组件:
send2ps = wechatWindow.WindowControl(Name="转发给", ClassName='SelectContactWnd')
receiver = send2ps.EditControl(Name="搜索")
sendB2 = send2ps.ButtonControl(Name='发送')
items = send2ps.ListControl()
下面随便选了一些需要转发的好友或群:
import time
users = ["文件传输助手", "小小明", "云朵君", "道财", "黄同学", "三人行"]
for user in users:
receiver.Click()
auto.SetClipboardText(user)
receiver.SendKeys('{Ctrl}v')
# 等待UI组件的渲染变化
time.sleep(0.1)
items.ButtonControl().Click()
receiver.GetParentControl().GetNextSiblingControl().Click()
测试一下:
接下来只需要调用一下以下代码就完成发送了:
sendB2.Click()
上述代码还未考虑微信一次最大只支持对9个用户转发的限制,那么我们可以做个分组后,多次转发。最终完整代码:
import time
import uiautomation as auto
wechatWindow = auto.WindowControl(
searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
wechatWindow.SetActive()
search = wechatWindow.EditControl(Name='搜索')
edit = wechatWindow.EditControl(Name='输入')
messages = wechatWindow.ListControl(Name='消息')
sendButton = wechatWindow.ButtonControl(Name='发送(S)')
message = messages.GetLastChildControl()
while message.Name == '':
message = message.GetPreviousSiblingControl()
message.EditControl().RightClick()
menu = wechatWindow.MenuControl()
menu_items = menu.GetLastChildControl().GetFirstChildControl().GetChildren()
for menu_item in menu_items:
if menu_item.ControlTypeName != "MenuItemControl":
continue
if menu_item.Name == "转发":
menu_item.Click()
break
send2ps = wechatWindow.WindowControl(Name="转发给", ClassName='SelectContactWnd')
receiver = send2ps.EditControl(Name="搜索")
sendB2 = send2ps.ButtonControl(Name='发送')
items = send2ps.ListControl()
def relay2users(users, max_n=9):
for i in range(0, len(users), max_n):
users_split = users[i:i+max_n]
for user in users_split:
receiver.Click()
auto.SetClipboardText(user)
receiver.SendKeys('{Ctrl}v')
# 等待UI组件的渲染变化
time.sleep(0.1)
items.ButtonControl().Click()
receiver.GetParentControl().GetNextSiblingControl().Click()
sendB2.Click()
users = ["文件传输助手", "小小明", "云朵君", "道财", "黄同学", "三人行"]
relay2users(users)
另一种更快的操作方案就是先用前一节的方法将新闻发送到文件传输助手,然后将当前界面的消息一口气转发到指定的列表中。
Recommend
-
410
事情的起源是这样的:前几天GF想用微信群发广告(囧),问我能不能在群发内容上自动添加该好友的备注名,或者是备注名的第一个字符(一般就是姓)+总、师呀什么的。网上搜了一圈,答案是没有。于是萌生自己撸一个的想法。无奈开发方向是web前端,不能撸什么Android...
-
34
-
0
V2EX › 程序员 自动化测试的数据都是手工维护的吗? chigeyaowaner · 1 小时 51...
-
4
使用企业微信群发消息,可以让我们企业的活动信息和产品信息同时触达多位客户,那么企业微信群发消息给客户具体是如何操作的呢?一次可以群发多少客户? 企业微信怎么群发消息给客户 有两种方式群发消息给客户
-
7
企业在进行营销宣传或者节假日来临时,可以给客户群群发一些消息,来帮助自己营销客户或者传达节日祝福。那么在企业微信中,企业应该如果群发消息到客户群呢?发送后可以查看群发的进度吗? 企业创建群发任务
-
6
在使用企业微信做社群运营,经常会面临一个员工管理多个群聊的情况,当有群消息通知时,为了更便捷不遗漏消息,企业微信是否可以使用群群发功能呢?企业微信单次可以给多少个客户群进行消息群发?
-
6
在给客户群发送通知、祝福、活动信息时,相比于一个一个的手动发送,统一群发会更加节省时间,提升工作效率。尤其是以企业名义的统一群发,不仅可以增加客户的信服力,还可以对群发的内容进行统一管理。
-
7
将祝福,通知以及活动的信息发送给客户,可以有效的维系与客户之间的关系,还可以让客户提前了解我们企业的活动信息以及产品信息,我们都知道企业微信可以选择不同的客户,然后群发消息,那么企业微信该怎么给客户群群发消息呢?企业微信客户群群发...
-
5
企业微信的社群可以直接添加微信用户,所以越来越多企业选择企业微信做社群运营,做群运营时一个员工往往要同时接管多个群聊,那么当有消息需要给手头多个群聊同时推送消息时,可以使用企业微信群群发吗...
-
6
为了能更好地服务客户,企业员工经常需要给客户发送通知、祝福、活动等消息,一个一个发送效率太低,员工通常会选择批量群发,那么企业微信能不能群发消息呢?企业微信群发功能在哪? 企业微信如何群发消...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK