前言:
今天我們利用requests模塊+django+bs4瀏覽器來實現一個web微信的基本功能,主要實現的功能如下
a、實現返回二維碼
b、實現手機掃碼后二維碼變成變成頭像
c、實現手機點擊登陸成功顯示微信的最近聯系人
d、實現顯示所有的聯系人
e、實現發送消息
下面我們就開始實現上述的功能,在看這篇博客的之前,讀者朋友需要去了解一下長輪詢的知識,因為wei微信的登陸就用到了長輪詢,首先我們先把web登陸的流程梳理一下,然后在實現我們的功能
一、web微信登陸分析
1、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,然后取整,生成的方法如下
t = int(time.time() * 1000)
b、分析這個url的返回值
c、查看網頁的源代碼,看下這個二維碼到底是什么
看下img標簽的src屬性,有沒有注意到,src的這一段字符串oaKKJgJRhA==,是我們返回二維碼的url返回的字符串,所以我們就可以拼接出來二維碼這個圖片的src的地址
https://login.weixin.qq.com/qrcode/oaKKJgJRhA==
2、等待用戶掃碼
這里就用到了一個長輪詢,如果客戶一直沒有掃碼,則會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,則表示等待用戶掃碼
3、web微信顯示頭像分析
手機掃碼后,二維碼變成頭像
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,則證明用戶已經掃碼成功
4、web微信登陸分析
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,則證明登陸成功
5、分析web微信的跳轉url
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后,方便的后面的請求使用
6、web微信顯示最近聯系人流程分析
訪問為跳轉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頁面就可以了
7、web微信顯示全部聯系人
點擊這里,就會顯示全部聯系人
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、這次請求的返回信息就是所有的聯系人
8、web發送消息
發送消息是一個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微信
1、首先看下登陸的html,重點看下我的注釋
2、進入views文件,看下返回二維碼的視圖函數,我們注意到,前面的html需要q_code這個變量來渲染img標簽的src的路徑,顯示二維碼
3、然后后看下等待用戶掃碼的后台邏輯
4、看下前端處理408返回碼的邏輯
5、在來看下用戶掃碼后的后台邏輯
6、在看下前端收到201的返回值處理邏輯,首先修改二維碼的地址為頭像的地址,然后再次發送一次請求,等待用戶點擊確認
7、在看下后端處理用戶點擊登陸的邏輯
8、在看下前端處理200請求的邏輯,會跳轉到一個最近聯系人的頁面
9、我們在看下這個url對應的視圖函數,這個視圖函數是返回最近聯系人的函數,需要攜帶規定的請求體,這些請求體已經被存儲到session中
10、在看下index.html這個頁面,這個數據結構比較簡單,大家自己自己抓包看
11、我們再看下查所有人聯系人
12、看下對應的視圖函數,拼接url,然后把返回值返回給前端
13、前端渲染數據即可
14、在看發送信息的前端頁面
15、再看下后端的處理邏輯,主要是拼接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、方式1,處理不了中文,由於json的問題 # json=data_dict # 2、方式2,解決了json處理不了中文的問題,但是微信用的解碼是不是常見的解碼方式,所以還是處理不了中文 # data = json.dumps( # data_dict, # ensure_ascii=False # ) # 3、方式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、方式1,處理不了中文,由於json的問題 # json=data_dict # 2、方式2,解決了json處理不了中文的問題,但是微信用的解碼是不是常見的解碼方式,所以還是處理不了中文 # data = json.dumps( # data_dict, # ensure_ascii=False # ) # 3、方式3,直接發送二進制文件,就可以解決發送中文的問題 data = bytes(json.dumps( data_dict, ensure_ascii=False ),encoding="utf-8") ) print(rep.text) return HttpResponse("success")
相信如果大家看懂我前面分析web微信的邏輯,看懂應該不成問題。如果有不清楚的,請評論留言,感謝大家關注,謝謝!