Python找出微信上删除你好友的人脚本写法
相信大家在微信上一定被上面的这段话刷过屏,群发消息应该算是微信上流传最广的找到删除好友的方法了。但群发消息不仅仅会把通讯录里面所有的好友骚扰一遍,而且你还得挨个删除好几百个聊天记录,回复大家的疑问和鄙视。作为一个互联网从业者,除了群发消息就不能有更高效、不打扰好友的方式么?
答案是当然有,本人的微:1613161916可以一起讨论python,还有许多Python资料可以发送。
微信在拉好友进群聊的时候,如果这个人删除了你好友的话,会提示你一下「请先发送朋友验证申请给某某,对方将你加为微信朋友后,你才能邀请其加入群聊。」有办法了,那我把微信好友拉一个大群里面,然后默默的删掉微信群不就好了么。
于是Github上就有一位叫0x5e的开发者写了这么一个Python脚本来代替你手动拉群和踢人。经过笔者测试,目前只能支持Mac的操作系统。在此之前,笔者在稀土掘金上先放出了Github的方法,这里再详细提下。
在0x5e的Github代码仓库描述里面,他具体的实现步骤是用Python脚本处理网页版微信的拉群、踢人操作。具体的步骤如下:
- 下载代码文件wdf.py
- 打开Terminal输入:python然后拖动刚才下载的wdf.py到Terminal窗后回车。命令格式类似:pythonwdf.py
- 接下来按步骤扫码操作即可;
查询结果可能会引起一些心理上的不适,请小心使用。
笔者测试了两次,这个Python脚本还是有一些小问题,欢迎开发者们给原作者发PullRequest完善:
- 两次结果稍有出入,可能是微信网页版返回数据的问题
- 最终在微信会遗留一个只有自己的群组,需要手动删除
- 此脚本暂时不支持查找被拉黑的情况
源码
#!/usr/bin/envpython #coding=utf-8 from__future__importprint_function importos importrequests importre importtime importxml.dom.minidom importjson importsys importmath importsubprocess importssl importthreading 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: #python3 pass defresponseState(func,BaseResponse): ErrMsg=BaseResponse['ErrMsg'] Ret=BaseResponse['Ret'] ifDEBUGorRet!=0: print('func:%s,Ret:%d,ErrMsg:%s'%(func,Ret,ErrMsg)) ifRet!=0: returnFalse returnTrue defgetUUID(): globaluuid 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) ifcode=='200': returnTrue returnFalse defshowQRImage(): globaltip 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) ifsys.platform.find('darwin')>=0: subprocess.call(['open',QRImagePath]) elifsys.platform.find('linux')>=0: subprocess.call(['xdg-open',QRImagePath]) else: os.startfile(QRImagePath) print('请使用微信扫描二维码以登录') defwaitForLogin(): globaltip,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) ifcode=='201':#已扫描 print('成功扫描,请在手机上点击确认以登录') tip=0 elifcode=='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)inservices: ifbase_uri.find(searchUrl)>=0: push_uri='https://%s/cgi-bin/mmwebwx-bin'%pushUrl break #closeQRImage ifsys.platform.find('darwin')>=0:#forOSXwithPreview os.system("osascript-e'quitapp\"Preview\"'") elifcode=='408':#超时 pass #elifcode=='400'orcode=='500': returncode deflogin(): globalskey,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 fornodeinroot.childNodes: ifnode.nodeName=='skey': skey=node.childNodes[0].data elifnode.nodeName=='wxsid': wxsid=node.childNodes[0].data elifnode.nodeName=='wxuin': wxuin=node.childNodes[0].data elifnode.nodeName=='pass_ticket': pass_ticket=node.childNodes[0].data #print('skey:%s,wxsid:%s,wxuin:%s,pass_ticket:%s'%(skey,wxsid, #wxuin,pass_ticket)) ifnotall((skey,wxsid,wxuin,pass_ticket)): returnFalse BaseRequest={ 'Uin':int(wxuin), 'Sid':wxsid, 'Skey':skey, 'DeviceID':deviceId, } returnTrue defwebwxinit(): 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() ifDEBUG: f=open(os.path.join(os.getcwd(),'webwxinit.json'),'wb') f.write(r.content) f.close() #print(data) globalContactList,My,SyncKey dic=data ContactList=dic['ContactList'] My=dic['User'] SyncKey=dic['SyncKey'] state=responseState('webwxinit',dic['BaseResponse']) returnstate defwebwxgetcontact(): 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() ifDEBUG: 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"] foriinrange(len(MemberList)-1,-1,-1): Member=MemberList[i] ifMember['VerifyFlag']&8!=0:#公众号/服务号 MemberList.remove(Member) elifMember['UserName']inSpecialUsers:#特殊账号 MemberList.remove(Member) elifMember['UserName'].find('@@')!=-1:#群聊 MemberList.remove(Member) elifMember['UserName']==My['UserName']:#自己 MemberList.remove(Member) returnMemberList defcreateChatroom(UserNames): MemberList=[{'UserName':UserName}forUserNameinUserNames] 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=[] forMemberinMemberList: ifMember['MemberStatus']==4:#被对方删除了 DeletedList.append(Member['UserName']) elifMember['MemberStatus']==3:#被加入黑名单 BlockedList.append(Member['UserName']) state=responseState('createChatroom',dic['BaseResponse']) returnChatRoomName,DeletedList,BlockedList defdeleteMember(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']) returnstate defaddMember(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=[] forMemberinMemberList: ifMember['MemberStatus']==4:#被对方删除了 DeletedList.append(Member['UserName']) elifMember['MemberStatus']==3:#被加入黑名单 BlockedList.append(Member['UserName']) state=responseState('addMember',dic['BaseResponse']) returnDeletedList,BlockedList defsyncKey(): SyncKeyItems=['%s_%s'%(item['Key'],item['Val']) foriteminSyncKey['List']] SyncKeyStr='|'.join(SyncKeyItems) returnSyncKeyStr defsyncCheck(): 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) returnselector defwebwxsync(): globalSyncKey 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']) returnstate defheartBeatLoop(): whileTrue: selector=syncCheck() ifselector!='0': webwxsync() time.sleep(1) defmain(): globalmyRequests ifhasattr(ssl,'_create_unverified_context'): ssl._create_default_https_context=ssl._create_unverified_context headers={'User-agent':'Mozilla/5.0(Macintosh;IntelMacOSX10_11_2)AppleWebKit/537.36(KHTML,likeGecko)Chrome/44.0.2403.125Safari/537.36'} myRequests=requests.Session() myRequests.headers.update(headers) ifnotgetUUID(): print('获取uuid失败') return print('正在获取二维码图片...') showQRImage() whilewaitForLogin()!='200': pass os.remove(QRImagePath) ifnotlogin(): print('登录失败') return ifnotwebwxinit(): print('初始化失败') return MemberList=webwxgetcontact() print('开启心跳线程') threading.Thread(target=heartBeatLoop) MemberCount=len(MemberList) print('通讯录共%s位好友'%MemberCount) ChatRoomName='' result=[] d={} forMemberinMemberList: d[Member['UserName']]=(Member['NickName'],Member['RemarkName']) print('开始查找...') group_num=int(math.ceil(MemberCount/float(MAX_GROUP_NUM))) foriinrange(0,group_num): UserNames=[] forjinrange(0,MAX_GROUP_NUM): ifi*MAX_GROUP_NUM+j>=MemberCount: break Member=MemberList[i*MAX_GROUP_NUM+j] UserNames.append(Member['UserName']) #新建群组/添加成员 ifChatRoomName=='': (ChatRoomName,DeletedList,BlockedList)=createChatroom( UserNames) else: (DeletedList,BlockedList)=addMember(ChatRoomName,UserNames) #todoBlockedList被拉黑列表 DeletedCount=len(DeletedList) ifDeletedCount>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) foriinrange(DeletedCount): ifd[DeletedList[i]][1]!='': print('%s(%s)'%(d[DeletedList[i]][0],d[DeletedList[i]][1])) else: print(d[DeletedList[i]][0]) ifi!=group_num-1: print('正在继续查找,请耐心等待...') #下一次进行接口调用需要等待的时间 time.sleep(INTERFACE_CALLING_INTERVAL) #todo删除群组 print('\n结果汇总完毕,20s后可重试...') resultNames=[] forrinresult: ifd[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(lambdax:re.sub(r'','',x),resultNames)) iflen(resultNames): print('\n'.join(resultNames)) else: print("无") print('---------------------------------------------') #windows下编码问题修复 #http://blog.csdn.net/heyuxuanzee/article/details/8442718 classUnicodeStreamFilter: def__init__(self,target): self.target=target self.encoding='utf-8' self.errors='replace' self.encode_to=self.target.encoding defwrite(self,s): iftype(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) ifsys.stdout.encoding=='cp936': sys.stdout=UnicodeStreamFilter(sys.stdout) if__name__=='__main__': print('本程序的查询结果可能会引起一些心理上的不适,请小心使用...') print('1小时内只能使用一次,否则会因操作繁忙阻止建群') main() print('回车键退出...') input()