欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

黑科技,Python 脚本帮你找出微信上删除你好友的人

程序员文章站 2022-07-09 21:19:47
本文来自稀土掘金江昪编译自 Github:0x5e/wechat-deleted-friends “ 清理下[微笑],不用回。你的朋友圈没事也该清清了,打开设置,通用,功能,群助手,全选,把我的信息粘贴一下,就可以了,发送就知道谁把你删了,方便你清人,不清不知道 ,一清吓一跳。” 相信大家在微信上一 ......

本文来自稀土掘金江昪编译自 github:0x5e/wechat-deleted-friends 

“ 清理下[微笑],不用回。你的朋友圈没事也该清清了,打开设置,通用,功能,群助手,全选,把我的信息粘贴一下,就可以了,发送就知道谁把你删了,方便你清人,不清不知道 ,一清吓一跳。”

相信大家在微信上一定被上面的这段话刷过屏,群发消息应该算是微信上流传最广的找到删除好友的方法了。但群发消息不仅仅会把通讯录里面所有的好友骚扰一遍,而且你还得挨个删除好几百个聊天记录,回复大家的疑问和鄙视。作为一个互联网从业者,除了群发消息就不能有更高效、不打扰好友的方式么?

答案是当然有,本人的微:1613161916可以一起讨论python,还有许多python资料可以发送。

微信在拉好友进群聊的时候,如果这个人删除了你好友的话,会提示你一下「请先发送朋友验证申请给某某,对方将你加为微信朋友后,你才能邀请其加入群聊。」有办法了,那我把微信好友拉一个大群里面,然后默默的删掉微信群不就好了么。

于是 github 上就有一位叫 0x5e 的开发者写了这么一个 python 脚本来代替你手动拉群和踢人。经过笔者测试,目前只能支持 mac 的操作系统。在此之前,笔者在稀土掘金上先放出了github的方法,这里再详细提下。

在 0x5e 的 github 代码仓库描述里面,他具体的实现步骤是用 python 脚本处理网页版微信的拉群、踢人操作。具体的步骤如下:

 

1、下载代码文件 wdf.py 

2、打开 terminal 输入:python 然后拖动刚才下载的 wdf.py 到 terminal 窗后回车。命令格式类似: python wdf.py

3、接下来按步骤扫码操作即可;

 

查询结果可能会引起一些心理上的不适,请小心使用。

 

笔者测试了两次,这个 python 脚本还是有一些小问题,欢迎开发者们给原作者发 pull request 完善:

 

1、两次结果稍有出入,可能是微信网页版返回数据的问题

2、最终在微信会遗留一个只有自己的群组,需要手动删除

3、此脚本暂时不支持查找被拉黑的情况

 

源码

#!/usr/bin/env python
# coding=utf-8
#本人威信:1613161916
from __future__ import print_function import os import requests import re import time import xml.dom.minidom import json import sys import math import subprocess import ssl import threading debug = false max_group_num = 2 # 每组人数 interface_calling_interval = 5 # 接口调用时间间隔, 间隔太短容易出现"操作太频繁", 会被限制操作半小时左右 max_progress_len = 50 qrimagepath = os.path.join(os.getcwd(), 'qrcode.jpg') tip = 0 uuid = '' base_uri = '' redirect_uri = '' push_uri = '' skey = '' wxsid = '' wxuin = '' pass_ticket = '' deviceid = 'e000000000000000' baserequest = {} contactlist = [] my = [] synckey = [] try: xrange range = xrange except: # python 3 pass def responsestate(func, baseresponse): errmsg = baseresponse['errmsg'] ret = baseresponse['ret'] if debug or ret != 0: print('func: %s, ret: %d, errmsg: %s' % (func, ret, errmsg)) if ret != 0: return false return true def getuuid(): global uuid url = 'https://login.weixin.qq.com/jslogin' params = { 'appid': 'wx782c26e4c19acffb', 'fun': 'new', 'lang': 'zh_cn', '_': int(time.time()), } r= myrequests.get(url=url, params=params) r.encoding = 'utf-8' data = r.text # print(data) # window.qrlogin.code = 200; window.qrlogin.uuid = "ozwt_bffrg=="; regx = r'window.qrlogin.code = (\d+); window.qrlogin.uuid = "(\s+?)"' pm = re.search(regx, data) code = pm.group(1) uuid = pm.group(2) if code == '200': return true return false def showqrimage(): global tip url = 'https://login.weixin.qq.com/qrcode/' + uuid params = { 't': 'webwx', '_': int(time.time()), } r = myrequests.get(url=url, params=params) tip = 1 f = open(qrimagepath, 'wb') f.write(r.content) f.close() time.sleep(1) if sys.platform.find('darwin') >= 0: subprocess.call(['open', qrimagepath]) elif sys.platform.find('linux') >= 0: subprocess.call(['xdg-open', qrimagepath]) else: os.startfile(qrimagepath) print('请使用微信扫描二维码以登录') def waitforlogin(): global tip, base_uri, redirect_uri, push_uri url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % ( tip, uuid, int(time.time())) r = myrequests.get(url=url) r.encoding = 'utf-8' data = r.text # print(data) # window.code=500; regx = r'window.code=(\d+);' pm = re.search(regx, data) code = pm.group(1) if code == '201': # 已扫描 print('成功扫描,请在手机上点击确认以登录') tip = 0 elif code == '200': # 已登录 print('正在登录...') regx = r'window.redirect_uri="(\s+?)";' pm = re.search(regx, data) redirect_uri = pm.group(1) + '&fun=new' base_uri = redirect_uri[:redirect_uri.rfind('/')] # push_uri与base_uri对应关系(排名分先后)(就是这么奇葩..) services = [ ('wx2.qq.com', 'webpush2.weixin.qq.com'), ('qq.com', 'webpush.weixin.qq.com'), ('web1.wechat.com', 'webpush1.wechat.com'), ('web2.wechat.com', 'webpush2.wechat.com'), ('wechat.com', 'webpush.wechat.com'), ('web1.wechatapp.com', 'webpush1.wechatapp.com'), ] push_uri = base_uri for (searchurl, pushurl) in services: if base_uri.find(searchurl) >= 0: push_uri = 'https://%s/cgi-bin/mmwebwx-bin' % pushurl break # closeqrimage if sys.platform.find('darwin') >= 0: # for osx with preview os.system("osascript -e 'quit app \"preview\"'") elif code == '408': # 超时 pass # elif code == '400' or code == '500': return code def login(): global skey, wxsid, wxuin, pass_ticket, baserequest r = myrequests.get(url=redirect_uri) r.encoding = 'utf-8' data = r.text # print(data) doc = xml.dom.minidom.parsestring(data) root = doc.documentelement for node in root.childnodes: if node.nodename == 'skey': skey = node.childnodes[0].data elif node.nodename == 'wxsid': wxsid = node.childnodes[0].data elif node.nodename == 'wxuin': wxuin = node.childnodes[0].data elif node.nodename == 'pass_ticket': pass_ticket = node.childnodes[0].data # print('skey: %s, wxsid: %s, wxuin: %s, pass_ticket: %s' % (skey, wxsid, # wxuin, pass_ticket)) if not all((skey, wxsid, wxuin, pass_ticket)): return false baserequest = { 'uin': int(wxuin), 'sid': wxsid, 'skey': skey, 'deviceid': deviceid, } return true def webwxinit(): url = (base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % ( pass_ticket, skey, int(time.time())) ) params = {'baserequest': baserequest } headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url, data=json.dumps(params),headers=headers) r.encoding = 'utf-8' data = r.json() if debug: f = open(os.path.join(os.getcwd(), 'webwxinit.json'), 'wb') f.write(r.content) f.close() # print(data) global contactlist, my, synckey dic = data contactlist = dic['contactlist'] my = dic['user'] synckey = dic['synckey'] state = responsestate('webwxinit', dic['baseresponse']) return state def webwxgetcontact(): url = (base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % ( pass_ticket, skey, int(time.time())) ) headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url,headers=headers) r.encoding = 'utf-8' data = r.json() if debug: f = open(os.path.join(os.getcwd(), 'webwxgetcontact.json'), 'wb') f.write(r.content) f.close() # print(data) dic = data memberlist = dic['memberlist'] # 倒序遍历,不然删除的时候出问题.. specialusers = ["newsapp", "fmessage", "filehelper", "weibo", "qqmail", "tmessage", "qmessage", "qqsync", "floatbottle", "lbsapp", "shakeapp", "medianote", "qqfriend", "readerapp", "blogapp", "facebookapp", "masssendapp", "meishiapp", "feedsapp", "voip", "blogappweixin", "weixin", "brandsessionholder", "weixinreminder", "wxid_novlwrv3lqwv11", "gh_22b87fa7cb3c", "officialaccounts", "notification_messages", "wxitil", "userexperience_alarm"] for i in range(len(memberlist) - 1, -1, -1): member = memberlist[i] if member['verifyflag'] & 8 != 0: # 公众号/服务号 memberlist.remove(member) elif member['username'] in specialusers: # 特殊账号 memberlist.remove(member) elif member['username'].find('@@') != -1: # 群聊 memberlist.remove(member) elif member['username'] == my['username']: # 自己 memberlist.remove(member) return memberlist def createchatroom(usernames): memberlist = [{'username': username} for username in usernames] url = (base_uri + '/webwxcreatechatroom?pass_ticket=%s&r=%s' % ( pass_ticket, int(time.time())) ) params = { 'baserequest': baserequest, 'membercount': len(memberlist), 'memberlist': memberlist, 'topic': '', } headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url, data=json.dumps(params),headers=headers) r.encoding = 'utf-8' data = r.json() # print(data) dic = data chatroomname = dic['chatroomname'] memberlist = dic['memberlist'] deletedlist = [] blockedlist = [] for member in memberlist: if member['memberstatus'] == 4: # 被对方删除了 deletedlist.append(member['username']) elif member['memberstatus'] == 3: # 被加入黑名单 blockedlist.append(member['username']) state = responsestate('createchatroom', dic['baseresponse']) return chatroomname, deletedlist, blockedlist def deletemember(chatroomname, usernames): url = (base_uri + '/webwxupdatechatroom?fun=delmember&pass_ticket=%s' % (pass_ticket) ) params = { 'baserequest': baserequest, 'chatroomname': chatroomname, 'delmemberlist': ','.join(usernames), } headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url, data=json.dumps(params),headers=headers) r.encoding = 'utf-8' data = r.json() # print(data) dic = data state = responsestate('deletemember', dic['baseresponse']) return state def addmember(chatroomname, usernames): url = (base_uri + '/webwxupdatechatroom?fun=addmember&pass_ticket=%s' % (pass_ticket) ) params = { 'baserequest': baserequest, 'chatroomname': chatroomname, 'addmemberlist': ','.join(usernames), } headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url, data=json.dumps(params),headers=headers) r.encoding = 'utf-8' data = r.json() # print(data) dic = data memberlist = dic['memberlist'] deletedlist = [] blockedlist = [] for member in memberlist: if member['memberstatus'] == 4: # 被对方删除了 deletedlist.append(member['username']) elif member['memberstatus'] == 3: # 被加入黑名单 blockedlist.append(member['username']) state = responsestate('addmember', dic['baseresponse']) return deletedlist, blockedlist def synckey(): synckeyitems = ['%s_%s' % (item['key'], item['val']) for item in synckey['list']] synckeystr = '|'.join(synckeyitems) return synckeystr def synccheck(): url = push_uri + '/synccheck?' params = { 'skey': baserequest['skey'], 'sid': baserequest['sid'], 'uin': baserequest['uin'], 'deviceid': baserequest['deviceid'], 'synckey': synckey(), 'r': int(time.time()), } r = myrequests.get(url=url,params=params) r.encoding = 'utf-8' data = r.text # print(data) # window.synccheck={retcode:"0",selector:"2"} regx = r'window.synccheck={retcode:"(\d+)",selector:"(\d+)"}' pm = re.search(regx, data) retcode = pm.group(1) selector = pm.group(2) return selector def webwxsync(): global synckey url = base_uri + '/webwxsync?lang=zh_cn&skey=%s&sid=%s&pass_ticket=%s' % ( baserequest['skey'], baserequest['sid'], quote_plus(pass_ticket)) params = { 'baserequest': baserequest, 'synckey': synckey, 'rr': ~int(time.time()), } headers = {'content-type': 'application/json; charset=utf-8'} r = myrequests.post(url=url, data=json.dumps(params)) r.encoding = 'utf-8' data = r.json() # print(data) dic = data synckey = dic['synckey'] state = responsestate('webwxsync', dic['baseresponse']) return state def heartbeatloop(): while true: selector = synccheck() if selector != '0': webwxsync() time.sleep(1) def main(): global myrequests if hasattr(ssl, '_create_unverified_context'): ssl._create_default_https_context = ssl._create_unverified_context headers = {'user-agent': 'mozilla/5.0 (macintosh; intel mac os x 10_11_2) applewebkit/537.36 (khtml, like gecko) chrome/44.0.2403.125 safari/537.36'} myrequests = requests.session() myrequests.headers.update(headers) if not getuuid(): print('获取uuid失败') return print('正在获取二维码图片...') showqrimage() while waitforlogin() != '200': pass os.remove(qrimagepath) if not login(): print('登录失败') return if not webwxinit(): print('初始化失败') return memberlist = webwxgetcontact() print('开启心跳线程') threading.thread(target=heartbeatloop) membercount = len(memberlist) print('通讯录共%s位好友' % membercount) chatroomname = '' result = [] d = {} for member in memberlist: d[member['username']] = (member['nickname'], member['remarkname']) print('开始查找...') group_num = int(math.ceil(membercount / float(max_group_num))) for i in range(0, group_num): usernames = [] for j in range(0, max_group_num): if i * max_group_num + j >= membercount: break member = memberlist[i * max_group_num + j] usernames.append(member['username']) # 新建群组/添加成员 if chatroomname == '': (chatroomname, deletedlist, blockedlist) = createchatroom( usernames) else: (deletedlist, blockedlist) = addmember(chatroomname, usernames) # todo blockedlist 被拉黑列表 deletedcount = len(deletedlist) if deletedcount > 0: result += deletedlist # 删除成员 deletemember(chatroomname, usernames) # 进度条 progress = max_progress_len * (i + 1) / group_num print('[', '#' * int(progress), '-' * int(max_progress_len - progress), ']', end=' ') print('新发现你被%d人删除' % deletedcount) for i in range(deletedcount): if d[deletedlist[i]][1] != '': print('%s(%s)' % (d[deletedlist[i]][0],d[deletedlist[i]][1])) else: print(d[deletedlist[i]][0]) if i != group_num - 1: print('正在继续查找,请耐心等待...') # 下一次进行接口调用需要等待的时间 time.sleep(interface_calling_interval) # todo 删除群组 print('\n结果汇总完毕,20s后可重试...') resultnames = [] for r in result: if d[r][1] != '': resultnames.append('%s(%s)' % (d[r][0],d[r][1])) else: resultnames.append(d[r][0]) print('---------- 被删除的好友列表(共%d人) ----------' % len(result)) # 过滤emoji resultnames = list(map(lambda x: re.sub(r'<span.+/span>', '', x), resultnames)) if len(resultnames): print('\n'.join(resultnames)) else: print("无") print('---------------------------------------------') # windows下编码问题修复 # http://blog.csdn.net/heyuxuanzee/article/details/8442718 class unicodestreamfilter: def __init__(self, target): self.target = target self.encoding = 'utf-8' self.errors = 'replace' self.encode_to = self.target.encoding def write(self, s): if type(s) == str: try: s = s.decode('utf-8') except: pass s = s.encode(self.encode_to, self.errors).decode(self.encode_to) self.target.write(s) if sys.stdout.encoding == 'cp936': sys.stdout = unicodestreamfilter(sys.stdout) if __name__ == '__main__': print('本程序的查询结果可能会引起一些心理上的不适,请小心使用...') print('1小时内只能使用一次,否则会因操作繁忙阻止建群') main() print('回车键退出...') input()