#coding=utf-8
#Python 3.6
#anthor rachel
import os #read system import urllib #for web module import requests import re import http.cookiejar #cookie lib import time #time module import xml.dom.minidom import json import sys import math import matplotlib.pyplot as plt import matplotlib.cbook as cbook import matplotlib from matplotlib.font_manager import FontProperties DEBUG = True #确定当前测试是否查看服务器返回的Json数据 max_group = 35 # 每组人数 QRImagePath = os.getcwd() + '/qrcode.jpg'# os.getcwd()返回当前工作目录 tip = 0 #全局变量:标识是否扫描二维码登陆 uuid = '' #全局变量:获取登陆值 base_uri = '' redirect_uri = '' push_uri = '' skey = '' wxsid = '' wxuin = '' pass_ticket = '' deviceId = 'e000000000000000' #这个参数是一个15个字节的随机数,所以写死了 BaseRequest = {} ContactList = [] My = [] SyncKey = [] def getUUID(): global uuid #定义在函数外的变量赋值 url = 'https://login.wx.qq.com/jslogin' params = { 'appid': 'wx782c26e4c19acffb', 'redirect_uri':'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage', 'fun': 'new', 'lang': 'zh_CN', '_': int(time.time()), #时间戳 } request = urllib.request.Request(url = url, data = urllib.parse.urlencode(params).encode(encoding='UTF-8')) response = urllib.request.urlopen(request) #urllib库 requeset 扒取网页的方法 #成功则返回:window.QRLogin.code = 200; window.QRLogin.uuid = "UUID" data = response.read() #只要有UUID就可以登陆微信网页版 # window.QRLogin.code = 200; window.QRLogin.uuid = "oZwt_bFfRg=="; regx = r'window.QRLogin.code = (\d+?); window.QRLogin.uuid = "(\S+?)"' #利用正则表达式匹配 r‘’表示原生字符串 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 #使用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()), }""" #get#request = urllib.request.Request(url = url) #post等价request = urllib.request.Request(url = url, data = urllib.parse.urlencode(params)) #response = urllib.request.urlopen(request) r = requests.get(url=url) f = open(QRImagePath, 'wb') #当前路径 f.write(r.content) f.close() image_file = cbook.get_sample_data(QRImagePath) image = plt.imread(image_file) #time.sleep(1)停顿一秒 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.wx.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (tip, uuid, int(time.time())) 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; 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('/')] elif code == '408': #超时不做操作 pass return code def login(): global skey, wxsid, wxuin, pass_ticket, BaseRequest #print(redirect_uri) #访问登录地址在获取地址后面加&fun=new,获得uin、sid、pass_ticket、skey request = urllib.request.Request(url = redirect_uri) response = urllib.request.urlopen(request) data = response.read() #根据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(): url = base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (pass_ticket, skey, int(time.time())) #(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) #print(response) data = response.read() ##urlib库容易抛出incompleteread异常 贼麻烦,原因是读取数据时有可能字节数不够,小白看不太懂源码 '''try: data = response.read() except (http.client.IncompleteRead) as e: data = e.partial''' #data = data.encode() global My #dic = json.read(data) dic = json.loads(str(data,encoding='utf-8')) #读取服务器通过重重验证过来返回的数据,dic就是当前用户所有信息 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 #获取好友列表数据 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() #data是这个用户所有好友数据 dic = json.loads(str(data,encoding='utf-8')) MemberList = dic['MemberList'] print(len(MemberList)) a = dic['Seq'] url = base_uri + '/webwxgetcontact?pass_ticket=%s&skey=%s&seq=%s&r=%s' % (pass_ticket, skey, a, 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() dic = json.loads(str(data, encoding='utf-8')) MemberList = dic['MemberList'] print(len(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())) print(url) params = { 'BaseRequest': BaseRequest, 'MemberCount': len(MemberList), 'MemberList': MemberList, 'Topic': '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')) print(dic) 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')) print(dic) 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(): #模拟登陆凭证 global myRequests 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) '''itchat.login() # 登录 time.sleep(1) MemberList = itchat.get_friends(update=True)[1:] MemberCount = len(MemberList[0:]) print ('通讯录共%s位好友' % MemberCount)''' chatroomUserName = '' ChatRoomName = '' result = [] #开始分组 好友总数/要分组人数 for i in range(0, int(math.ceil(MemberCount / float(max_group)))): UserNames = [] NickNames = [] Member = {} DeletedList = '' for j in range(0, max_group): if i * max_group + j >= MemberCount: #判断 第N组人数是否超过总人数 break Member.update(MemberList[i * max_group + j]) #print(Member) UserNames.append(Member['UserName']) #Username是一丢字母,不断变化的 NickNames.append(Member['NickName'].encode('utf-8')) #NickName是好友昵称 #print(UserNames) #print(NickNames) print('第%s组...' % (i + 1)) for k in range(0,NickNames.__len__()): NickNames[k] = str(NickNames[k],'utf-8') #print(NickNames) #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 == '': #r = itchat.add_member_into_chatroom(ChatRoomName, Member) # print(r) '''if r['BaseResponse']['ErrMsg'] == '': status = r['MemberList'][0]['MemberStatus'] #r = itchat.delete_member_from_chatroom(ChatRoomName, friend) return {3: u'该好友已经将你加入黑名单。', 4: u'该好友已经将你删除。', }.get(status, u'该好友仍旧与你是好友关系。') #itchat.add_member_into_chatroom(ChatRoomName, [UserNames])''' (ChatRoomName, DeletedList) = createChatroom(UserNames) else: DeletedList = addMember(ChatRoomName, UserNames) DeletedCount = len(DeletedList) if DeletedCount > 0: result += DeletedList print(DeletedList) print('找到%s个被删好友' % DeletedCount) #input() time.sleep(20) # 删除成员 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 ('开始查询微信删除你的好友信息...') main()
之前网上的代码普遍不能用。好友体量大的话,显示好友列表长度不全,是getcontact的seq值的问题。我在测试期间遇到了各种各样的问题,试过itchat库。小白啥都不懂,边百度边调试。欢迎交流,请求指点。
1.itchat库很好用,但是在add_member_into_chatroom时报错,错误原因是chartroom['memberlist'] 中的memberlist不存在。这个是库创建的chartroom,所以根源行出在itchat上,也可能是我弄错了,有知道的能否指教一二
2.困扰时间最长的是 我好友900多,代码显示186,上面的getcontact部分有问题,我更改了seq值之后显示正常了。但是代码比较繁琐,感兴趣的可以去优化一下。这个代码仍然存在bug,具体原因应该是微信限制了操作频率,以后我有时间再进行优化。
3.下面代码可能出问题。具体原因是urllib抛出的异常,时有时无,加一个try即可,高阶可试一下with
data = response.read()
4.下面代码可能有问题。具体抛出异常值我忘了,也是时有时无,解决的方法百度即可,好像关系到正则
code = pm.group(1)