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

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

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

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

于是 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
from __future__ import print_functionimport os
import requests
import re
import time
import xml.dom.minidom
import json
import sys
import math
import subprocess
import ssl
import threadingDEBUG = FalseMAX_GROUP_NUM = 2  # 每组人数
INTERFACE_CALLING_INTERVAL = 5  # 接口调用时间间隔, 间隔太短容易出现"操作太频繁", 会被限制操作半小时左右
MAX_PROGRESS_LEN = 50QRImagePath = 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:xrangerange = xrange
except:# python 3passdef 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 Falsereturn Truedef getUUID():global uuidurl = '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 Truereturn Falsedef showQRImage():global tipurl = 'https://login.weixin.qq.com/qrcode/' + uuidparams = {'t': 'webwx','_': int(time.time()),}r = myRequests.get(url=url, params=params)tip = 1f = 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_uriurl = '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 = 0elif 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_urifor (searchUrl, pushUrl) in services:if base_uri.find(searchUrl) >= 0:push_uri = 'https://%s/cgi-bin/mmwebwx-bin' % pushUrlbreak# closeQRImageif sys.platform.find('darwin') >= 0:  # for OSX with Previewos.system("osascript -e 'quit app \"Preview\"'")elif code == '408':  # 超时pass# elif code == '400' or code == '500':return codedef login():global skey, wxsid, wxuin, pass_ticket, BaseRequestr = myRequests.get(url=redirect_uri)r.encoding = 'utf-8'data = r.text# print(data)doc = xml.dom.minidom.parseString(data)root = doc.documentElementfor node in root.childNodes:if node.nodeName == 'skey':skey = node.childNodes[0].dataelif node.nodeName == 'wxsid':wxsid = node.childNodes[0].dataelif node.nodeName == 'wxuin':wxuin = node.childNodes[0].dataelif 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 FalseBaseRequest = {'Uin': int(wxuin),'Sid': wxsid,'Skey': skey,'DeviceID': deviceId,}return Truedef 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, SyncKeydic = dataContactList = dic['ContactList']My = dic['User']SyncKey = dic['SyncKey']state = responseState('webwxinit', dic['BaseResponse'])return statedef 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 = dataMemberList = 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 MemberListdef 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 = dataChatRoomName = 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, BlockedListdef 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 = datastate = responseState('deleteMember', dic['BaseResponse'])return statedef 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 = dataMemberList = 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, BlockedListdef syncKey():SyncKeyItems = ['%s_%s' % (item['Key'], item['Val'])for item in SyncKey['List']]SyncKeyStr = '|'.join(SyncKeyItems)return SyncKeyStrdef 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 selectordef webwxsync():global SyncKeyurl = 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 = dataSyncKey = dic['SyncKey']state = responseState('webwxsync', dic['BaseResponse'])return statedef heartBeatLoop():while True:selector = syncCheck()if selector != '0':webwxsync()time.sleep(1)def main():global myRequestsif hasattr(ssl, '_create_unverified_context'):ssl._create_default_https_context = ssl._create_unverified_contextheaders = {'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失败')returnprint('正在获取二维码图片...')showQRImage()while waitForLogin() != '200':passos.remove(QRImagePath)if not login():print('登录失败')returnif not webwxinit():print('初始化失败')returnMemberList = 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:breakMember = 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_numprint('[', '#' * 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))# 过滤emojiresultNames = 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/8442718class UnicodeStreamFilter:def __init__(self, target):self.target = targetself.encoding = 'utf-8'self.errors = 'replace'self.encode_to = self.target.encodingdef write(self, s):if type(s) == str:try:s = s.decode('utf-8')except:passs = 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()

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

  1. python找人脚本_黑科技 Python脚本帮你找出微信上删除你好友的人

    怎么利用 Python 查看被删的微信好友几种方法可以找回被删除的好友,方法也许不全,但是希望可以帮到大家. 不用群发用Python脚本查微信被哪些好友删除 Python大法已经被网友们玩儿的出神入化 ...

  2. python批量删除微信好友_黑科技,Python 脚本帮你找出微信上删除你好友的人(附视频教程)...

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

  3. Python 脚本帮你找出微信上删除你好友的人

    本文来自稀土掘金江昪编译自 Github:0x5e/wechat-deleted-friends  " 清理下[微笑],不用回.你的朋友圈没事也该清清了,打开设置,通用,功能,群助手,全选, ...

  4. Python 脚本帮你找出微信上删除了你的“好友“

    一.普遍验证办法 群发消息应该算是微信上流传最广的找到删除好友的方法了.但群发消息不仅仅会把通讯录里面所有的好友骚扰一遍,而且你还得挨个删除好几百个聊天记录,回复大家的疑问和鄙视.作为一个互联网从业者 ...

  5. python批量删除微信好友_Python找出微信上删除你好友的人脚本写法

    原标题:Python找出微信上删除你好友的人脚本写法 相信大家在微信上一定被上面的这段话刷过屏,群发消息应该算是微信上流传最广的找到删除好友的方法了.但群发消息不仅仅会把通讯录里面所有的好友骚扰一遍, ...

  6. python找人脚本_Python找出微信上删除你好友的人脚本写法

    相信大家在微信上一定被上面的这段话刷过屏,群发消息应该算是微信上流传最广的找到删除好友的方法了.但群发消息不仅仅会把通讯录里面所有的好友骚扰一遍,而且你还得挨个删除好几百个聊天记录,回复大家的疑问和鄙 ...

  7. 使用scrapy来找出微博上没有加好友的熟人

    背景介绍 我微博玩的晚,同学里面加上好友的也就40不到,为了把那些隐藏的好友揪出来.用scrapy写一个爬虫试一试. 思路 微博上面关注和粉丝都是公开的数据,可以用爬虫获取到的.而一个好友圈子里面的人 ...

  8. 【微信辅助】疫情当前,python帮你找出朋友圈的武汉朋友给予关怀

    我是程序员小小叶,为大家带来原创精彩技术内容. 疫情无论是对于个体还是企业都是无法承受之重.希望疫情早日过去,这是作为一个普通人一个的小小心愿. 我们身边的亲朋好友也或多或少受到了疫情的影响,让我们来 ...

  9. Linux Shell脚本专栏_找出占用CPU/内存过高的进程_05

    文章目录 找出占用CPU/内存过高的进程 1. 脚本 2. 运行脚本 3. 效果图 找出占用CPU/内存过高的进程 1. CPU 过高的进程2. 内存 过高的进程 1. 脚本 #/bin/bash e ...

最新文章

  1. [codevs 1302] 小矮人(2002年CEOI中欧信息学奥赛)
  2. 自定义控件-侧边菜单SlidingMenu(滑动菜单)
  3. [Qt教程] 第26篇 数据库(六)SQL关系表格模型QSqlRelationalTableModel
  4. 过 DNF TP 驱动保护(一)
  5. 网站如何快速搭建即时响应客服系统
  6. uva 12563——Jin Ge Jin Qu hao
  7. 使用docker部署flask项目
  8. 仿58 php框架源码,转转最新源码
  9. 2012年9月计算机二级c语言,2012 年9月 全国计算机二级c语言试题
  10. 一个技术预案,让老板当场喊出了“奥利给”
  11. 统计字符串中个字符的个数
  12. IOS开发学习笔记-----UILabel 详解
  13. leetcode—20.二叉树构建相关题目leetcode总结
  14. jdk Integer 具体实现
  15. 绿油损耗大于大多数高速板材,对于高度板材而言,绿油带来的损耗会更明显
  16. 【SAR图像分辨率】匹配跟踪算法和自聚焦算法提高SAR图像分辨率
  17. MySql 磁盘满了的处理
  18. Android开源项目 陌客随机聊天
  19. 台式计算机中经常使用的硬盘多是多少英寸,笔记本硬盘和台式机硬盘有什么不同...
  20. 凌晨4点的大家都在做什么?大数据告诉你

热门文章

  1. 跨境时尚电商SHEIN基于Kubernetes的DevOps实践
  2. 联想G510的Win10系统重装
  3. python in arcgis_【直播】基于Python开发ArcGIS插件:Python Add-in
  4. 3M2131 3M2131胶3M2131B 3M2131C电缆修补胶 3M海缆修补胶 冷硫化胶
  5. java jdbctemplate update_java - Spring JdbcTemplate.update()不更新行 - 堆栈内存溢出
  6. SAP成都研究院2018年总共87篇技术文章合集
  7. 表格划过变色之tr:hover无效,td:hover有效解决方法
  8. 测试常用 SQL 命令
  9. Python地理坐标系和投影坐标系转换
  10. 调用海龟画图模块--画五角星