本文共 15101 字,大约阅读时间需要 50 分钟。
此篇文章参考微信通信机制,收益匪浅!:
当然也可以写发送消息的代码,机制和上述文章介绍的一样,大家可以参考学习!
偶遇:偶尔在论坛发现有外国友人在开源社区发布的Python微信删除好友查看,但是这个作者的代码我这边运行出现了点问题,就是各种问题,我在作者基础上进行研究修改!最终在Python2.7成功运行,之后又用了两天时间把运行环境转移到Python3.5。几乎完美执行!但是有一个问题我真的无能为力!在当我输出第三组好友名单的时候,就报错了!
报错如下:没办法我就去掉输出好友,就正常了,完整代码在下面,几乎每一句做了详细的注释!
#UnicodeEncodeError: 'UCS-2' codec can't encode characters in position 138-138: Non-BMP character not supported in Tk
原理:简单点就是把好友分组拉入群组,不超过40,别人是看不到的,拉不进俩就代表你被删了!!
但是问题来了,我最近发现腾讯web端操作容易被受限制,我拉了一组或者两组就被限制,说操作频繁了!!所以大家可以拿来学习微信通讯机制!!,不可以用来做坏事哦!虽然可以得到这个用户所有的信息!
完整代码如下:祝大家学习进步!详细注释代码中有写。^~^,就不一句一句讲了。
1、运行代码,会跳出二维码,扫描在手机确认登陆,然后关闭图片,程序自动运行!! 2、按操作一步一步执行就可以了~~,具体实现看注释其实不难。# coding=utf-8 #Python 3.5#Author Jack Chiangimport os import urllib import re import http.cookiejar import time import xml.dom.minidom import json import sysimport mathimport matplotlib.pyplot as plt import matplotlib.cbook as cbookimport matplotlibfrom matplotlib.font_manager import FontPropertiesDEBUG = True #确定当前测试是否查看服务器返回的Json数据 max_group = 35 # 每组人数 QRImagePath = os.getcwd() + '/qrcode.jpg' tip = 0 #全局变量:标识是否扫描二维码登陆uuid = '' #全局变量:获取登陆值 base_uri = '' redirect_uri = '' skey = '' wxsid = '' wxuin = '' pass_ticket = '' deviceId = 'e000000000000000'#这个参数是一个15个字节的随机数,所以写死了 BaseRequest = {} ContactList = [] My = [] def getUUID(): global uuid url = 'https://login.weixin.qq.com/jslogin' params = { 'appid': 'wx782c26e4c19acffb', 'fun': 'new', 'lang': 'zh_CN', '_': int(time.time()), } #使用get方法,通过请求地址:https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&fun=new&lang=zh_CN&_=时间戳 #其中,时间戳这个值是当前距离林威治标准时间的毫秒。 request = urllib.request.Request(url = url, data = urllib.parse.urlencode(params).encode(encoding='UTF-8')) response = urllib.request.urlopen(request) #成功则返回:window.QRLogin.code = 200; window.QRLogin.uuid = "UUID" data = response.read() #1970纪元后经过的浮点秒数time.time() #print (time.time()) #print(data) #只要有UUID就可以登陆微信网页版 # window.QRLogin.code = 200; window.QRLogin.uuid = "oZwt_bFfRg=="; regx = r'window.QRLogin.code = (\d+?); window.QRLogin.uuid = "(\S+?)"' #利用正则表达式匹配 pm = re.search(regx,str(data)) code = pm.group(1) #200 uuid = pm.group(2) #oZwt_bFfRg== if code == '200': return True return False #下载微信登陆图片然后打开def showQRImage(): global tip #3、查询是否扫描二维码登录 #使用get方法,查询地址:https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid=XXXXXX&tip=1&_=时间戳 tip=1 url = 'https://login.weixin.qq.com/qrcode/%s?tip=%d&_=%d' % (uuid,tip,int(time.time())) """params = { 'tip': '1', '_': int(time.time()), }""" #这里的XXXXXX是我们刚才获取的uuid,时间戳同上。tip在第一次获取时应为1,这个数是每次查询要变的。 #拼接地址 request = urllib.request.Request(url = url) #等价request = urllib.request.Request(url = url, data = urllib.parse.urlencode(params)) response = urllib.request.urlopen(request) f = open(QRImagePath, 'wb') f.write(response.read()) f.close() #使用matplotlib显示图片 image_file = cbook.get_sample_data(QRImagePath) image = plt.imread(image_file) font = FontProperties(fname=r"D:/Develop//Python27//Lib//site-packages//matplotlib//mpl-data//fonts//ttf//Veral.ttc", size=20) s = u'~扫描二维码,然后关闭继续哦~' plt.imshow(image) plt.title(s,fontproperties=font) plt.axis('off') # clear x- and y-axes plt.show() plt.close() #判断当前执行是哪种环境,然后对呀的环境打开图片 """if sys.platform.find('darwin') >= 0: #MAC os.system('open %s' % QRImagePath) elif sys.platform.find('linux') >= 0: #Linux os.system('xdg-open %s' % QRImagePath) else: #win32 os.system('call %s' % QRImagePath)""" print('请使用微信扫描二维码以登录') def waitForLogin(): global tip, base_uri, redirect_uri url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (tip, uuid, int(time.time())) print(url) request = urllib.request.Request(url = url) response = urllib.request.urlopen(request) #如果不扫描会停在这个位置 data = response.read() #print(data) # window.code=500; regx = r'window.code=(\d+);' pm = re.search(regx, str(data)) code = pm.group(1) # window.code=408; if code == '201': #已扫描 print('扫描成功!,请在手机上点击确认以登录') tip = 0 elif code == '200': #已登录 #window.code=200; #下面链接为个人登陆的网页版,你可以把自己获取的链接复制到百度试一下,看看效果!直接跳到网页版自己的微信。 #window.redirect_uri="https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A7YkJKTtu09JTsy7f6zy3w8e@qrticket_0 #&uuid=********&lang=zh_CN&scan=*********"; print('正在登录...') regx = r'window.redirect_uri="(\S+?)";' pm = re.search(regx, str(data)) redirect_uri = pm.group(1) + '&fun=new' base_uri = redirect_uri[:redirect_uri.rfind('/')] #print(redirect_uri) #print(base_uri) elif code == '408': #超时不做操作 pass return code def login(): global skey, wxsid, wxuin, pass_ticket, BaseRequest #访问登录地址在获取地址后面加&fun=new,获得uin、sid、pass_ticket、skey request = urllib.request.Request(url = redirect_uri) response = urllib.request.urlopen(request) data = response.read() # print(data) #根据data链接访问得到一下xml数据,进行解析 '''''''' 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 skey == '' or wxsid == '' or wxuin == '' or pass_ticket == '': return False #拼接成一个字典后面登陆会用到 BaseRequest = { 'Uin': int(wxuin), 'Sid': wxsid, 'Skey': skey, 'DeviceID': deviceId, } return True #微信初始化def webwxinit(): #要使用POST方法,访问地址:https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=时间戳&lang=ch_ZN&pass_ticket=XXXXXX url = base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (pass_ticket, skey, int(time.time())) #print(url) #当然这个url需要post方式访问,参数就是之前形成的字典值 #(BaseRequest)uin、sid、skey分别对应上面步骤4获取的字符串,DeviceID是e后面跟着一个15字节的随机数。 params = { 'BaseRequest': BaseRequest } request = urllib.request.Request(url = url, data = json.dumps(params).encode(encoding='UTF-8')) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request) data = response.read() #data = data.encode() global ContactList, My dic = json.loads(str(data,encoding='utf-8')) #读取服务器通过重重验证过来返回的数据,dic就是当前用户所有信息 ContactList = dic['ContactList'] My = dic['User'] #User就是你的个人信息 #print(os.getcwd() + '/webwxinit.json'); if DEBUG == True: f = open(os.getcwd() + '/webwxinit.json', 'wb') f.write(data) f.close() ErrMsg = dic['BaseResponse']['ErrMsg']#捕获服务器异常信息 if len(ErrMsg) > 0: print(ErrMsg) Ret = dic['BaseResponse']['Ret'] if Ret != 0: return False return True # print(data) Ret = dic['BaseResponse']['Ret'] if Ret != 0: return False return True #获取好友列表数据def webwxgetcontact(): url = base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&r=%s' % (pass_ticket, skey, int(time.time())) request = urllib.request.Request(url = url) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request) data = response.read() if DEBUG == True: f = open(os.getcwd() + '/webwxgetcontact.json', 'wb') f.write(data) f.close() # print(data) #data是这个用户所有好友数据 dic = json.loads(str(data,encoding='utf-8')) MemberList = dic['MemberList'] # 倒序遍历,不然删除的时候出问题.. SpecialUsers = ['newsapp', 'fmessage', 'filehelper', 'weibo', 'qqmail', 'fmessage', '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', 'wxid_novlwrv3lqwv11', 'gh_22b87fa7cb3c', 'wxitil', 'userexperience_alarm', 'notification_messages'] for i in range(len(MemberList) - 1, -1, -1): #range(x-1,-1,-1)是倒序便利 例如range(10,-1,-1) 10,...0 Member = MemberList[i] if Member['VerifyFlag'] & 8 != 0: # 公众号/服务号 24&8=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 = [] for UserName in UserNames: MemberList.append({ 'UserName': UserName}) url = base_uri + '/webwxcreatechatroom?pass_ticket=%s&r=%s' % (pass_ticket, int(time.time())) params = { 'BaseRequest': BaseRequest, 'MemberCount': len(MemberList), 'MemberList': MemberList, 'Topic': '', } request = urllib.request.Request(url = url, data = json.dumps(params).encode(encoding='UTF-8')) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request) data = response.read() if DEBUG == True: #测试数据 f = open(os.getcwd() + '/webwxChatroo.json', 'wb') f.write(data) f.close() # print(data) dic = json.loads(str(data,encoding='utf-8')) ChatRoomName = dic['ChatRoomName'] MemberList = dic['MemberList'] DeletedList = [] for Member in MemberList: if Member['MemberStatus'] == 4: #被对方删除了 DeletedList.append(Member['UserName']) ErrMsg = dic['BaseResponse']['ErrMsg'] if len(ErrMsg) > 0: print(ErrMsg) return (ChatRoomName, DeletedList) #删除群组成员,最后删除自己def deleteMember(ChatRoomName, UserNames): url = base_uri + '/webwxupdatechatroom?fun=delmember&pass_ticket=%s' % (pass_ticket) params = { 'BaseRequest': BaseRequest, 'ChatRoomName': ChatRoomName, 'DelMemberList': ','.join(UserNames), } request = urllib.request.Request(url = url, data = json.dumps(params).encode(encoding='UTF8')) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request) data = response.read() # print(data) dic = json.loads(str(data,encoding='utf-8')) ErrMsg = dic['BaseResponse']['ErrMsg'] if len(ErrMsg) > 0: print(ErrMsg) Ret = dic['BaseResponse']['Ret'] if Ret != 0: return False return True def addMember(ChatRoomName, UserNames): url = base_uri + '/webwxupdatechatroom?fun=addmember&pass_ticket=%s' % (pass_ticket) params = { 'BaseRequest': BaseRequest, 'ChatRoomName': ChatRoomName, 'AddMemberList': ','.join(UserNames), } request = urllib.request.Request(url = url, data = json.dumps(params).encode(encoding='UTF-8')) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request) data = response.read() # print(data) dic = json.loads(str(data,encoding='utf-8')) MemberList = dic['MemberList'] DeletedList = [] for Member in MemberList: if Member['MemberStatus'] == 4: #被对方删除了 DeletedList.append(Member['UserName']) ErrMsg = dic['BaseResponse']['ErrMsg'] if len(ErrMsg) > 0: print(ErrMsg) return DeletedList def main(): #模拟登陆凭证 opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(http.cookiejar.CookieJar())) urllib.request.install_opener(opener) if getUUID() == False: print ('获取uuid失败') return showQRImage() #下载微信登陆图片然后登陆 time.sleep(1) while waitForLogin() != '200': pass #循环验证直到为200返回true成功扫描 os.remove(QRImagePath) if login() == False: print ('登录失败') return if webwxinit() == False: print ('初始化失败') return MemberList = webwxgetcontact() MemberCount = len(MemberList) print ('通讯录共%s位好友' % MemberCount) ChatRoomName = '' result = [] #开始分组 好友总数/要分组人数 for i in range(0, int(math.ceil(MemberCount / float(max_group)))): UserNames = [] NickNames = [] DeletedList = '' for j in range(0, max_group): if i * max_group + j >= MemberCount: #判断 第N组人数是否超过总人数 break Member = MemberList[i * max_group + j] UserNames.append(Member['UserName']) #Username是一丢字母,不断变化的 NickNames.append(Member['NickName'].encode('utf-8')) #NickName是好友昵称 print('第%s组...' % (i + 1)) for k in range(0,NickNames.__len__()): NickNames[k] = str(NickNames[k],'utf-8') #str1 = ", " #str1 = str1.join(NickNames) #print(str1) #但是下面输出好友会出现问题,在出现第三组的时候就报下面错,不知道如何解决!!,去掉输出完美运行! #UnicodeEncodeError: 'UCS-2' codec can't encode characters in position 138-138: Non-BMP character not supported in Tk #print(', '.join(NickNames)) #会出现css标签,我想了想那是表情啊~~~哈哈 print('按回车键继续。。。') input() # 新建群组/添加成员 if ChatRoomName == '': (ChatRoomName, DeletedList) = createChatroom(UserNames) else: DeletedList = addMember(ChatRoomName, UserNames) DeletedCount = len(DeletedList) if DeletedCount > 0: result += DeletedList print('找到%s个被删好友' % DeletedCount) #input() # 删除成员 deleteMember(ChatRoomName, UserNames) # todo 删除群组 resultNames = [] for Member in MemberList: if Member['UserName'] in result: NickName = Member['NickName'] if Member['RemarkName'] != '': NickName += '(%s)' % Member['RemarkName'] resultNames.append(NickName.encode('utf-8')) for k in range(0,resultNames.__len__()): resultNames[k] = str(resultNames[k],'utf-8') print ('---------- 被删除的好友列表 ----------') print ('\n'.join(resultNames)) print ('-----------------------------------') if __name__ == '__main__' : print ('开始查询微信删除你的好友信息...') input('回车键继续...') main() input('回车键结束') 0 xxx xxx xxx xxx 1
4、测试结果如下:我最近容易受限制!!