前言:html
今天咱们利用requests模块+django+bs4浏览器来实现一个web微信的基本功能,主要实现的功能以下前端
a、实现返回二维码python
b、实现手机扫码后二维码变成变成头像web
c、实现手机点击登录成功显示微信的最近联系人django
d、实现显示全部的联系人json
e、实现发送消息后端
下面咱们就开始实现上述的功能,在看这篇博客的以前,读者朋友须要去了解一下长轮询的知识,由于wei微信的登录就用到了长轮询,首先咱们先把web登录的流程梳理一下,而后在实现咱们的功能浏览器
a、首先拿到url,这个请求是get请求微信
https://login.wx2.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage&fun=new&lang=zh_&_=1555510256420
这个url很好构建,只有1555510256420这个参数须要咱们认为生成,其余他就是时间戳*1000,而后取整,生成的方法以下cookie
t = int(time.time() * 1000)
b、分析这个url的返回值
c、查看网页的源代码,看下这个二维码究竟是什么
看下img标签的src属性,有没有注意到,src的这一段字符串oaKKJgJRhA==,是咱们返回二维码的url返回的字符串,因此咱们就能够拼接出来二维码这个图片的src的地址
https://login.weixin.qq.com/qrcode/oaKKJgJRhA==
这里就用到了一个长轮询,若是客户一直没有扫码,则会hang住,等待客户的扫码
a、先来分析一下url
https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=oeoQNe1EiA==&tip=1&r=-732967182&_=1555511127069
这个url有2个地方须要咱们来构建
第一个参数就上一步返回的字符串,第二个参数就是一个仍是一个时间戳
b、在来看下这个url返回了什么
只有一个状态码408
结论:若是url的返回的code为408,则表示等待用户扫码
手机扫码后,二维码变成头像
a、先来分析url
https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=Qfn4ldhuNQ==&tip=1&r=-732688468&_=1555510848123
这个url和上面的url同样,因此咱们知道,第一步返回的字符串很是重要,因此咱们要把这段字符串放在session中
b、在来看下url的返回值
这里返回了一段字符串,code为201,后面那一段字符串是头像的地址
c、咱们在来看下html中的img标签的src的地址
结论:返回201,则证实用户已经扫码成功
a、首先url仍是以前的url,这里就不作分析
https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=YacjFJrAfA==&tip=0&r=-733737113&_=1555511755717
b、看下此次请求的返回值
这里有一个跳转的url,也就是当咱们点击登录后,会跳转到这个url
这里还有一个返回码是200
结论:状态码返回200,则证实登录成功
a、分析一下此次请求的返回值
<error><ret>0</ret><message></message><skey>@crypt_90b16895_59f7cbfc1c217310b90558af662ea9c7</skey><wxsid>VP1xxDiAiU5Xz8gN</wxsid><wxuin>1632086000</wxuin><pass_ticket>w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy</pass_ticket><isgrayscale>1</isgrayscale></error>
这个返回值很是重要,咱们后面登录后须要作的操做都须要这里的信息。因此这个信息咱们也要组合一下放在session后,方便的后面的请求使用
访问为跳转url后,拿到返回值信息,web微信又会发送一个post请求,获取最近联系人信息
a、先看下url,这里url就须要用到上面跳转url的返回值的信息来拼接
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-733594626&pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy
b、这个请求的返回值就是最近联系人
c、咱们就能够把这些数据渲染到html页面就能够了
点击这里,就会显示所有联系人
a、分析一下url
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy&r=1555511910553&seq=0&skey=@crypt_90b16895_59f7cbfc1c217310b90558af662ea9c7
咱们彻底能够根据session中的数据拼接这个字符串
b、此次请求的返回信息就是全部的联系人
发送消息是一个post的请求
a、先来分析url
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=w%2BIW73Y3XXFLqfA%2BBworrfgKu5aRlyXW%2F57wtMPYwrP%2BWnDW3ieWQ8jBmUUTbawy
咱们能够经过session中的数据拼接出这个url
b、在来看下此次post携带的请求体,咱们彻底能够经过session中的数据拼接出这个请求体
c、分析msg这个信息
第一条是时间戳
第二条是发送的内容
第三条发送者的微信id
第四条也是时间戳
第五条是接受者的微信id
经过上面的分析,我相信你们对web微信的请求已经很是了解了,下面咱们使用requests+bs4+djangon来实现一个建议的web微信
一、首先看下登录的html,重点看下个人注释
二、进入views文件,看下返回二维码的视图函数,咱们注意到,前面的html须要q_code这个变量来渲染img标签的src的路径,显示二维码
三、而后后看下等待用户扫码的后台逻辑
四、看下前端处理408返回码的逻辑
五、在来看下用户扫码后的后台逻辑
六、在看下前端收到201的返回值处理逻辑,首先修改二维码的地址为头像的地址,而后再次发送一次请求,等待用户点击确认
七、在看下后端处理用户点击登录的逻辑
八、在看下前端处理200请求的逻辑,会跳转到一个最近联系人的页面
九、咱们在看下这个url对应的视图函数,这个视图函数是返回最近联系人的函数,须要携带规定的请求体,这些请求体已经被存储到session中
十、在看下index.html这个页面,这个数据结构比较简单,你们本身本身抓包看
十一、咱们再看下查全部人联系人
十二、看下对应的视图函数,拼接url,而后把返回值返回给前端
1三、前端渲染数据便可
1四、在看发送信息的前端页面
1五、再看下后端的处理逻辑,主要是拼接url和处理中文的信息
def sendmsg(request): if request.method == "GET": return render(request,"sendmsg.html") else: from_user = request.POST.get("from_user") to_user = request.POST.get("to_user") content = request.POST.get("content") data_dict = { "BaseRequest":{ "DeviceID":"e461335461567419", "Sid":request.session["temp_dict"].get("wxsid"), "Skey":request.session["temp_dict"].get("skey"), "Uin":request.session["temp_dict"].get("wxuin") }, "Msg":{ "ClientMsgId":int(time.time() * 1000), "Content":content, "FromUserName":from_user, "LocalID":int(time.time() * 1000), "ToUserName":to_user, "Type":1 }, "Scene":0 } rep = requests.post( url= "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={p}".format(p = request.session["temp_dict"]["pass_ticket"]), # 一、方式1,处理不了中文,因为json的问题 # json=data_dict # 二、方式2,解决了json处理不了中文的问题,可是微信用的解码是否是常见的解码方式,因此仍是处理不了中文 # data = json.dumps( # data_dict, # ensure_ascii=False # ) # 三、方式3,直接发送二进制文件,就能够解决发送中文的问题 data = bytes(json.dumps( data_dict, ensure_ascii=False ),encoding="utf-8") ) print(rep.text) return HttpResponse("success")
from django.shortcuts import render from django.shortcuts import HttpResponse from django.shortcuts import redirect import requests import re # Create your views here. import time def login(request): if request.method.lower() == "get": t = int(time.time() * 1000) url = "https://login.wx.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_={t}" res = requests.get(url=url) # window.QRLogin.code = 200; # window.QRLogin.uuid = "oc86pbX-hQ=="; re_obj = re.compile('= "(.*==)";$') q_code = re_obj.findall(res.text)[0] request.session["q_code"] = q_code return render(request,"login.html",locals()) import json import re from bs4 import BeautifulSoup # BeautifulSoup还能够处理xml文档 def checklogin(request): if request.method.lower() == "get": res_dict = {"code":408,"img":None,"url":None} code = request.session["q_code"] t = int(time.time() * 1000) url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={code}&tip=0&r=-131537270&_={t}".format(code = code,t = t) rep = requests.get(url=url) if "window.code=408;" in rep.text: return HttpResponse(json.dumps(res_dict)) elif "window.code=201;" in rep.text: # 扫码成功 obj = re.compile("window.userAvatar = '(.*)';") src = obj.findall(rep.text)[0] res_dict["code"] = 201 res_dict["img"] = src return HttpResponse(json.dumps(res_dict)) elif "window.code=200;" in rep.text: # 肯定登录 obj = re.compile('window.redirect_uri="(.*)";') url = obj.findall(rep.text)[0] res_dict["code"] = 200 res_dict["url"] = url new = requests.get(url = url + "&fun=new&version=v2&lang=zh_CN") script_obj = BeautifulSoup(new.text,"html.parser") temp_dict = {} for tag in script_obj.find(name="error"): temp_dict[tag.name] = tag.text request.session["temp_dict"] = temp_dict request.session["cookies"] = new.cookies.get_dict() return HttpResponse(json.dumps(res_dict)) else: pass return HttpResponse("haha") def index(request): url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-221192329&pass_ticket={t}".format(t = request.session["temp_dict"].get("pass_ticket")) init = requests.post( url=url, json={ "BaseRequest":{ "DeviceID":"e701447882725714", "Sid":request.session["temp_dict"].get("wxsid"), "Skey":request.session["temp_dict"].get("skey"), "Uin":request.session["temp_dict"].get("wxuin") } } ) init.encoding = "utf-8" init_user_dict = init.json() return render(request,"index.html",locals()) def contact(request): t = int(time.time() * 1000) rep = requests.get( url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket={p}&r={t}&seq=0&skey={s}".format(t = t, p = request.session["temp_dict"]["pass_ticket"], s = request.session["temp_dict"]["skey"]), cookies = request.session["cookies"] ) rep.encoding = "utf-8" user_list= rep.json() return render(request,"contact.html",locals()) def avator(request): # print(request.GET.get("prev")) # print(request.GET.get("username")) # print(request.GET.get("skey")) url = "https://wx2.qq.com{p}&username={u}&skey={s}".format(p = request.GET.get("prev"), u = request.GET.get("username"), s = request.GET.get("skey") ) img = requests.get( url = url, cookies = request.session["cookies"] ) print(url) return img.content def sendmsg(request): if request.method == "GET": return render(request,"sendmsg.html") else: from_user = request.POST.get("from_user") to_user = request.POST.get("to_user") content = request.POST.get("content") data_dict = { "BaseRequest":{ "DeviceID":"e461335461567419", "Sid":request.session["temp_dict"].get("wxsid"), "Skey":request.session["temp_dict"].get("skey"), "Uin":request.session["temp_dict"].get("wxuin") }, "Msg":{ "ClientMsgId":int(time.time() * 1000), "Content":content, "FromUserName":from_user, "LocalID":int(time.time() * 1000), "ToUserName":to_user, "Type":1 }, "Scene":0 } rep = requests.post( url= "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket={p}".format(p = request.session["temp_dict"]["pass_ticket"]), # 一、方式1,处理不了中文,因为json的问题 # json=data_dict # 二、方式2,解决了json处理不了中文的问题,可是微信用的解码是否是常见的解码方式,因此仍是处理不了中文 # data = json.dumps( # data_dict, # ensure_ascii=False # ) # 三、方式3,直接发送二进制文件,就能够解决发送中文的问题 data = bytes(json.dumps( data_dict, ensure_ascii=False ),encoding="utf-8") ) print(rep.text) return HttpResponse("success")
相信若是你们看懂我前面分析web微信的逻辑,看懂应该不成问题。若是有不清楚的,请评论留言,感谢你们关注,谢谢!