前言: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微信的邏輯,看懂應該不成問題。若是有不清楚的,請評論留言,感謝你們關注,謝謝!