一:轮询,长轮询,WebSocket了解
轮询:
在前端,设置时间内,一直向后端发送请求。
例如:使用setInterval方法设置定时器,一秒向后端发送一次请求,去主动获取数据,进行更新
由于前端一直请求,后端压力太大。而且当没有数据更新,前端一直去请求,太浪费了,没必要。
代码简单
长轮询:
在轮询的基础上,加以改造。Http请求到来,若是不主动close或者return,则连接会一直存在。但是不要让这个时间太长,会占用太多资源
例如:当前端发送请求,后端拿到后,不去关闭,而是等待一段时间,在这段时间内若是有数据到达,立刻返回,否则直到等待时间结束。
然后返回给前端,前端马上又发起一次请求......
消息是实时获取。
WebSocket:
http是单向请求,客户端去服务端获取数据。服务端不能主动推送消息。
而websocket类似于socket,可以实现双向发送,
实现当数据更新,可以主动推送
二:web微信流程介绍
三:微信登录开发
from django.shortcuts import render,HttpResponse
from bs4 import BeautifulSoup
import requests
import time,re,json CTIME = None #用于保存全局时间戳
QCODE = None #当我们访问二维码时,会产生一个UUID,我们将其存放为全局
TIP = 1 #url中的一个参数tip,当其为1:代表我们还没有扫描二维码,当其为0:扫描了二维码
登录视图login,用于显示二维码
def login(request):
global CTIME
global QCODE
CTIME = int(time.time()) data = {
'appid':'wx782c26e4c19acffb',
'fun':'new',
'lang':'zh_CN',
'_':CTIME
} response = requests.get(
url="https://login.wx.qq.com/jslogin",
params=data
) pat_res = re.findall('uuid = "(.*)";',response.text) #正则匹配UUID
QCODE = pat_res[] return render(request,"login.html",{'qcode':QCODE})
check_login用于检测登录状态:408未扫描,201扫描二维码但是未登录,200点击登录
def check_login(request):
global TIP
ret = {'code':,'data':None}
data = {
'loginicon':"true",
'uuid':QCODE,
'tip':TIP,
'r':'-577317906',
'_':int(time.time())
}
r1 = requests.get(
url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
params=data
) if 'window.code=408' in r1.text:
print("无人扫描")
return HttpResponse(json.dumps(ret))
elif 'window.code=201' in r1.text:
ret['code'] =
pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[]
ret['data'] = pat_ret
TIP =
return HttpResponse(json.dumps(ret))
elif 'window.code=200;' in r1.text:
ret['code'] =
redirect_url = re.findall('window.redirect_uri="(.*)";',r1.text)[]
reponse = requests.get(
url=redirect_url+"&fun=new&version=v2" #url不够完整,需要我们完善
)
# print(reponse.text) #<error><ret></ret><message></message><skey>@crypt_7358fe11_af06754907ad9c216768337d80cf0ce7</skey><wxsid>icUySQoySDi2OZFK</wxsid><wxuin></wxuin><pass_ticket>IWScm1SE%2BGQ%2BNEaghUBCxbF3xPJSzqXUGTO6BYh3TBEGlw8Wa7qETkA9EEAUudYU</pass_ticket><isgrayscale></isgrayscale></error>
soup = BeautifulSoup(reponse.text,"lxml")
info_dict = {}
for tag in soup.find("error").children:
info_dict[tag.name]=tag.get_text() get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket='+info_dict['pass_ticket']
get_user_info_form = {
'BaseRequest':
{
'DeviceID':"e055319847811019",
'Sid':info_dict['wxsid'],
'Skey':info_dict['skey'],
'Uin':info_dict['wxuin']
}
} reponse2 = requests.post( #获取的是用户信息,最近联系人,公众号,自己信息
url=get_user_info_url,
json=get_user_info_form, #注意这里使用的是json,post不允许传送字典
) reponse2.encoding = "utf-8"
print(reponse2.text) return HttpResponse("OK")
'''
新请求 GET 获取跳转地址redirect_uri
https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?
loginicon=true
&uuid=QfsKELYXow==
&tip=
&r=-
&_=
---------------------------------------------------------
window.code=;
window.redirect_uri="
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?
ticket=ASWg1dxC1oWVbJtZH8V-HhlB@qrticket_0
&uuid=QfsKELYXow==
&lang=zh_CN
&scan="; 新请求 GET 获取凭证pass_ticket 服务端开始设置了cookie,说明在后面的请求中需要携带cookie
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?
ticket=ASWg1dxC1oWVbJtZH8V-HhlB@qrticket_0
&uuid=QfsKELYXow==
&lang=zh_CN
&scan=
&fun=new
&version=v2
-----------------------------------------------------------------
<error>
<ret></ret>
<message></message>
<skey>@crypt_7358fe11_ea821d506c39f7d75a3e83b4233caab4</skey>
<wxsid>qUJZlkBIWQ0130QI</wxsid>
<wxuin></wxuin>
<pass_ticket>xNiKeCBgFkMBfEK8oOK3Gp9qj%2F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ</pass_ticket>
<isgrayscale></isgrayscale>
</error> 新请求:获取用户所有信息,最近联系人和公众号 POST 需要携带数据,数据来自于上面凭证中
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?
r=-
&pass_ticket=xNiKeCBgFkMBfEK8oOK3Gp9qj%252F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ 数据
{
BaseRequest:
{
DeviceID:"e055319847811019"
Sid:"pY7nfHplUAsBOINz"
Skey:"@crypt_7358fe11_e0ae163bd19650bea336df66837e9f7a"
Uin:""
}
}
--------------------------------------------------------------------
{
"BaseResponse": {
"Ret": ,
"ErrMsg": ""
}
,
"Count": ,
"ContactList": [{
"Uin": ,
"UserName": "filehelper",
"NickName": "æ–‡ä»¶ä¼ è¾“åŠ©æ‰‹",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=660872310&username=filehelper&skey=@crypt_7358fe11_ea821d506c39f7d75a3e83b4233caab4",
"ContactFlag": ,
"MemberCount": ,
"MemberList": [],
"RemarkName": "",
"HideInputBarFlag": ,
"Sex": ,
"Signature": "",
"VerifyFlag": ,
"OwnerUin": ,
"PYInitial": "WJCSZS",
"PYQuanPin": "wenjianchuanshuzhushou",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": ,
"AppAccountFlag": ,
"Statues": ,
"AttrStatus": ,
"Province": "",
"City": "",
"Alias": "",
"SnsFlag": ,
"UniFriend": ,
"DisplayName": "",
"ChatRoomId": ,
"KeyWord": "fil",
"EncryChatRoomId": "",
"IsOwner":
},还有其他的]
} 新请求
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?
r=-
&pass_ticket=xNiKeCBgFkMBfEK8oOK3Gp9qj%252F1HfLpcfPrDwGv3A4nltKskVqoxkECrVYEN9eJJ 新请求 GET 获取所有联系人和公众号
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?
lang=zh_CN
&pass_ticket=gWbCT8vTjFeFKXDvfJZ6DtMtHo5d8zzhtLgLoybILn7eeTNSMI4BErA7e9otuPXQ
&r=
&seq=
&skey=@crypt_7358fe11_9dc260b8cffb962a3e475ca50e7813c9
-----------------------------------------------------------------------------
{
"BaseResponse": {
"Ret": ,
"ErrMsg": ""
}
,
"MemberCount": ,
"MemberList": [{
"Uin": ,
"UserName": "@39ef4d4197e9a7388e41fc9de150b3e28bf125082f1e442822814dec4803c6a0",
"NickName": "å®é™è‡´è¿œ",
"HeadImgUrl": "/cgi-bin/mmwebwx-bin/webwxgeticon?seq=0&username=@39ef4d4197e9a7388e41fc9de150b3e28bf125082f1e442822814dec4803c6a0&skey=@crypt_7358fe11_9dc260b8cffb962a3e475ca50e7813c9",
"ContactFlag": ,
"MemberCount": ,
"MemberList": [],
"RemarkName": "",
"HideInputBarFlag": ,
"Sex": ,
"Signature": "凶巴巴呛è´è´",
"VerifyFlag": ,
"OwnerUin": ,
"PYInitial": "NJZY",
"PYQuanPin": "ningjingzhiyuan",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"StarFriend": ,
"AppAccountFlag": ,
"Statues": ,
"AttrStatus": ,
"Province": "æ²³å—",
"City": "郑州",
"Alias": "",
"SnsFlag": ,
"UniFriend": ,
"DisplayName": "",
"ChatRoomId": ,
"KeyWord": "",
"EncryChatRoomId": "",
"IsOwner":
},
还有其他
]
'''
各个url详细请求
前端代码:显示二维码和头像
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img id="qrcode" style="width: 340px;height: 340px;" src="https://login.weixin.qq.com/qrcode/{{ qcode }}" alt="">
</body>
</html>
<script src="/static/jquery.js"></script>
<script>
$(function(){
checkLogin();
}) function checkLogin() {
$.ajax({
url:'/check-login.html',
type:'GET',
dataType:"json",
success:function(data){
console.log(data.code);
if (data.code==){
checkLogin();
}
else if(data.code==){
$("#qrcode").attr('src',data.data)
checkLogin();
}
}
})
}
</script>
测试返回的最近联系人和公众号信息
user_dict = {} for item in user_dict.items():
print(item) for item in user_dict['ContactList']: #最近联系人
print(item['PYQuanPin'],item['NickName']) for item in user_dict['MPSubscribeMsgList']: #公众号和推送消息
print(item['UserName'],item['NickName'])
for item2 in item['MPArticleList']:
print(item2['Title'],item2['Cover'],item2['Digest'],item2['Url'])
最近联系人和公众号
('ClientVersion', )
('GrayScale', )
('Count', )
('SystemTime', )
('MPSubscribeMsgList:公众号列表,含有文章推送等信息', [{'UserName': '@393d71e59f81ac2feca148e8e269c0df', 'MPArticleList': [{'Title': '', 'Digest': '', 'Url': '', 'Cover': '图片'}, ], 'MPArticleCount': , 'Time': , 'NickName': '人工智能头条'},])
('ChatSet', 'filehelper,@@7c7137978e7349eac97453fa2adc290df295eaba3e7981e07ff85111f94a403c,weixin,@0fdf14d27dc0b2d34d013329ec498aae6284dbc340bdfbd8741227a72b1b3fa4,@393d71e59f81ac2feca148e8e269c0df,@@4d7d0c68e8445a6d69a5e3a2415c57c8f46858724cfdef8618b5790094e5de37,@@6b45638d8a8394a5bea103bd55ef49ce69dad73b8eda94dd5f63a126ec0e6ee4,@@d6c45082c0686e0cef729f3cb20db704b381b2aef67fe0a8a82151869220c8ef,@@03e7d8c59c30bb81dc0f2dc683b8e7a6f4a707f2aac6655c6e5036c349a96fe3,@02bf3be3c826bc38d4461d3ee52704e8,')
('MPSubscribeMsgCount:最近推送的公众号数目', )
('BaseResponse', {'ErrMsg': '', 'Ret': })
('SKey', '@crypt_7358fe11_08012eadffc70f5c3189f802236830be')
('ClickReportInterval', )
('InviteStartCount', )
('User:用户自己的信息', {'VerifyFlag': , 'HeadImgFlag': , 'Uin': , 'NickName': '宁静致远', 'AppAccountFlag': , 'UserName': '@c959c389ab390d9f71d3f528f5a4ee1e81d6c8cd4aaf48d8b1f0077073660c5c', 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=1753775271&username=@c959c389ab390d9f71d3f528f5a4ee1e81d6c8cd4aaf48d8b1f0077073660c5c&skey=@crypt_7358fe11_08012eadffc70f5c3189f802236830be', 'ContactFlag': , 'RemarkPYInitial': '', 'SnsFlag': , 'PYQuanPin': '', 'WebWxPluginSwitch': , 'HideInputBarFlag': , 'RemarkPYQuanPin': '', 'Signature': '凶巴巴呛贝贝', 'Sex': , 'StarFriend': , 'PYInitial': '', 'RemarkName': ''})
('ContactList:最近联系人信息', [
{'VerifyFlag': , 'Uin': , 'Signature': '', 'AppAccountFlag': , 'HeadImgUrl': '/cgi-bin/mmwebwx-bin/webwxgeticon?seq=660872310&username=filehelper&skey=@crypt_7358fe11_08012eadffc70f5c3189f802236830be', 'PYInitial': 'WJCSZS', 'Province': '', 'PYQuanPin': 'wenjianchuanshuzhushou', 'DisplayName': '', 'RemarkName': '', 'IsOwner': , 'Sex': , 'EncryChatRoomId': '', 'KeyWord': 'fil', 'City': '', 'ChatRoomId': , 'RemarkPYQuanPin': '', 'Alias': '', 'UniFriend': , 'UserName': 'filehelper', 'MemberCount': , 'ContactFlag': , 'RemarkPYInitial': '', 'Statues': , 'AttrStatus': , 'SnsFlag': , 'HideInputBarFlag': , 'NickName': '文件传输助手', 'OwnerUin': , 'StarFriend': , 'MemberList': []},])
('SyncKey', {'List': [{'Key': , 'Val': }, {'Key': , 'Val': }, {'Key': , 'Val': }, {'Key': , 'Val': }], 'Count': })
相关数据打印(格式)
四:显示最近联系人和公众号
视图所有代码:对于上面是有所修改的
from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json CTIME = None
QCODE = None
TIP =
TICKET_DICT = {} #保存凭证信息
ALL_COOKIE_DICT = {} # Create your views here.
def login(request):
global CTIME
global QCODE
CTIME = int(time.time()*) data = {
'appid':'wx782c26e4c19acffb',
'fun':'new',
'lang':'zh_CN',
'_':CTIME
} response = requests.get(
url="https://login.wx.qq.com/jslogin",
params=data
) pat_res = re.findall('uuid = "(.*)";',response.text)
QCODE = pat_res[] return render(request,"login.html",{'qcode':QCODE}) def check_login(request):
global TIP
ret = {'code':,'data':None}
data = {
'loginicon':"true",
'uuid':QCODE,
'tip':TIP,
'r':'-577317906',
'_':int(time.time())
}
r1 = requests.get(
url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
params=data
) if 'window.code=408' in r1.text:
print("无人扫描")
return HttpResponse(json.dumps(ret))
elif 'window.code=201' in r1.text:
ret['code'] =
pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[]
ret['data'] = pat_ret
TIP =
return HttpResponse(json.dumps(ret))
elif 'window.code=200;' in r1.text:
ret['code'] =
redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[]
reponse = requests.get( #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
url=redirect_url + "&fun=new&version=v2" # url不够完整,需要我们完善
)
ALL_COOKIE_DICT.update(reponse.cookies) soup = BeautifulSoup(reponse.text, "lxml")
info_dict = {}
for tag in soup.find("error").children:
info_dict[tag.name] = tag.get_text() global TICKET_DICT
TICKET_DICT.update(info_dict) ret['code']= return HttpResponse(json.dumps(ret)) def user(request):
get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
'pass_ticket']
get_user_info_form = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
}
} reponse2 = requests.post( # 获取的是用户信息,几个联系人,公众号,自己信息
url=get_user_info_url,
json=get_user_info_form, # 注意这里使用的是json,post不允许传送字典
)
ALL_COOKIE_DICT.update(reponse2.cookies) reponse2.encoding = "utf-8" user_info_dict = json.loads(reponse2.text) # 获取的是用户信息,几个联系人,公众号,自己信息 return render(request, "user.html", {'user_info_dict': user_info_dict})
views修改后的代码,主要是将凭证放入全局字典
视图方法user去获取最近联系人
注意:我们将上面的凭证保存到了全局变量中TICKET_DICT方便查询使用 def user(request):
get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
'pass_ticket']
get_user_info_form = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
}
} reponse2 = requests.post( # 获取的是用户信息,几个联系人,公众号,自己信息
url=get_user_info_url,
json=get_user_info_form, # 注意这里使用的是json,post不允许传送字典
)
ALL_COOKIE_DICT.update(reponse2.cookies) reponse2.encoding = "utf-8" user_info_dict = json.loads(reponse2.text) # 获取的是用户信息,几个联系人,公众号,自己信息 return render(request, "user.html", {'user_info_dict': user_info_dict})
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>最近联系人</h3>
<ul>
{% for item in user_info_dict.ContactList %}
<li>{{ item.NickName }}</li>
{% endfor %}
</ul>
<a href="/contact-list.html">获取更多联系人</a>
</div>
<div>
<h3>微信公众号</h3>
<div>
{% for item in user_info_dict.MPSubscribeMsgList %}
<h4>{{ item.NickName }}</h4>
<ul>
{% for item2 in item.MPArticleList %}
<li>
<a href="{{ item2.Url }}">
{{ item2.Title }}
</a>
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
</div>
</body>
</html>
五:显示所有联系人
视图所有的修改:主要在设置一个全局字典存放网站cookie,注意这里是需要携带cookie的,而cookie是在我们点击登录后,服务器开始设置的,我们需要去获取自那时以后的所有cookie
from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json CTIME = None
QCODE = None
TIP =
TICKET_DICT = {} #保存凭证信息
ALL_COOKIE_DICT = {} # Create your views here.
def login(request):
global CTIME
global QCODE
CTIME = int(time.time()*) data = {
'appid':'wx782c26e4c19acffb',
'fun':'new',
'lang':'zh_CN',
'_':CTIME
} response = requests.get(
url="https://login.wx.qq.com/jslogin",
params=data
) pat_res = re.findall('uuid = "(.*)";',response.text)
QCODE = pat_res[] return render(request,"login.html",{'qcode':QCODE}) def check_login(request):
global TIP
ret = {'code':,'data':None}
data = {
'loginicon':"true",
'uuid':QCODE,
'tip':TIP,
'r':'-577317906',
'_':int(time.time())
}
r1 = requests.get(
url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
params=data
) if 'window.code=408' in r1.text:
print("无人扫描")
return HttpResponse(json.dumps(ret))
elif 'window.code=201' in r1.text:
ret['code'] =
pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[]
ret['data'] = pat_ret
TIP =
return HttpResponse(json.dumps(ret))
elif 'window.code=200;' in r1.text:
ret['code'] =
redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[]
reponse = requests.get( #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
url=redirect_url + "&fun=new&version=v2" # url不够完整,需要我们完善
)
ALL_COOKIE_DICT.update(reponse.cookies) soup = BeautifulSoup(reponse.text, "lxml")
info_dict = {}
for tag in soup.find("error").children:
info_dict[tag.name] = tag.get_text() global TICKET_DICT
TICKET_DICT.update(info_dict) ret['code']= return HttpResponse(json.dumps(ret)) def user(request):
get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
'pass_ticket']
get_user_info_form = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
}
} reponse2 = requests.post( # 获取的是用户信息,几个联系人,公众号,自己信息
url=get_user_info_url,
json=get_user_info_form, # 注意这里使用的是json,post不允许传送字典
)
ALL_COOKIE_DICT.update(reponse2.cookies) reponse2.encoding = "utf-8" user_info_dict = json.loads(reponse2.text) # 获取的是用户信息,几个联系人,公众号,自己信息 return render(request, "user.html", {'user_info_dict': user_info_dict}) def contact_list(request):
get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*),TICKET_DICT['skey']) reponse = requests.get(
url=get_all_user_url, #这里需要用到cookie
cookies=ALL_COOKIE_DICT
) reponse.encoding = "utf-8"
contact_info_list = json.loads(reponse.text) return render(request,"contact_info.html",{'contact_info_list':contact_info_list})
所有视图代码,主要修改在ALL_COOKIE_DICT 存放cookie
不携带cookie情况:
视图方法:contact_list
def contact_list(request):
get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*),TICKET_DICT['skey']) reponse = requests.get(
url=get_all_user_url, #这里需要用到cookie
cookies=ALL_COOKIE_DICT
) reponse.encoding = "utf-8"
contact_info_list = json.loads(reponse.text) return render(request,"contact_info.html",{'contact_info_list':contact_info_list})
前端代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>全部联系人列表</h3>
<ul>
{% for item in contact_info_list.MemberList %}
<li>{{ item.NickName }}</li>
{% endfor %}
</ul>
</div>
</body>
</html>
六:模拟发送信息
发送信息的url:
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=l8yQcPtEelgrNY6fSnMf72i%252BoP10LCLTdmjnFgEQOCK9n7401krRfKnc0xMbNweJ
POST传递数据内容:
{
"BaseRequest": #这里数据存放在全局凭证中
{"Uin":,
"Sid":"xU10r+19IxmPU8Fb",
"Skey":"@crypt_7358fe11_5fc69b570e562f35f5e96aa1039d83aa",
"DeviceID":"e308142734343946"
},
"Msg": #发送的数据信息
{"Type":, #文本信息
"Content":"参数", #发送的数据
"FromUserName":"@c63e475396e438ef81d9825832217c06e4cc302269db05b7eae7e2980de2d56d", #我的username
"ToUserName":"@94bcc0a92c726c0e2639ffa59618549d222bee0107150515cf247dc8d45f8144", #发给谁username
"LocalID":"", #和时间戳一致
"ClientMsgId":"" #时间戳
},
"Scene":
}
{"BaseRequest":
{"Uin":,
"Sid":"xU10r+19IxmPU8Fb",
"Skey":"@crypt_7358fe11_5fc69b570e562f35f5e96aa1039d83aa",
"DeviceID":"e149192355196085" #可变的设备ID
},
"Msg":
{"Type":,
"Content":"哈哈哈", #发送内容改变了
"FromUserName":"@c63e475396e438ef81d9825832217c06e4cc302269db05b7eae7e2980de2d56d",
"ToUserName":"@94bcc0a92c726c0e2639ffa59618549d222bee0107150515cf247dc8d45f8144",
"LocalID":"", #同时间戳一致
"ClientMsgId":"" #时间戳改变了
},
"Scene":
}
修改前端contact_info.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/nifty.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
<link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
<script src="/static/js/jquery-2.2.4.min.js"></script>
<script>
$(function(){
$(".list-group-item").click(function(){
$(".list-unstyled").empty();
$(this).siblings().removeClass("active");
$(this).addClass("active");
var NickName = $(this).first().text().trim();
$(".panel-title").text(NickName);
})
})
</script>
<script src="/static/plugins/pace/pace.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/nifty.min.js"></script>
<script src="/static/js/demo/nifty-demo.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
<script src="/static/plugins/gauge-js/gauge.min.js"></script>
<script src="/static/plugins/skycons/skycons.min.js"></script>
<script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
<script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect aside-bright mainnav-sm aside-right aside-in">
<div class="boxed">
<div id="content-container">
<div class="row">
<div class="col-md-8 col-lg-8 col-sm-8"> <!--Chat widget-->
<!--===================================================-->
<div class="panel" style="height: 640px">
<!--Heading-->
<div class="panel-heading">
<h3 class="panel-title">Chat</h3>
</div> <!--Widget body-->
<div style="height:510px;padding-top:0px;" class="widget-body">
<div class="nano">
<div class="nano-content pad-all">
<ul class="list-unstyled media-block">
<li class="mar-btm">
<div class="media-left">
<img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor">
<div class="speech">
<a href="#" class="media-heading">Aaron Chavez</a>
<p>Hello Lucy, how can I help you today ?</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i>:23AM
</p>
</div>
</div>
</li>
<li class="mar-btm">
<div class="media-right">
<img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor speech-right">
<div class="speech">
<a href="#" class="media-heading">Lucy Doe</a>
<p>Hi, I want to buy a new shoes.</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i> :23AM
</p>
</div>
</div>
</li>
</ul>
</div>
</div> <!--Widget footer-->
<div class="panel-footer" style="height: 90px;">
<div class="row">
<div class="col-xs-9">
<input type="text" placeholder="Enter your text" class="form-control chat-input">
</div>
<div class="col-xs-3">
<button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
</div>
</div>
</div>
</div>
</div>
<!--===================================================-->
<!--Chat widget--> </div>
<div class="col-md-4 col-lg-4 col-sm-4">
<aside id="aside-container">
<div id="aside">
<div class="nano has-scrollbar">
<div class="nano-content" tabindex="" style="right: -17px;"> <!--Nav tabs-->
<!--================================-->
<ul class="nav nav-tabs nav-justified">
<li class="active">
<a href="#demo-asd-tab-1" data-toggle="tab">
<i class="demo-pli-speech-bubble-7"></i>
</a>
</li>
</ul>
<!--================================-->
<!--End nav tabs--> <!-- Tabs Content -->
<!--================================-->
<div class="tab-content">
<div class="tab-pane fade in active" id="demo-asd-tab-1">
<p class="pad-hor text-semibold text-main">
<span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
</p> <!--Works-->
<div class="list-group bg-trans">
{% for item in contact_info_list.MemberList %}
<a href="#" for="{{ item.UserName }}" class="list-group-item">
<span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
</div>
</aside>
</div>
</div>
</div>
</div>
</div>
</body>
</html> <script>
function sendMsg(ths){
var sel_tag = $(".list-group").find(".active")
if(sel_tag.length==){
return false;
}
var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
var sendMsg={
'ToUserName':sel_tag.attr("for"),
'Type':,
'Content':msg,
'csrfmiddlewaretoken':'{{ csrf_token }}'
} $.ajax({
url:"send-msg.html",
data:sendMsg,
type:"POST",
dataType:"json",
success:function(callback){
if (callback.code==){
var dt = new Date()
var now_time = dt.toLocaleString(); console.log(callback);
var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
li += '<p>'+msg+'</p>';
li += '<p class="speech-time">';
li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
li += '</p></div></div></li>';
$(ths).parents(".widget-body").find(".list-unstyled").append(li);
$(ths).parents(".panel-footer").find(".chat-input").val("");
}
}
})
}
</script>
前端代码使用ajax向后端传送
后端代码send_msg
注意:处理传送中文时,在requests模块有点麻烦,下面代码有写解决方法
def send_msg(request):
ret = {
'code':,
'error':'Send Success',
'data':{}
} recv_data = request.POST if not recv_data:
ret['code']=
ret['data']="Send failure"
return HttpResponse(json.dumps(ret)) #数据整合
Send_data={}
Send_data['BaseRequest'] = {
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
} Send_data['Msg'] = {
'Type':recv_data.get("Type",),
'Content':recv_data.get("Content"),
'FromUserName':USER_INIT_DICT['User']['UserName'],
'ToUserName':recv_data.get("ToUserName"),
'LocalID':int(time.time()*),
'ClientMsgId':int(time.time()*),
}
Send_data['Scene']= send_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=%s'%TICKET_DICT['pass_ticket']
# reponse = requests.post(
# url=send_url,
# json=Send_data, #注意这里如果使用json,会将中文转换为Unicode
# cookies=ALL_COOKIE_DICT
# )
reponse = requests.post(
url=send_url,
#若是有中文,需要加上ensure_ascii=False,若是字符串中含有中文,request传递数据时,将中文转换为字节,无法为我们转换。需要我们提前使用encoding编码,直接传递字节,不让requests为我们转换
#data可以是字典,字符串,字节,既然对于字典,字符串直接含有中文不正确,直接转字节传送,py3默认是utf-8,所以我们直接传送字节就可以
data=bytes(json.dumps(Send_data,ensure_ascii=False),encoding="utf-8"), #注意这里如果使用json,会将中文转换为Unicode
cookies=ALL_COOKIE_DICT #携带cookie
) reponse.encoding = "utf-8" ret_status = re.findall('"Ret": (.*),', reponse.text)[]
ret_error = re.findall('"ErrMsg": "(.*)"', reponse.text)[] if int(ret_status) != :
ret['code']=
ret['data']=ret_error
return HttpResponse(json.dumps(ret)) ret['username'] = USER_INIT_DICT['User']['NickName']
ret['headImgUrl'] = 'https://wx.qq.com'+USER_INIT_DICT['User']['HeadImgUrl'] return HttpResponse(json.dumps(ret))
七:实现长轮询接收消息
先参考微信的实例:微信依靠两个url实现去后端获取数据
1.webwxsync:ajax使用POST,长轮询去获取发送的消息,和获取一个SyncCheckKey,下面检测是否有消息到了需要携带这个SyncCheckKey https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=NOWucA2Et3xw0l8a&skey=@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&pass_ticket=9ZK9OoNzaLxkdRqVWghy7uGzWFvsIzXNWDjgeJqTskg3Bl08tQxAZ9t0hcEYOzmO 要传递的POST数据
{
"BaseRequest":{ #都是已经获取的数据
"Uin":2821071261,
"Sid":"NOWucA2Et3xw0l8a",
"Skey":"@crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853",
"DeviceID":"e937865997050874"
},
"SyncKey":{ #我们上一次的SyncKey值
"Count":9,"List":[{"Key":1,"Val":677540346},{"Key":2,"Val":677540435},{"Key":3,"Val":677540036},{"Key":11,"Val":677540181},{"Key":201,"Val":1529672373},{"Key":203,"Val":1529658601},{"Key":1000,"Val":1529658962},{"Key":1001,"Val":1529659033},{"Key":2001,"Val":1529480143}]
},
"rr":-664997335 #未知
}
{
"BaseResponse": {
"Ret": ,
"ErrMsg": ""
}
,
"AddMsgCount": ,
"AddMsgList": [],
"ModContactCount": ,
"ModContactList": [],
"DelContactCount": ,
"DelContactList": [],
"ModChatRoomMemberCount": ,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": ,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": ,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": ,
"Sex": ,
"PersonalCard": ,
"Alias": "",
"HeadImgUpdateFlag": ,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": ,
"SyncKey": {
"Count": ,
"List": [
{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": ,
"List": [
{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
}
}
获取的数据:SyncKey
2.synccheck:轮询请求,用于检测是否有人发消息过来。注意:当我们刚刚登陆时,是有一个初始的synckey的,可以在《相关数据打印(格式)》那里看到
https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=1529672369137&skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=2821071261&deviceid=e539832593408529&synckey=1_677540346%7C2_677540434%7C3_677540036%7C11_677540181%7C201_1529672366%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143&_=1529671539738
https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?
r= #时间戳
&skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a
&uin= #和其他数据一起放在TICKET_DICT中
&deviceid=e065841963151848 #设备id
&synckey=
1_677540346 #和上一个请求url中接收的数据一致
%7C |
2_677540423
%7C |
3_677540036
%7C
11_677540181%7C201_1529671726%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143
&_= #时间戳 r=&skey=%40crypt_7358fe11_181eea60999ab937ed1ff13e1d1f7853&sid=NOWucA2Et3xw0l8a&uin=&deviceid=e647845000558861&synckey=1_677540346%7C2_677540435%7C3_677540036%7C11_677540181%7C201_1529672373%7C203_1529658601%7C1000_1529658962%7C1001_1529659033%7C2001_1529480143&_=
返回值
window.synccheck={retcode:"0",selector:"2"} #有消息到来
window.synccheck={retcode:"0",selector:"0"} #没有消息到来
当有消息到来:我们将从webwxsync中获取到数据
{
"BaseResponse": {
"Ret": ,
"ErrMsg": ""
}
,
"AddMsgCount": , #到来的消息数目
"AddMsgList": [{
"MsgId": "",
"FromUserName": "@@e780498496215d2077ef72216ce629bfd570595c2d1b99192aa32fae78a3e489", #来自谁的微信
"ToUserName": "@9419ce13e311ec0469803ba1667703ca16fd3e9ec13d5998c52f42dba33f4c3d", #这是我们的微信
"MsgType": ,
"Content": "@0e722870282c2e079ec9ddc7304ef501bb99d45465501efac52f8b050498d8a4:<br/>@A~ç±åµ©ç¬â€…10分钟å§", #发过来的信息,我们未编码
"Status": , #状态
"ImgStatus": ,
"CreateTime": ,
"VoiceLength": ,
"PlayLength": ,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": ,
"StatusNotifyCode": ,
"StatusNotifyUserName": "",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": ,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": ,
"VerifyFlag": ,
"AttrStatus": ,
"Sex": ,
"Ticket": "",
"OpCode":
}
,
"ForwardFlag": ,
"AppInfo": {
"AppID": "",
"Type":
}
,
"HasProductId": ,
"Ticket": "",
"ImgHeight": ,
"ImgWidth": ,
"SubMsgType": ,
"NewMsgId": ,
"OriContent": "",
"EncryFileName": ""
}
],
"ModContactCount": ,
"ModContactList": [],
"DelContactCount": ,
"DelContactList": [],
"ModChatRoomMemberCount": ,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": ,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": ,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": ,
"Sex": ,
"PersonalCard": ,
"Alias": "",
"HeadImgUpdateFlag": ,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": ,
"SyncKey": { #下一次去检测需要的SyncKey,每当我们接受一次真正的消息或者长轮询结束,原来的就失效,需要一个新的SyncKey值
"Count": ,
"List": [{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": ,
"List": [{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
} }
后台视图方法获取接收的信息
def get_msg(request):
ret = {
'code':,
'msg':'no message arrived'
}
global SYNCKEY_DICT #设置为全局
#.先去查询是否有消息到达
req_url = 'https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck'
sync_key = []
for item in SYNCKEY_DICT['List']:
sync_key.append("%s_%s"%(item['Key'],item['Val'])) reponse = requests.get(
url=req_url,
params={
'r':int(time.time()*),
'skey':TICKET_DICT['skey'],
'sid':TICKET_DICT['wxsid'],
'uin':TICKET_DICT['wxuin'],
'deviceid':'e055319847811019',
'synckey':'|'.join(sync_key) #参数重组
},
cookies = ALL_COOKIE_DICT
) if 'window.synccheck={retcode:"0",selector:"2"}' in reponse.text:
ret['code'] =
ret['msg'] = "message arrived"
#有消息到来,这时我们需要去获取信息,并且更新SYNCKEY_DICT
req_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync' post_dict = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
},
'SyncKey':SYNCKEY_DICT
} reponse2 = requests.post(
url=req_url,
params={
'sid':TICKET_DICT['wxsid'],
'pass_ticket':TICKET_DICT['pass_ticket'],
'skey':TICKET_DICT['skey'],
'lang': 'zh_CN'
},
json = post_dict
) reponse2.encoding = "utf-8"
rep_dict = json.loads(reponse2.text)
SYNCKEY_DICT = rep_dict.get("SyncKey")
if rep_dict['AddMsgCount'] != :
ret['code'] =
ret['msg'] = []
for item in rep_dict['AddMsgList']:
ret['msg'].append(item) return HttpResponse(json.dumps(ret))
前端修改:首先是有get_msg方法去长轮询获取接收消息和检测数据是否接收。然后针对谁发送的,谁就高亮
<script>
$(function(){
bind_event();
get_msg();
}) function bind_event() {
$(".list-group-item").click(function(){
$(".list-unstyled").empty();
$(this).siblings().removeClass("active");
$(this).addClass("active");
var NickName = $(this).first().text().trim();
$(".panel-title").text(NickName);
})
} function get_msg(){
$.ajax({
url:"/get-msg.html",
type:"GET",
dataType:"json",
success:function(callback){
if (callback.code==){
//这里只去看一个人的信息
var info = callback['msg'][]
$(".list-group-item").each(function(){
if($(this).attr("for")==info['FromUserName']){
$(this).addClass("active")
return false;
}
})
}
get_msg();
}
})
}
</script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/nifty.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
<link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
<script src="/static/js/jquery-2.2.4.min.js"></script>
<script>
$(function(){
bind_event();
get_msg();
}) function bind_event() {
$(".list-group-item").click(function(){
$(".list-unstyled").empty();
$(this).siblings().removeClass("active");
$(this).addClass("active");
var NickName = $(this).first().text().trim();
$(".panel-title").text(NickName);
})
} function get_msg(){
$.ajax({
url:"/get-msg.html",
type:"GET",
dataType:"json",
success:function(callback){
if (callback.code==){
//这里只去看一个人的信息
var info = callback['msg'][]
$(".list-group-item").each(function(){
if($(this).attr("for")==info['FromUserName']){
$(this).addClass("active")
return false;
}
})
}
get_msg();
}
})
}
</script>
<script src="/static/plugins/pace/pace.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/nifty.min.js"></script>
<script src="/static/js/demo/nifty-demo.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
<script src="/static/plugins/gauge-js/gauge.min.js"></script>
<script src="/static/plugins/skycons/skycons.min.js"></script>
<script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
<script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect aside-bright mainnav-sm aside-right aside-in">
<div class="boxed">
<div id="content-container">
<div class="row">
<div class="col-md-8 col-lg-8 col-sm-8"> <!--Chat widget-->
<!--===================================================-->
<div class="panel" style="height: 640px">
<!--Heading-->
<div class="panel-heading">
<h3 class="panel-title">Chat</h3>
</div> <!--Widget body-->
<div style="height:510px;padding-top:0px;" class="widget-body">
<div class="nano">
<div class="nano-content pad-all">
<ul class="list-unstyled media-block">
<li class="mar-btm">
<div class="media-left">
<img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor">
<div class="speech">
<a href="#" class="media-heading">Aaron Chavez</a>
<p>Hello Lucy, how can I help you today ?</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i>:23AM
</p>
</div>
</div>
</li>
<li class="mar-btm">
<div class="media-right">
<img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor speech-right">
<div class="speech">
<a href="#" class="media-heading">Lucy Doe</a>
<p>Hi, I want to buy a new shoes.</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i> :23AM
</p>
</div>
</div>
</li>
</ul>
</div>
</div> <!--Widget footer-->
<div class="panel-footer" style="height: 90px;">
<div class="row">
<div class="col-xs-9">
<input type="text" placeholder="Enter your text" class="form-control chat-input">
</div>
<div class="col-xs-3">
<button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
</div>
</div>
</div>
</div>
</div>
<!--===================================================-->
<!--Chat widget--> </div>
<div class="col-md-4 col-lg-4 col-sm-4">
<aside id="aside-container">
<div id="aside">
<div class="nano has-scrollbar">
<div class="nano-content" tabindex="" style="right: -17px;"> <!--Nav tabs-->
<!--================================-->
<ul class="nav nav-tabs nav-justified">
<li class="active">
<a href="#demo-asd-tab-1" data-toggle="tab">
<i class="demo-pli-speech-bubble-7"></i>
</a>
</li>
</ul>
<!--================================-->
<!--End nav tabs--> <!-- Tabs Content -->
<!--================================-->
<div class="tab-content">
<div class="tab-pane fade in active" id="demo-asd-tab-1">
<p class="pad-hor text-semibold text-main">
<span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
</p> <!--Works-->
<div class="list-group bg-trans">
{% for item in contact_info_list.MemberList %}
<a href="#" for="{{ item.UserName }}" class="list-group-item">
<span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
</div>
</aside>
</div>
</div>
</div>
</div>
</div>
</body>
</html> <script>
function sendMsg(ths){
var sel_tag = $(".list-group").find(".active")
if(sel_tag.length==){
return false;
}
var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
var sendMsg={
'ToUserName':sel_tag.attr("for"),
'Type':,
'Content':msg,
'csrfmiddlewaretoken':'{{ csrf_token }}'
} $.ajax({
url:"send-msg.html",
data:sendMsg,
type:"POST",
dataType:"json",
success:function(callback){
if (callback.code==){
var dt = new Date()
var now_time = dt.toLocaleString(); console.log(callback);
var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
li += '<p>'+msg+'</p>';
li += '<p class="speech-time">';
li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
li += '</p></div></div></li>';
$(ths).parents(".widget-body").find(".list-unstyled").append(li);
$(ths).parents(".panel-footer").find(".chat-input").val("");
}
}
})
}
</script>
contact_info前端代码
window.synccheck={retcode:"",selector:""}
{
"BaseResponse": {
"Ret": ,
"ErrMsg": ""
}
,
"AddMsgCount": ,
"AddMsgList": [{
"MsgId": "",
"FromUserName": "@7ef22458145d7446b96c0c1612549c00991cab433640c86b9c7d5f9d17dc8a43",
"ToUserName": "@fb5a4e79e7d71ec3f11d3351fdec8a1cb5c4df7979041ed9315833e427323198",
"MsgType": ,
"Content": "在",
"Status": ,
"ImgStatus": ,
"CreateTime": ,
"VoiceLength": ,
"PlayLength": ,
"FileName": "",
"FileSize": "",
"MediaId": "",
"Url": "",
"AppMsgType": ,
"StatusNotifyCode": ,
"StatusNotifyUserName": "",
"RecommendInfo": {
"UserName": "",
"NickName": "",
"QQNum": ,
"Province": "",
"City": "",
"Content": "",
"Signature": "",
"Alias": "",
"Scene": ,
"VerifyFlag": ,
"AttrStatus": ,
"Sex": ,
"Ticket": "",
"OpCode":
}
,
"ForwardFlag": ,
"AppInfo": {
"AppID": "",
"Type":
}
,
"HasProductId": ,
"Ticket": "",
"ImgHeight": ,
"ImgWidth": ,
"SubMsgType": ,
"NewMsgId": ,
"OriContent": "",
"EncryFileName": ""
}
],
"ModContactCount": ,
"ModContactList": [],
"DelContactCount": ,
"DelContactList": [],
"ModChatRoomMemberCount": ,
"ModChatRoomMemberList": [],
"Profile": {
"BitFlag": ,
"UserName": {
"Buff": ""
}
,
"NickName": {
"Buff": ""
}
,
"BindUin": ,
"BindEmail": {
"Buff": ""
}
,
"BindMobile": {
"Buff": ""
}
,
"Status": ,
"Sex": ,
"PersonalCard": ,
"Alias": "",
"HeadImgUpdateFlag": ,
"HeadImgUrl": "",
"Signature": ""
}
,
"ContinueFlag": ,
"SyncKey": {
"Count": ,
"List": [{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
}
,
"SKey": "",
"SyncCheckKey": {
"Count": ,
"List": [{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
,{
"Key": ,
"Val":
}
]
} }
后端打印的数据
其实应该更进一步,显示出来我们接受的数据,但是明天考试..不再继续了
八:全部代码
后端:
from django.shortcuts import render,HttpResponse,redirect
from bs4 import BeautifulSoup
import requests
import time,re,json CTIME = None
QCODE = None
TIP =
TICKET_DICT = {} #保存凭证信息
ALL_COOKIE_DICT = {} #保存所有COOKIE信息
USER_INIT_DICT = {} #保存用户最近联系信息,和自己的信息
SYNCKEY_DICT = {} #用于保存获取请求时需要的SyncKey # Create your views here.
def login(request):
global CTIME
global QCODE
CTIME = int(time.time()*) data = {
'appid':'wx782c26e4c19acffb',
'fun':'new',
'lang':'zh_CN',
'_':CTIME
} response = requests.get(
url="https://login.wx.qq.com/jslogin",
params=data
) pat_res = re.findall('uuid = "(.*)";',response.text)
QCODE = pat_res[] return render(request,"login.html",{'qcode':QCODE}) def check_login(request):
global TIP
ret = {'code':,'data':None}
data = {
'loginicon':"true",
'uuid':QCODE,
'tip':TIP,
'r':'-577317906',
'_':int(time.time())
}
r1 = requests.get(
url='https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login',
params=data
) if 'window.code=408' in r1.text:
print("无人扫描")
return HttpResponse(json.dumps(ret))
elif 'window.code=201' in r1.text:
ret['code'] =
pat_ret = re.findall("window.userAvatar = '(.*)';",r1.text)[]
ret['data'] = pat_ret
TIP =
return HttpResponse(json.dumps(ret))
elif 'window.code=200;' in r1.text:
ret['code'] =
redirect_url = re.findall('window.redirect_uri="(.*)";', r1.text)[]
reponse = requests.get( #获取凭证,这里也开始设置cookie了,所以我们在这里向后需要记录cookie
url=redirect_url + "&fun=new&version=v2" # url不够完整,需要我们完善
)
ALL_COOKIE_DICT.update(reponse.cookies) soup = BeautifulSoup(reponse.text, "lxml")
info_dict = {}
for tag in soup.find("error").children:
info_dict[tag.name] = tag.get_text() global TICKET_DICT
TICKET_DICT.update(info_dict) ret['code']= return HttpResponse(json.dumps(ret)) def user(request):
get_user_info_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-613135321&pass_ticket=' + TICKET_DICT[
'pass_ticket']
get_user_info_form = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
}
} reponse2 = requests.post( # 获取的是用户信息,几个联系人,公众号,自己信息
url=get_user_info_url,
json=get_user_info_form, # 注意这里使用的是json,post不允许传送字典
)
ALL_COOKIE_DICT.update(reponse2.cookies) reponse2.encoding = "utf-8" user_info_dict = json.loads(reponse2.text) # 获取的是用户信息,几个联系人,公众号,自己信息
USER_INIT_DICT.update(user_info_dict)
SYNCKEY_DICT.update(user_info_dict['SyncKey']) return render(request, "user.html", {'user_info_dict': user_info_dict}) def contact_list(request):
get_all_user_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=%s&r=%s&seq=0&skey=%s' % (TICKET_DICT['pass_ticket'],int(time.time()*),TICKET_DICT['skey']) reponse = requests.get(
url=get_all_user_url, #这里需要用到cookie
cookies=ALL_COOKIE_DICT
) reponse.encoding = "utf-8"
contact_info_list = json.loads(reponse.text) return render(request,"contact_info.html",{'contact_info_list':contact_info_list}) def send_msg(request):
ret = {
'code':,
'error':'Send Success',
'data':{}
} recv_data = request.POST if not recv_data:
ret['code']=
ret['data']="Send failure"
return HttpResponse(json.dumps(ret)) #数据整合
Send_data={}
Send_data['BaseRequest'] = {
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
} Send_data['Msg'] = {
'Type':recv_data.get("Type",),
'Content':recv_data.get("Content"),
'FromUserName':USER_INIT_DICT['User']['UserName'],
'ToUserName':recv_data.get("ToUserName"),
'LocalID':int(time.time()*),
'ClientMsgId':int(time.time()*),
}
Send_data['Scene']= send_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?lang=zh_CN&pass_ticket=%s'%TICKET_DICT['pass_ticket']
# reponse = requests.post(
# url=send_url,
# json=Send_data, #注意这里如果使用json,会将中文转换为Unicode
# cookies=ALL_COOKIE_DICT
# )
reponse = requests.post(
url=send_url,
#若是有中文,需要加上ensure_ascii=False,若是字符串中含有中文,request传递数据时,将中文转换为字节,无法为我们转换。需要我们提前使用encoding编码,直接传递字节,不让requests为我们转换
#data可以是字典,字符串,字节,既然对于字典,字符串直接含有中文不正确,直接转字节传送,py3默认是utf-,所以我们直接传送字节就可以
data=bytes(json.dumps(Send_data,ensure_ascii=False),encoding="utf-8"), #注意这里如果使用json,会将中文转换为Unicode
cookies=ALL_COOKIE_DICT
) reponse.encoding = "utf-8" ret_status = re.findall('"Ret": (.*),', reponse.text)[]
ret_error = re.findall('"ErrMsg": "(.*)"', reponse.text)[] if int(ret_status) != :
ret['code']=
ret['data']=ret_error
return HttpResponse(json.dumps(ret)) ret['username'] = USER_INIT_DICT['User']['NickName']
ret['headImgUrl'] = 'https://wx.qq.com'+USER_INIT_DICT['User']['HeadImgUrl'] return HttpResponse(json.dumps(ret)) def get_msg(request):
ret = {
'code':,
'msg':'no message arrived'
}
global SYNCKEY_DICT
#.先去查询是否有消息到达
req_url = 'https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck'
sync_key = []
for item in SYNCKEY_DICT['List']:
sync_key.append("%s_%s"%(item['Key'],item['Val'])) reponse = requests.get(
url=req_url,
params={
'r':int(time.time()*),
'skey':TICKET_DICT['skey'],
'sid':TICKET_DICT['wxsid'],
'uin':TICKET_DICT['wxuin'],
'deviceid':'e055319847811019',
'synckey':'|'.join(sync_key)
},
cookies = ALL_COOKIE_DICT
) if 'window.synccheck={retcode:"0",selector:"2"}' in reponse.text:
ret['code'] =
ret['msg'] = "message arrived"
#有消息到来,这时我们需要去获取信息,并且更新SYNCKEY_DICT
req_url = 'https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync' post_dict = {
'BaseRequest':
{
'DeviceID': "e055319847811019",
'Sid': TICKET_DICT['wxsid'],
'Skey': TICKET_DICT['skey'],
'Uin': TICKET_DICT['wxuin']
},
'SyncKey':SYNCKEY_DICT
} reponse2 = requests.post(
url=req_url,
params={
'sid':TICKET_DICT['wxsid'],
'pass_ticket':TICKET_DICT['pass_ticket'],
'skey':TICKET_DICT['skey'],
'lang': 'zh_CN'
},
json = post_dict
) reponse2.encoding = "utf-8"
rep_dict = json.loads(reponse2.text)
SYNCKEY_DICT = rep_dict.get("SyncKey")
if rep_dict['AddMsgCount'] != :
ret['code'] =
ret['msg'] = []
for item in rep_dict['AddMsgList']:
ret['msg'].append(item) return HttpResponse(json.dumps(ret))
views.py
前端:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<img id="qrcode" style="width: 340px;height: 340px;" src="https://login.weixin.qq.com/qrcode/{{ qcode }}" alt="">
</body>
</html>
<script src="/static/jquery.js"></script>
<script>
$(function(){
checkLogin();
}) function checkLogin() {
$.ajax({
url:'/check-login.html',
type:'GET',
dataType:"json",
success:function(data){
console.log(data.code);
if (data.code==){
checkLogin();
}
else if(data.code==){
$("#qrcode").attr('src',data.data)
checkLogin();
}
else if(data.code==){
location.href='/user.html'
}
}
})
}
</script>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<h3>最近联系人</h3>
<ul>
{% for item in user_info_dict.ContactList %}
<li>{{ item.NickName }}</li>
{% endfor %}
</ul>
<a href="/contact-list.html">获取更多联系人</a>
</div>
<div>
<h3>微信公众号</h3>
<div>
{% for item in user_info_dict.MPSubscribeMsgList %}
<h4>{{ item.NickName }}</h4>
<ul>
{% for item2 in item.MPArticleList %}
<li>
<a href="{{ item2.Url }}">
{{ item2.Title }}
</a>
</li>
{% endfor %}
</ul>
{% endfor %}
</div>
</div>
</body>
</html>
user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/css/nifty.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo-icons.min.css" rel="stylesheet">
<link href="/static/css/demo/nifty-demo.min.css" rel="stylesheet">
<link href="/static/plugins/pace/pace.min.css" rel="stylesheet">
<script src="/static/js/jquery-2.2.4.min.js"></script>
<script>
$(function(){
bind_event();
get_msg();
}) function bind_event() {
$(".list-group-item").click(function(){
$(".list-unstyled").empty();
$(this).siblings().removeClass("active");
$(this).addClass("active");
var NickName = $(this).first().text().trim();
$(".panel-title").text(NickName);
})
} function get_msg(){
$.ajax({
url:"/get-msg.html",
type:"GET",
dataType:"json",
success:function(callback){
if (callback.code==){
//这里只去看一个人的信息
var info = callback['msg'][]
$(".list-group-item").each(function(){
if($(this).attr("for")==info['FromUserName']){
$(this).addClass("active")
return false;
}
})
}
get_msg();
}
})
}
</script>
<script src="/static/plugins/pace/pace.min.js"></script>
<script src="/static/js/bootstrap.min.js"></script>
<script src="/static/js/nifty.min.js"></script>
<script src="/static/js/demo/nifty-demo.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.min.js"></script>
<script src="/static/plugins/flot-charts/jquery.flot.resize.min.js"></script>
<script src="/static/plugins/gauge-js/gauge.min.js"></script>
<script src="/static/plugins/skycons/skycons.min.js"></script>
<script src="/static/plugins/easy-pie-chart/jquery.easypiechart.min.js"></script>
<script src="/static/js/demo/widgets.js"></script>
</head>
<body>
<div id="container" class="effect aside-bright mainnav-sm aside-right aside-in">
<div class="boxed">
<div id="content-container">
<div class="row">
<div class="col-md-8 col-lg-8 col-sm-8"> <!--Chat widget-->
<!--===================================================-->
<div class="panel" style="height: 640px">
<!--Heading-->
<div class="panel-heading">
<h3 class="panel-title">Chat</h3>
</div> <!--Widget body-->
<div style="height:510px;padding-top:0px;" class="widget-body">
<div class="nano">
<div class="nano-content pad-all">
<ul class="list-unstyled media-block">
<li class="mar-btm">
<div class="media-left">
<img src="img/profile-photos/1.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor">
<div class="speech">
<a href="#" class="media-heading">Aaron Chavez</a>
<p>Hello Lucy, how can I help you today ?</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i>:23AM
</p>
</div>
</div>
</li>
<li class="mar-btm">
<div class="media-right">
<img src="img/profile-photos/8.png" class="img-circle img-sm" alt="Profile Picture">
</div>
<div class="media-body pad-hor speech-right">
<div class="speech">
<a href="#" class="media-heading">Lucy Doe</a>
<p>Hi, I want to buy a new shoes.</p>
<p class="speech-time">
<i class="demo-pli-clock icon-fw"></i> :23AM
</p>
</div>
</div>
</li>
</ul>
</div>
</div> <!--Widget footer-->
<div class="panel-footer" style="height: 90px;">
<div class="row">
<div class="col-xs-9">
<input type="text" placeholder="Enter your text" class="form-control chat-input">
</div>
<div class="col-xs-3">
<button class="btn btn-primary btn-block" onclick="sendMsg(this);" type="submit">Send</button>
</div>
</div>
</div>
</div>
</div>
<!--===================================================-->
<!--Chat widget--> </div>
<div class="col-md-4 col-lg-4 col-sm-4">
<aside id="aside-container">
<div id="aside">
<div class="nano has-scrollbar">
<div class="nano-content" tabindex="" style="right: -17px;"> <!--Nav tabs-->
<!--================================-->
<ul class="nav nav-tabs nav-justified">
<li class="active">
<a href="#demo-asd-tab-1" data-toggle="tab">
<i class="demo-pli-speech-bubble-7"></i>
</a>
</li>
</ul>
<!--================================-->
<!--End nav tabs--> <!-- Tabs Content -->
<!--================================-->
<div class="tab-content">
<div class="tab-pane fade in active" id="demo-asd-tab-1">
<p class="pad-hor text-semibold text-main">
<span class="pull-right badge badge-success">{{ contact_info_list.MemberCount }}</span> Friends
</p> <!--Works-->
<div class="list-group bg-trans">
{% for item in contact_info_list.MemberList %}
<a href="#" for="{{ item.UserName }}" class="list-group-item">
<span class="badge badge-purple badge-icon badge-fw pull-left"></span> {{ item.NickName }}
</a>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="nano-pane" style="display: none;"><div class="nano-slider" style="height: 4059px; transform: translate(0px, 0px);"></div></div></div>
</div>
</aside>
</div>
</div>
</div>
</div>
</div>
</body>
</html> <script>
function sendMsg(ths){
var sel_tag = $(".list-group").find(".active")
if(sel_tag.length==){
return false;
}
var msg = $(ths).parents(".panel-footer").find(".chat-input").val();
var sendMsg={
'ToUserName':sel_tag.attr("for"),
'Type':,
'Content':msg,
'csrfmiddlewaretoken':'{{ csrf_token }}'
} $.ajax({
url:"send-msg.html",
data:sendMsg,
type:"POST",
dataType:"json",
success:function(callback){
if (callback.code==){
var dt = new Date()
var now_time = dt.toLocaleString(); console.log(callback);
var li = '<li class="mar-btm"><div class="media-right"><img src="'+callback.headImgUrl+'" class="img-circle img-sm" alt="Profile Picture"></div>';
li += '<div class="media-body pad-hor speech-right"><div class="speech"><a href="#" class="media-heading">'+callback.username+'</a>';
li += '<p>'+msg+'</p>';
li += '<p class="speech-time">';
li += '<i class="demo-pli-clock icon-fw"></i>'+now_time;
li += '</p></div></div></li>';
$(ths).parents(".widget-body").find(".list-unstyled").append(li);
$(ths).parents(".panel-footer").find(".chat-input").val("");
}
}
})
}
</script>
contact_info.html