最近某位好友问我有没有一种工具,可以不打扰好友且绿色、安全、无毒检测出微信的僵尸粉。精心搜索了些文章学习了下,结果很多看不懂……
直到看到一位大佬的文章,使用Python+uiautomation自动获取好友列表。正巧最近看了点Python自动化的资料,这篇文章中代码的逻辑能看懂一半。由于很多年没有写过代码(本来也没写过几行代码),更没有写过Python代码。直接大招,Ctrl+c Ctrl+v,右键运行(具体安装IDE和下载uiautomation的类库不描述,网上教程很多),神奇的事情发生了,可以直接运行。瞬间打开了思路,有精神了。
大佬文章的链接:
https://blog.csdn.net/weixin_45081575/article/details/126806657
查了些资料,有三种方式识别僵尸粉:
- 给好友发信息,拒收信息或提示发送失败,则为僵尸粉
- 新建群聊,提示未能创建群聊,或者提示需要添加好友后才能入群,则为僵尸粉
- 通过转账,提示非好友关系,则为僵尸粉
通过群发消息,必然各种骚扰好友,不得行!通过转账(python+appium感觉略微麻烦,关键是没有看懂多少)。那么,就用python自动模拟人的操作,在PC端的微信一步两步去建群检测吧。
注意:建群次数有限制!建群次数有限制!建群次数有限制!且群聊人数不要超过30人,否则会给好友发送群聊邀请。
可是代码该咋个写呢?没写过。百度!
引入需要的库,获取微信窗口句柄,使用快捷键激活微信为当前活动窗口,程序开始接管微信窗口,进行自动操作微信菜单
import uiautomation as auto
import time
import re
import os
wechatWindow = auto.WindowControl(searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
auto.SendKeys(text='{Alt}{Ctrl}w')
首先,获取通讯录中全部好友列表,直接上copy大佬的代码(模拟操作滑动块的参数没有算明白,经过测试可以直接用)
def get_friends_list(num: int=10) -> list:
#auto.SendKeys(text='{Alt}{Ctrl}w')
wechatWindow.ButtonControl(Name="通讯录").Click()
wechatWindow.ListControl(Name="联系人").ButtonControl(Name="通讯录管理").Click()
contacts_window = auto.GetForegroundControl()
scroll = contacts_window.ListControl().GetScrollPattern()
assert scroll, "没有可滑动对象"
name_list = list()
rate: int = int(float(102000 / num)) #不太懂计算逻辑,有知道的大佬可以帮忙指点下
for pct in range(0, 102000, rate):
scroll.SetScrollPercent(horizontalPercent=-1, verticalPercent=pct /100000)
for name_node in contacts_window.ListControl().GetChildren():
nick_name = name_node.TextControl().Name
remark_name = name_node.ButtonControl(foundIndex=2).Name
name_list.append(remark_name if remark_name else nick_name)
contacts_window.SendKey(auto.SpecialKeyNames['ESC'])
return list(set(name_list))
num为翻页参数,每页一般为10个好友(跟打开微信窗口的大小有关),根据好友数有多少除以10。
例如自己又1099个好友,可以设置为110
contacts = get_friends_list(num=110)
print("当前所有好友数:", len(contacts))
然后定义一个自动搜索好友的函数
def searchContacts(fans: str = None):
search = addgroup_window.EditControl(Name="搜索")
search.Click()
searchContent = wechatWindow.EditControl().GetValuePattern().Value
if searchContent: #搜索栏中有内容,则清空下再模拟输入
search.GetParentControl().GetChildren()[1].SendKeys('{Ctrl}a')
search.GetParentControl().GetChildren()[1].SendKeys('{Delete}')
search.GetParentControl().GetChildren()[1].SendKeys(fans)
time.sleep(0.5) #避免结果未搜索出来,已经开始下一步操作
searchResult = addgroup_window.ListControl(Name="请勾选需要添加的联系人").GetChildren()
if searchResult:
for sear in searchResult:
if sear == fans: #有些好友微信名很简单,可能会模糊匹配搜索出一堆其他好友,需要过滤
sear.ButtonControl().Click()
print("当前检测的联系人是:", fans)
return 'ok'
else:
print("此联系人名字含有特殊符号:", fans)
with open("conf/checkFailedNickName.txt", "a", encoding="utf-8") as recordmsg:
recordmsg.write(format(fans) + "\n") #SendKeys如何模拟输入特殊的表情字符暂时不清楚,先记录下来
recordmsg.close()
return 'not ok'
最后,开始循环全部好友列表,进行拉人建群聊,提取微信窗口的信息。
刚开始不清楚微信规则,边写代码边测试(只创建三个人的群聊,中间固定使用一个工具人),创建群聊频率过快,次数过多,导致PC端微信被强制下线和不能创建群聊限制,解除限制时间间隔不清楚。有文章说是每天创建群聊次数上限为100次(没有记录自己创建过多少次被限制,有兴趣的小伙伴可以测试下,奸笑)。
于是将自己毛坯版的代码进行雕琢了下,每天只创建70个群聊,一次只创建3个人的群聊(找了几个好友测试后,未出现异常)。可有1000+的好友数待检测,这效率……因此有了进阶版的念头,创建30人以下的群聊,测试时发现两个问题:一个是30人以下的群聊不发送邀请的说法不准,有创建23个人群聊的时候,给某个好友发了个邀请(原因未知);另一个是创建两个群之后,就显示创建群聊频率过快而被限制了(不知道是不是自己的微信之前有被限制记录导致的)。进阶版的计划暂停(脑壳疼,不琢磨了!希望有清楚微信规则的大佬能指点下)。
有些粗糙的雕琢版完整代码:
import uiautomation as auto
import time
import re
import os
wechatWindow = auto.WindowControl(searchDepth=1, Name="微信", ClassName='WeChatMainWndForPC')
auto.SendKeys(text='{Alt}{Ctrl}w')
def get_friends_list(num: int=10) -> list:
#auto.SendKeys(text='{Alt}{Ctrl}w')
wechatWindow.ButtonControl(Name="通讯录").Click()
wechatWindow.ListControl(Name="联系人").ButtonControl(Name="通讯录管理").Click()
contacts_window = auto.GetForegroundControl()
scroll = contacts_window.ListControl().GetScrollPattern()
assert scroll, "没有可滑动对象"
name_list = list()
rate: int = int(float(102000 / num))
for pct in range(0, 102000, rate):
scroll.SetScrollPercent(horizontalPercent=-1, verticalPercent=pct /100000)
for name_node in contacts_window.ListControl().GetChildren():
nick_name = name_node.TextControl().Name
remark_name = name_node.ButtonControl(foundIndex=2).Name
name_list.append(remark_name if remark_name else nick_name)
contacts_window.SendKey(auto.SpecialKeyNames['ESC'])
return list(set(name_list))
needCheckContacts = list()
allContacts = list()
checkedContacts = list()
if os.path.getsize("conf/allContacts.txt") == 0:
print("当前为第一次使用工具。开始获取好友列表,并初始化所有联系人文件!")
contacts = get_friends_list(num=110)
conInFile = open("conf/allContacts.txt", "w", encoding="utf-8")
wrapStr = '\n'
conInFile.write(wrapStr.join(contacts))
conInFile.close()
needCheckContacts = contacts
print("当前所有好友数:", len(contacts))
else:
print("所有联系人文件已初始化完成,开始获取待检测联系人列表!")
conOutFile = open("conf/allContacts.txt", "r", encoding="utf-8")
for line in conOutFile.readlines():
allContacts.append(line)
if os.path.getsize('conf/checkedContacts.txt') == 0:
needCheckContacts = allContacts
else:
conOutFile = open("conf/checkedContacts.txt", "r", encoding="utf-8")
for line in conOutFile.readlines():
checkedContacts.append(line)
for allContact in allContacts:
for checkedContact in checkedContacts:
if allContact == checkedContact:
continue
else:
needCheckContacts.append(allContact)
print("获取待检测联系人完成,当前未检测联系人还有:", len(needCheckContacts))
def searchContacts(fans: str = None):
search = addgroup_window.EditControl(Name="搜索")
search.Click()
searchContent = wechatWindow.EditControl().GetValuePattern().Value
if searchContent:
search.GetParentControl().GetChildren()[1].SendKeys('{Ctrl}a')
search.GetParentControl().GetChildren()[1].SendKeys('{Delete}')
search.GetParentControl().GetChildren()[1].SendKeys(fans)
time.sleep(0.5)
searchResult = addgroup_window.ListControl(Name="请勾选需要添加的联系人").GetChildren()
if searchResult:
for sear in searchResult:
if sear == fans: #有些好友微信名很简单,可能会搜索一堆其他好友,需要过滤
sear.ButtonControl().Click()
print("当前检测的联系人是:", fans)
return 'ok'
else:
print("此联系人名字含有特殊符号:", fans)
with open("conf/checkFailedNickName.txt", "a", encoding="utf-8") as recordmsg:
recordmsg.write(format(fans) + "\n")
recordmsg.close()
return 'not ok'
time.sleep(0.5) #防止电脑反应慢,代码运行过快
flagnum = 0
for contact in needCheckContacts:
if flagnum == 70:
break
if contact == '工具人好友的昵称':
continue
wechatWindow.ButtonControl(Name="聊天").Click()
wechatWindow.ButtonControl(Name="发起群聊").Click()
addgroup_window = auto.GetForegroundControl()
searchContacts('工具人好友的昵称或微信ID')
rst = searchContacts(contact)
if rst == 'ok':
addgroup_window.ButtonControl(Name='确定').Click()
else:
addgroup_window.SendKey(auto.SpecialKeyNames['ESC'])
continue
time.sleep(2)
#僵尸粉分三种:拉黑你的好友,删除你的好友,被封号限制登录的好友,每种僵尸粉进群聊异常提示不同
zombieName = auto.GetForegroundControl().EditControl().Name
if zombieName != '搜索':
search = re.search(r'创建群聊,.*拒绝加入群聊', zombieName)
fnsName = search.group(0).replace("创建群聊,", "").replace("拒绝加入群聊,", "")
print("找到一个僵尸粉:", fnsName)
with open("conf/zombieContacts.txt", "a", encoding="utf-8") as recordmsg:
recordmsg.write(format(fnsName) + "\n")
recordmsg.close()
auto.GetForegroundControl().ButtonControl(Name='确定').Click()
auto.GetForegroundControl().ButtonControl(Name='关闭').Click()
elif zombieName == '搜索':
if len(wechatWindow.ListControl(Name="消息").GetChildren()) == 3:
print("找到一个僵尸粉:", wechatWindow.ListControl(Name="消息").GetChildren()[2].Name)
with open("conf/zombieContacts.txt", "a", encoding="utf-8") as recordmsg:
recordmsg.write(format(wechatWindow.ListControl(Name="消息").GetChildren()[2].Name) + "\n")
recordmsg.close()
wechatWindow.ButtonControl(Name="聊天信息").Click()
scroll = wechatWindow.ListControl(Name="聊天成员").GetParentControl().GetScrollPattern()
assert scroll, "没有可滑动对象"
for pct in range(0, 102000, 51000):
scroll.SetScrollPercent(horizontalPercent=-1, verticalPercent=pct / 1000)
wechatWindow.ButtonControl(Name="删除并退出").Click()
auto.GetForegroundControl().ButtonControl(Name='退出并删除').Click()
with open("conf/checkedContacts.txt", "a", encoding="utf-8") as recordmsg:
recordmsg.write(format(contact) + "\n")
recordmsg.close()
flagnum = flagnum + 1
time.sleep(0.5)
print('检测结束!')
仅学习娱乐,请勿对好友造成骚扰。