Python+uiautomation 查找微信僵尸粉

  • Post author:
  • Post category:python


最近某位好友问我有没有一种工具,可以不打扰好友且绿色、安全、无毒检测出微信的僵尸粉。精心搜索了些文章学习了下,结果很多看不懂……

直到看到一位大佬的文章,使用Python+uiautomation自动获取好友列表。正巧最近看了点Python自动化的资料,这篇文章中代码的逻辑能看懂一半。由于很多年没有写过代码(本来也没写过几行代码),更没有写过Python代码。直接大招,Ctrl+c  Ctrl+v,右键运行(具体安装IDE和下载uiautomation的类库不描述,网上教程很多),神奇的事情发生了,可以直接运行。瞬间打开了思路,有精神了。

大佬文章的链接:

https://blog.csdn.net/weixin_45081575/article/details/126806657

查了些资料,有三种方式识别僵尸粉:

  1. 给好友发信息,拒收信息或提示发送失败,则为僵尸粉
  2. 新建群聊,提示未能创建群聊,或者提示需要添加好友后才能入群,则为僵尸粉
  3. 通过转账,提示非好友关系,则为僵尸粉

通过群发消息,必然各种骚扰好友,不得行!通过转账(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('检测结束!')





仅学习娱乐,请勿对好友造成骚扰。



版权声明:本文为m0_58111479原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。