访问项目的网页,扫一扫网页上的二维码,就会显示你的微信好友中将你删除的人的列表。java
访问115.29.55.54:8080/WXApi就可使用该项目所说的网页node
在微信中,将你删掉的好友是没法加入你建立的群聊的,而微信网页版也能够建立群聊,因此使用微信网页版的接口能够实现分辨一个好友是否是将你删除了。python
微信在生成二维码以前,会先生成一个UUID,做为一个识别的标记,携带这个UUID访问微信的接口就能够获取到二维码。同时也是查看二维码是否被扫描的一个重要参数。
参数列表以下:git
appid (可写死,wx782c26e4c19acffb)github
fun : newweb
lang : zh-CN (中国地区)json
_ : 时间戳微信
// 参考代码: // Java版本 public String getUUID(){ String url = "https://login.weixin.qq.com/jslogin?appid=%s&fun=new&lang=zh-CN&_=%s"; url = String.format(url, appID,System.currentTimeMillis()); httpGet = new HttpGet(url); try { response = httpClient.execute(httpGet); entity = response.getEntity(); String result = EntityUtils.toString(entity); logger.debug(result); String[] res = result.split(";"); if (res[0].replace("window.QRLogin.code = ", "").equals("200")) { uuid = res[1].replace(" window.QRLogin.uuid = ", "").replace("\"", ""); return uuid; } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; }
# python版本 def getuuid(): global uuid url = 'https://login.weixin.qq.com/jslogin' params = { 'appid': 'wx782c26e4c19acffb', 'fun': 'new', 'lang': 'zh_CN', '_': int(time.time()), } request = urllib2.Request(url=url, data=urllib.urlencode(params)) response = urllib2.urlopen(request) data = response.read() 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 True return False
将uuid放在url中而后使用get请求,会收到一个一张二维码的图片.固然,若是在网页中使用<img>的标签能够直接将这个URL放进去,就能够直接显示一张二维码。
参数列表以下:app
uuid:也就是上面所获取的UUIDdom
//java版本 // 若是忽略注释直接返回获取图片的url放在网页中的<img>的标签下能够直接显示,若是使用注释中的内容会将其下载为本地图片 public String getQR(String uuid) { if (uuid == null || "".equals(uuid)) { return null; } String QRurl = "http://login.weixin.qq.com/qrcode/" + uuid; logger.debug(QRurl); return QRurl; // 同时提供使其变为本地图片的方法 // httpGet = new HttpGet(QRurl); // response = httpClient.execute(httpGet); // entity = response.getEntity(); // InputStream in = entity.getContent(); // //注意这里要对filepath赋值 // OutputStream out = new FileOutputStream(new File("FilePath"+".png")); // byte[] b = new byte[1024]; // int t; // while((t=in.read())!=-1){ // out.write(b, 0, t); // } // out.flush(); // in.close(); // out.close(); }
# Python版本 def showQRImage(): global tip url = 'https://login.weixin.qq.com/qrcode/' + uuid request = urllib2.Request(url=url) response = urllib2.urlopen(request) f = open(QRImagePath, 'wb') f.write(response.read()) f.close() # 保存到本地
登录状态主要是两种,一种是用户已经扫描,一种是用户扫描后在手机端已经点击确认了。这两种状态的获取访问的url是同样的,区别是一个叫作tip的参数,当tip=1的时候,若是没有扫描,服务端会一直等待,若是已经扫描,服务端会返回代买201.当tip=0的时候,若是用户没有点击肯定,那么就会一直等待,直到用户点击肯定后返回200.因此问题来了,若是不改变tip让他一直为1也是能够的,可是就须要不断的轮询,而若是改变tip的话,就能够while的循环。
参数以下:
uuid : 就是以前得到的uuid
_ : 时间戳
tip : 判断是要得到点击状态仍是扫描状态
状态=200时,返回值是redirect_url:该返回值是一个url,访问该url就算是正式的登录。
//java版本 public int waitForLogin(String uuid, int tip) { String urlString = "http://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s"; urlString = String.format(urlString, tip, uuid, System.currentTimeMillis()); httpGet = new HttpGet(urlString); try { response = httpClient.execute(httpGet); String re = EntityUtils.toString(response.getEntity()); String[] result = re.split(";"); logger.debug(re); if (result[0].replace("window.code=", "").equals("201")) { tip = 0; return 201; } else if (result[0].replace("window.code=", "").equals("200")) { redirectUri = (result[1].replace("window.redirect_uri=", "").replace("\"", "") + "&fun=new").trim(); return 200; } else { return 400; } } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return -1; }
# python版本 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())) request = urllib2.Request(url = url) response = urllib2.urlopen(request) data = response.read() regx = r'window.code=(\d+);' pm = re.search(regx, data) code = pm.group(1) if code == '201': #已扫描 print '成功扫描,请在手机上点击确认以登陆' tip = 0 elif code == '200': #已登陆 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('/')] elif code == '408': #超时 pass return code
手机端已经受权经过,上一步会返回一个Redirect_Url,这是一个真正的登录url,使用get方法访问该url会返回一个xml格式的字符串,其中的属性将是接下来动做的重要参数。解析该字符串有以下的属性:
int ret;//返回值为0时表示本次请求成功
String message;//一些信息(好比失败缘由等)
String skey;//后面请求会用到的参数
String wxsid;//同上
long wxuin;// 本人编码
String pass_ticket;//重要!!后面不少请求都会用到这张通行证
int isgrayscale;//不明
代码以下:
//java private boolean login() { String url = redirectUri; httpGet = new HttpGet(url); try { response = httpClient.execute(httpGet); entity = response.getEntity(); String data = EntityUtils.toString(entity); logger.debug(data); loginResponse = CommonUtil.parseLoginResult(data); baseRequest = new BaseRequest(loginResponse.getWxuin(), loginResponse.getWxsid(), loginResponse.getSkey(), loginResponse.getDeviceID()); return true; } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return false; }
#python版本 def login(): global skey, wxsid, wxuin, pass_ticket, BaseRequest request = urllib2.Request(url = redirect_uri) response = urllib2.urlopen(request) data = response.read() 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 if skey == '' or wxsid == '' or wxuin == '' or pass_ticket == '': return False BaseRequest = { 'Uin': int(wxuin), 'Sid': wxsid, 'Skey': skey, 'DeviceID': deviceId, } return True
该方法无关紧要,做用主要是初始化几个联系人,多是最近联系人仍是怎样,而且能得到的是登录人的信息。若是不须要获取这些东西就能够跳过这一步。该方法是post方法,但在url中也能够放几个值
主要参数:
url中:
pass_ticket
skey 这两个参数都是login时的返回值之一
r 时间戳
post 文中携带:BaseRequst=Json格式的BaseRequest,BaseRequest类中有以下参数:、
long Uin;
String Sid;
String Skey;
String DeviceID; DeviceID是一串e开头的随机数,随便填就能够。
//java private void initWX() { String url = String.format("http://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?pass_ticket=%s&skey=%s&r=%s", loginResponse.getPass_ticket(), loginResponse.getSkey(), System.currentTimeMillis()); InitRequestJson initRequestJson = new InitRequestJson(baseRequest);//Java中包含了BaseRequest的包装类 String re = getResponse(url, gson.toJson(initRequestJson));//这是本身写的一个公有方法,能够直接看源码 InitResponseJson initResponseJson = gson.fromJson(re, InitResponseJson.class); mine = initResponseJson.getUser();// 获取当前用户信息 }
def webwxinit(): url = base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (pass_ticket, skey, int(time.time())) params = { 'BaseRequest': json.dumps(BaseRequest) } request = urllib2.Request(url=url, data=json.dumps(params)) request.add_header('ContentType', 'application/json; charset=UTF-8') response = urllib2.urlopen(request) data = response.read() global ContactList, My dic = json.loads(data) ContactList = dic['ContactList'] My = dic['User'] ErrMsg = dic['BaseResponse']['ErrMsg'] if len(ErrMsg) > 0: print ErrMsg Ret = dic['BaseResponse']['Ret'] if Ret != 0: return False return True
因为登录成功后后面部分基本就是调用接口了,难点基本没有,能够直接看源码,我在这里贴上操做步骤
获取全部的用户
经过post方法访问一个url(源码中能够看),就能够获取全部的用户列表。
建立聊天室
注意一次最多40人不然会出现问题
删除聊天室的成员
为聊天室添加成员
微信会返回该成员的一个状态,若是状态等于4,那么添加失败,就能够判断该用户已经删除了登录用户。
获得uuid,并将其包装直接插入<img>标签中就能够在网页中显示该二维码
使用AJAX请求,请求waitforlogging()方法,当返回值为200时成功,此时遍历该用户每个好友,判断其是否删除了该用户。
显示