利用web微信获取人员列表,得到单删数据

#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)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值