文章所使用Python版本为py3.5

1.微信服务器返回一个会话ID

微信Web版本不使用用户名和密码直接登录,而是采用二维码登录,所以服务器需要首先分配一个唯一的会话ID,用来标识当前的一次登录。

通过查看网络请求我们找到了这个 二维码图片代表的随机字符串,(IcelandB9Entig==),

2.通过会话ID获得二维码

然后找到该随机字符串的来源请求

请求方式为 GET形式 , 具体连接为:

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
&_=1492591577859

我们只需要改变最后一个变量 _值即可获得新的字符串序列: (这个值是当前距离林威治标准时间的毫秒)可以自行构造!

通过分割,我们就可以获得随机字符串,自己在前端页面上构造一个二维码出来.

import tornado.ioloop
import tornado.web
import requests
import time
import re
import json
import urllib.request,urllib.parse
import random WECHAT_SESSION_ID = None
WECHAT_TIMESPAN = None SESSION_ID_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&_={0}' class HomeHandler(tornado.web.RequestHandler):
def get(self):
print('\n--------------------')
global WECHAT_SESSION_ID
global WECHAT_TIMESPAN WECHAT_TIMESPAN = str(time.time())
sesssion_url = SESSION_ID_URL.format(WECHAT_TIMESPAN)
response = requests.get(sesssion_url)
# 获取验证码,随机字段
WECHAT_SESSION_ID = re.split('=', response.text, 2)[-1].strip().replace('"', '').replace(';', '')
print('Session_id : ',WECHAT_SESSION_ID)
self.render('index.html',session_id = WECHAT_SESSION_ID)

HomeHandle

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0;
padding: 0;
}
.login_alt{
margin: 42px auto;
text-align: center;
}
.image{
display: block;
width: 270px;
height: 270px;
margin: 42px auto;
}
</style>
</head>
<body> <div class="login_alt"><h1>二维码登陆</h1></div> <img src="https://login.weixin.qq.com/qrcode/{{session_id}}" class="image"> <script src="{{ static_url('js/jquery-2.1.4.min.js')}}" ></script>
<script>
$(function () {
acquire_image();
});
function acquire_image() {
$.ajax({
url:'/login',
type:'POST',
success:function (data) {
if(data == '200'){
window.location.href = "/index";
}
else{
acquire_image();
} },
error:function (data) {
console.log('error');
}
})
}
</script>
</body>
</html>

前端二维码页面

3.轮询手机端是否已经扫描二维码并确认在Web端登录

当我们还没进行扫码登录时,发现微信web网页会自动向服务器 轮询手机端是否已经扫码并且确认登录!!

每一分钟发送一次,如果没有登录,请求会返回

window.code=408;

扫码后为

window.code=201;

确认登陆后为:

window.code=200;
window.redirect_uri="https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A_ToQqd_AFXpsrLMH6RNgi3W@qrticket_0&uuid=we6XJEZYDg==&lang=zh_CN&scan=1492592531";

需要获得其中的跳转url,对rensponse进行分割后取得。

redirect_url = re.split('=', http_res_code.text, 2)[-1].strip().replace('"', '').replace(';', '')

4.访问登录地址,获得uin和sid

在接下来的连接中,我们发现服务器发给了我们skey,sid,pass_ticket 的重要信息, 分析发现该请求的发送地址为: GET方式,

https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A_ToQqd_AFXpsrLMH6RNgi3W@qrticket_0&uuid=we6XJEZYDg==&lang=zh_CN&scan=1492592531
&fun=new&version=v2

发现,该请求地址为 跳转url + &fun=new&version=v2 构造而成!

直接GET发送请求,我们就拿到返回信息,发现是 xml 格式的数据!

构造一个方法方便获取我们的连接信息

def auth_analysis(self,str_xml):
'''
xml中取出数据,返回字典
:param http_res_ticket:
:return:
'''
from xml.etree import ElementTree as ET
root = ET.XML(str_xml)
ret = {}
for child in root:
ret[child.tag] = child.text
return ret

我们还需要获得该请求下的cookies参数,直接通过requests模块的  requests.cookies.get_dict()

# 获取xml 登录信息
user_ticket_url = redirect_url+ '&fun=new&version=v2'
user_xml_ticket = requests.get(user_ticket_url) TICKET_COOKIR = user_xml_ticket.cookies.get_dict()
ALL_COOKIE_DICT.update(TICKET_COOKIR) #记录cookies # 获取xml中信息
user_ticket_dic = self.auth_analysis(user_xml_ticket.text)
USER_RESULT_DICT.update(user_ticket_dic) #记录xml中的具体信息

5.初使化微信信息

# 初始化Url :
USER_INIT_URL = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?pass_ticket={0}&skey={1}&r={2}'

pass_ticket,skey为上一步骤获得的xml信息树中,r为时间戳

# 初始化准备
user_init_payload = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
}
}
# 初始化url
user_init_url = USER_INIT_URL.format(USER_RESULT_DICT['pass_ticket'],USER_RESULT_DICT['skey'],int(time.time())) # payload 转换成bytes
data = (json.dumps(user_init_payload)).encode() request = urllib.request.Request(url=user_init_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read()
# 获得初始化用户数据..
obj = json.loads(data.decode('utf-8'))
INIT_RESULT_DICT.update(obj)

这一步的发送请求,使用了urllib.requests来发送,注意格式转换!

此时我们已经获得了服务端返回的部分数据.如果需要进一步获取全部信息,......如下

6.获得所有的好友列表

该请求返回了全部的用户信息.

https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?
pass_ticket=YK57xcgn4qT0n0qoGtxvtLvsV6XXzTDka7Z9DOFAIcGDK0wtZ6GrNpGdHGIHKiiu
&r=1492592546748
&seq=0
&skey=@crypt_8226323c_fd634988b8e43769cb3b7b9fc99ca549

构造请求并发送:

class ContactHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
try:
user_list_url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket={0}&r={1}&seq=0&skey={2}"
user_list_url = user_list_url.format(USER_RESULT_DICT['pass_ticket'],USER_RESULT_DICT['skey'],int(time.time())) conn = requests.get(url = user_list_url , data={} , cookies=ALL_COOKIE_DICT , headers={'contentType':'application/json; charset=UTF-8','Referer':'https://wx.qq.com/?&lang=zh_CN'})
# print(conn.encoding) # ISO-8859-1
conn.encoding = 'utf-8'
USER_LIST_DICT.update(json.loads(conn.text))
# print(USER_LIST_DICT) self.render('contact_list.html', user_list_dict=USER_LIST_DICT, user=CURRENT_USER)
except Exception:
self.redirect('/login')

获得所有信息的handler

7.保持与服务器的信息同步

与服务器保持同步需要在客户端做轮询,该轮询的URL如下:

https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck?
r=1492594498787
&skey=%40crypt_8226323c_fd634988b8e43769cb3b7b9fc99ca549
&sid=mM%2BFZGbHxGcO3x93
&uin=976834800
&deviceid=e738560216565557
&synckey=1_661566297%7C2_661566394%7C3_661565574%7C11_661566180%7C13_661374753%7C201_1492594315%7C203_1492582669%7C1000_1492594202%7C1001_1492563271
&_=1492592515065

skey,sid,uin,与上面步骤的值相对应此处的synkey是上步步骤获得的同步键值,但需要按一定的规则组合成以下的字符串:

1_124125|2_452346345|3_65476547|1000_5643635

sync_data_list = []
for item in INIT_RESULT_DICT['SyncKey']['List']:
temp = "%s_%s" % (item['Key'], item['Val'])
sync_data_list.append(temp)
sync_data_str = "|".join(sync_data_list)

合成synckey

| 被URL编码成%7C,通过对上面的地址发送请求:,

res_sync = requests.get(synccheck_url,params=payloads,cookies = ALL_COOKIE_DICT)

会返回如下的字符串:

window.synccheck={retcode:"0",selector:"0"}

当有人发送信息给你时, :

window.synccheck={retcode:"0",selector:"2"}

8.获得别人发来的消息

我们通过该URL地址获取发送给我们的消息:

https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?
sid=XQvf1MmZ7W8O2BU2
&skey=@crypt_75efaa00_3afa77c01d45461eef7eac88da37f70d
&pass_ticket=YArFw%252BDpJHCpRtKCTTabpe2ytETBDmYsp%252BB7ywe%252BtplmzxQZ6ohX7d14sJgjgk6T

制造相应的payload 发送GET请求,获得返回response数据

if 'selector:"2"' in res_sync.text:

    fetch_msg_url = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&pass_ticket={2}'
fetch_msg_url = fetch_msg_url.format(USER_RESULT_DICT['wxsid'],USER_RESULT_DICT['skey'],USER_RESULT_DICT['pass_ticket']) payloads = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
},
'SyncKey' : INIT_RESULT_DICT['SyncKey'],
'rr' : int(time.time())
} # payload 转换成bytes
data = (json.dumps(payloads)).encode() request = urllib.request.Request(url=fetch_msg_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read() # 获得初始化用户数据..
res_fetch_msg_dict = json.loads(data.decode('utf-8'))
INIT_RESULT_DICT['SyncKey'] = res_fetch_msg_dict['SyncKey'] for item in res_fetch_msg_dict['AddMsgList']:
print(item['FromUserName'], "---->", item['ToUserName'], ":::::", item['Content'])

9.向用户发送消息

用户主动发送消息,通过以下的URL地址:

https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg
?pass_ticket=mRfzOKMZdUAfqrZVaT7VZeroYNX6SgTtO7WzDwDXmiZvwWC0iWlln2fBuGa8oeld

上面的pass_ticket参数不再解释了,访问该URL采用POST方式,payload如以下的格式:

message = self.get_argument('message')         # 需要发送的信息
to_username = self.get_argument('username') # 接收人的id 好友列表中获取
print(message,to_username)
#
send_url = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}'
send_url = send_url.format(USER_RESULT_DICT['pass_ticket'])
time_str = str(int(time.time()))
payloads = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
},
'Msg': {
'ClientMsgId': time_str,
'Content': message,
'FromUserName': CURRENT_USER['UserName'], # 自己的用户信息,在微信初始化时获得!
'LocalID': time_str,
'ToUserName': to_username,
'Type': 1,
},
'Scene': 0,
} # payload 转换成bytes
data = (json.dumps(payloads)).encode()
request = urllib.request.Request(url=send_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read() # 获得初始化用户数据..
res_fetch_msg_dict = json.loads(data.decode('utf-8')) print(res_fetch_msg_dict)

  BaseRequest都是授权相关的值,与上面的步骤中的值对应,Msg是对消息的描述,包括了发送人与接收人,消息内容,消息的类型(1为文本),ClientMsgId和LocalID由本地生成。rr可用当前的时间。在返回JSON结果中BaseResponse描述了发送情况,Ret为0表示发送成功。

注意:

该流程在py3环境下,使用了Tornado框架 和 requests 包 和 urllib.requests 包

urllib.requests 包 具体用法参考如下示例:

# payload 转换成bytes
data = (json.dumps(payload)).encode() request = urllib.request.Request(url=user_init_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
# 获得数据
data = response.read()
obj = json.loads(data.decode('utf-8'))
# 保存数据
INIT_RESULT_DICT.update(obj)

  

完整参考代码:

#!/usr/bin/env python
# -*-coding:utf-8 -*- import tornado.ioloop
import tornado.web from controllers import index settings = {
'template_path': 'views', # 模版路径的配置
'static_path' : 'static', # 静态文件路径
} application = tornado.web.Application([
(r"/home", index.HomeHandler),
(r"/login", index.LoginHandler),
(r"/index", index.IndexHandler),
(r"/contact_list", index.ContactHandler),
(r"/msg", index.MessageGetHandler),
(r"/test", index.TestHandler),
],**settings) if __name__ == "__main__":
print('http://localhost:8888/home')
application.listen(8888)
tornado.ioloop.IOLoop.instance().start()

app.py

#!/usr/bin/env python
# -*-coding:utf-8 -*- import tornado.ioloop
import tornado.web
import requests
import time
import re
import json
import urllib.request
import random
import copy WECHAT_SESSION_ID = None
WECHAT_TIMESPAN = None SESSION_ID_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&_={0}' LOGIN_URL = 'https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=1&r=-1910008125&_={1}' # 登录信息 :
USER_INFO_URL = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket={0}&uuid={1}&lang=zh_CN&scan=1492317317&fun=new&version=v2' # 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A_efO6sZQDqimyL-Cybhd6G8@qrticket_0&uuid=AanPHT1H9w==&lang=zh_CN&scan=1492317317&fun=new&version=v2' # 初始化 :
USER_INIT_URL = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?pass_ticket={0}&skey={1}&r={2}' # https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-1964927750&pass_ticket=u%252BqG9nArZmFOEieyz9dA0i7f4kSKwRxnM9O5KgBtOp8Z5FSVKGKrNmTrKOBTl0Ml BASE_REQUEST_DICT = {} INIT_RESULT_DICT = {} USER_RESULT_DICT = {} CURRENT_USER = {} USER_LIST_DICT = {} ALL_COOKIE_DICT = {} class HomeHandler(tornado.web.RequestHandler):
def get(self):
print('\n--------------------')
global WECHAT_SESSION_ID
global WECHAT_TIMESPAN WECHAT_TIMESPAN = str(time.time())
sesssion_url = SESSION_ID_URL.format(WECHAT_TIMESPAN)
response = requests.get(sesssion_url)
# 获取验证码,随机字段
WECHAT_SESSION_ID = re.split('=', response.text, 2)[-1].strip().replace('"', '').replace(';', '')
print('Session_id : ',WECHAT_SESSION_ID)
self.render('index.html',session_id = WECHAT_SESSION_ID) class LoginHandler(tornado.web.RequestHandler): def auth_analysis(self,str_xml):
'''
xml中取出数据,返回字典
:param http_res_ticket:
:return:
'''
from xml.etree import ElementTree as ET
root = ET.XML(str_xml)
ret = {}
for child in root:
ret[child.tag] = child.text
return ret def post(self):
ret_code = ""
login_url = LOGIN_URL.format(WECHAT_SESSION_ID,str(time.time())) http_res_code = requests.get(login_url) if "window.code=408" in http_res_code.text:
ret_code = ""
if "window.code=200" in http_res_code.text:
ret_code = "" # 登录跳转URL
redirect_url = re.split('=', http_res_code.text, 2)[-1].strip().replace('"', '').replace(';', '')
print('200 : 跳转url',redirect_url) code_cookie = http_res_code.cookies.get_dict()
ALL_COOKIE_DICT.update(code_cookie)
print('cookies 1 login:', ALL_COOKIE_DICT) # 获取xml 登录信息
user_ticket_url = redirect_url+ '&fun=new&version=v2'
user_xml_ticket = requests.get(user_ticket_url) TICKET_COOKIR = user_xml_ticket.cookies.get_dict()
ALL_COOKIE_DICT.update(TICKET_COOKIR)
print('cookies 2 xml:',ALL_COOKIE_DICT) xml_str = user_xml_ticket.text
# 获取xml中信息
user_ticket_dic = self.auth_analysis(xml_str) USER_RESULT_DICT.update(user_ticket_dic) # 初始化准备
# __init__ payload :
user_init_payload = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
}
} BASE_REQUEST_DICT.update(user_init_payload) # 初始化url
user_init_url = USER_INIT_URL.format(USER_RESULT_DICT['pass_ticket'],USER_RESULT_DICT['skey'],int(time.time())) # payload 转换成bytes
data = (json.dumps(user_init_payload)).encode() request = urllib.request.Request(url=user_init_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read()
# 获得初始化用户数据..
obj = json.loads(data.decode('utf-8'))
INIT_RESULT_DICT.update(obj) # print('response : ' ,type(obj))
# print('--',INIT_RESULT_DICT) print(ret_code)
self.write(ret_code) class IndexHandler(tornado.web.RequestHandler):
def get(self):
print(INIT_RESULT_DICT)
print('--------------')
print(INIT_RESULT_DICT['SyncKey'])
try:
count = INIT_RESULT_DICT['Count']
sync_key = INIT_RESULT_DICT['SyncKey']
system_time = INIT_RESULT_DICT['SystemTime']
skey = INIT_RESULT_DICT['SKey'] client_version = INIT_RESULT_DICT['ClientVersion']
base_response = INIT_RESULT_DICT['BaseResponse']
MPSubscribeMsgCount = INIT_RESULT_DICT['MPSubscribeMsgCount']
GrayScale = INIT_RESULT_DICT['GrayScale']
InviteStartCount = INIT_RESULT_DICT['InviteStartCount'] MPSubscribeMsgList = INIT_RESULT_DICT['MPSubscribeMsgList'] ClickReportInterval = INIT_RESULT_DICT['ClickReportInterval'] contact_list = INIT_RESULT_DICT['ContactList'] user = INIT_RESULT_DICT['User']
CURRENT_USER.update(user) self.render('main.html', user=user, contact_list=contact_list, MPSubscribeMsgList=MPSubscribeMsgList)
except Exception as e:
print(e)
self.redirect('/login') class ContactHandler(tornado.web.RequestHandler):
def get(self, *args, **kwargs):
try:
user_list_url = "https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket={0}&r={1}&seq=0&skey={2}"
user_list_url = user_list_url.format(USER_RESULT_DICT['pass_ticket'],USER_RESULT_DICT['skey'],int(time.time()))
# print(user_list_url)
conn = requests.get(url = user_list_url , data={} , cookies=ALL_COOKIE_DICT , headers={'contentType':'application/json; charset=UTF-8','Referer':'https://wx.qq.com/?&lang=zh_CN'})
# print(conn.encoding) # ISO-8859-1
conn.encoding = 'utf-8'
USER_LIST_DICT.update(json.loads(conn.text))
# print(USER_LIST_DICT) self.render('contact_list.html', user_list_dict=USER_LIST_DICT, user=CURRENT_USER)
except Exception:
self.redirect('/login') class MessageGetHandler(tornado.web.RequestHandler):
def get(self):
global INIT_RESULT_DICT synccheck_url = 'https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck'
sync_data_list = []
for item in INIT_RESULT_DICT['SyncKey']['List']:
temp = "%s_%s" % (item['Key'], item['Val'])
sync_data_list.append(temp)
sync_data_str = "|".join(sync_data_list) payloads = {
"r" : int(time.time()),
"skey" : USER_RESULT_DICT['skey'],
"sid" : USER_RESULT_DICT['wxsid'],
"uin" : USER_RESULT_DICT['wxuin'],
"devicedid" : 'e' + repr(random.random())[2:17],
"synckey" : sync_data_str,
} res_sync = requests.get(synccheck_url,params=payloads,cookies = ALL_COOKIE_DICT)
print('Status: ', res_sync.text) if 'selector:"2"' in res_sync.text: fetch_msg_url = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&pass_ticket={2}'
fetch_msg_url = fetch_msg_url.format(USER_RESULT_DICT['wxsid'],USER_RESULT_DICT['skey'],USER_RESULT_DICT['pass_ticket']) payloads = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
},
'SyncKey' : INIT_RESULT_DICT['SyncKey'],
'rr' : int(time.time())
} # payload 转换成bytes
data = (json.dumps(payloads)).encode() # fetch_msg_data = copy.deepcopy(BASE_REQUEST_DICT)
# fetch_msg_data['SyncKey'] = INIT_RESULT_DICT['SyncKey']
# fetch_msg_data['rr'] = int(time.time()) request = urllib.request.Request(url=fetch_msg_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read()
# 获得初始化用户数据..
res_fetch_msg_dict = json.loads(data.decode('utf-8'))
INIT_RESULT_DICT['SyncKey'] = res_fetch_msg_dict['SyncKey'] for item in res_fetch_msg_dict['AddMsgList']:
print(item['FromUserName'], "---->", item['ToUserName'], ":::::", item['Content'])
self.write("ok") def post(self, *args, **kwargs): message = self.get_argument('message')
to_username = self.get_argument('username')
print(message,to_username)
#
send_url = 'https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}'
send_url = send_url.format(USER_RESULT_DICT['pass_ticket'])
time_str = str(int(time.time()))
payloads = {
'BaseRequest': {
'DeviceID': 'e' + repr(random.random())[2:17],
'Sid': USER_RESULT_DICT['wxsid'],
'Skey': USER_RESULT_DICT['skey'],
'Uin': int(USER_RESULT_DICT['wxuin']),
},
'Msg': {
'ClientMsgId': time_str,
'Content': message,
'FromUserName': CURRENT_USER['UserName'],
'LocalID': time_str,
'ToUserName': to_username,
'Type': 1,
},
'Scene': 0,
} # payload 转换成bytes
data = (json.dumps(payloads)).encode()
request = urllib.request.Request(url=send_url, data=data)
request.add_header(
'ContentType', 'application/json; charset=UTF-8') response = urllib.request.urlopen(request)
data = response.read() # 获得初始化用户数据..
res_fetch_msg_dict = json.loads(data.decode('utf-8')) print(res_fetch_msg_dict)
self.write(data) # self.write('ok') class TestHandler(tornado.web.RequestHandler):
def get(self): self.write("Hello, world 1 ")
time.sleep(10)
self.write("Hello, world 2 ")

Handler

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<style>
body{
margin: 0;
padding: 0;
}
.login_alt{
margin: 42px auto;
text-align: center;
}
.image{
display: block;
width: 270px;
height: 270px;
margin: 42px auto;
}
</style>
</head>
<body> <div class="login_alt"><h1>二维码登陆</h1></div> <img src="https://login.weixin.qq.com/qrcode/{{session_id}}" class="image"> <script src="{{ static_url('js/jquery-2.1.4.min.js')}}" ></script>
<script>
$(function () {
acquire_image();
});
function acquire_image() {
$.ajax({
url:'/login',
type:'POST',
success:function (data) {
if(data == '200'){
window.location.href = "/index";
}
else{
acquire_image();
} },
error:function (data) {
console.log('error');
}
})
}
</script>
</body>
</html>

index.html

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
.pg-header{
height: 48px;
background-color: #337ab7;
color: white;
line-height: 48px;
}
.container{}
.menu{
float: left;
width: 250px;
position: absolute;
top:48px;
bottom: 0;
left: 0;
overflow: auto;
background-color: black;
color: white;
}
.menu .item{
padding: 10px 5px;
}
.menu .item:hover{
background-color: #337ab7;
}
.content{
float: left;
position: absolute;
left: 259px;
top: 28px;
right: 0;
bottom: 0;
overflow: auto;
}
.hide{
display: none;
}
.chat-panel{
position: relative;
}
.chat-panel .title{
background-color: black;
height: 50px;
color: white;
}
.chat-panel .body{
border: 1px solid black;
height: 300px;
}
.chat-panel .footer{
height: 200px;
} </style>
</head>
<body style="margin: 0 auto;">
<div class="pg-header">
<span>登陆用户:{{user['NickName']}}</span>
<img class="hide" src="https://wx.qq.com{{user['HeadImgUrl']}}">
<span class="hide">{{user['UserName']}}</span>
</div> <div class="container">
<div class="menu">
{% for member in user_list_dict['MemberList'] %}
<div class="item" user-name="{{member['UserName']}}" nick-name="{{member['NickName']}}">{{member['NickName']}}</div>
{% end %}
</div>
<div class="content">
<div class="chat-panel hide">
<div class="title"></div>
<div class="body"></div>
<div class="footer">
<textarea id="message" class="msg"></textarea>
<input class="send" type="button" value="发送" onclick="SendMsg();"/>
<input class="send" type="button" value="sync" onclick="GetMsg();"/>
</div>
</div> </div>
</div>
<script src="{{ static_url('js/jquery-2.1.4.min.js')}}" ></script>
<script>
$(function(){
// BindSendEvent();
GetMsg();
BindSendEvent2();
});
// USERNAME = "";
// function BindSendEvent(){
// $(".menu").delegate('.item', "dblclick", function(){
// nickname = $(this).attr('nick-name');
// USERNAME = $(this).attr('user-name');
// $('.chat-panel .title').text(nickname);
// $('.chat-panel').removeClass('hide');
// })
// }
USERNAME = "";
function BindSendEvent2() {
$(".menu div").on('click',function () {
USERNAME = $(this).attr('user-name');
var nickname = $(this).attr('nick-name');
$('.chat-panel').removeClass('hide');
$('.chat-panel .title').text(nickname);
})
} function GetMsg(){
$.ajax({
url: "/msg",
type: "GET",
success: function (arg) {
GetMsg();
}
})
}
function SendMsg(){
var sendMsg = $('#message').val();
console.log({"username": window.USERNAME, "message": sendMsg});
$.ajax({
url: "/msg",
data: {"username": window.USERNAME, "message": sendMsg},
type: "POST",
success: function (arg) {
console.log(arg);
$('#message').val("");
}
})
}
</script>
</body>
</html>

contact_list.html

【Python之路】特别篇--微信Web网页版通信的全过程分析的更多相关文章

  1. Python之路,Day18 - 开发一个WEB聊天来撩妹吧

    Python之路,Day18 - 开发一个WEB聊天来撩妹吧   本节内容: 项目实战:开发一个WEB聊天室 功能需求: 用户可以与好友一对一聊天 可以搜索.添加某人为好友 用户可以搜索和添加群 每个 ...

  2. 挖掘微信Web版通信的全过程

    昨天是周末,在家闲得无聊,于是去weiphone.com逛了一圈,偶然发现有人发了一帖叫<微信 for Mac>,这勾起了我的好奇心,国内做Mac开发的人确实很少,对于那些能够独自开发一些 ...

  3. wechat 网页版通信全过程

    想要记录总结一下自己在这个小项目中所遇到的坑,以及解决问题的思路. 首先我觉得这个小项目挺有实际市场的,市场上有一定的需求量,这个就是驱动力吧.这个小项目的关键点是wechat网页版通信全过程,讲真挺 ...

  4. python之路入门篇

    一. Python介绍 python的创始人为吉多·范罗苏姆(Guido van Rossum).1989年的圣诞节期间,Guido开始写能够解释Python语言语法的解释器.Python这个名字,来 ...

  5. 微信号网页版api

    Django Wechat Api djangowechatapi是基于wxpy和django制作的web应用 安装 使用pip pip install djangowechatapi 源码安装 gi ...

  6. 挖掘微信Web版通信的全过程 [转]

    昨天是周末,在家闲得无聊,于是去weiphone.com逛了一圈,偶然发现有人发了一帖叫<微信 for Mac>, 这勾起了我的好奇心,国内做Mac开发的人确实很少,对于那些能够独自开发一 ...

  7. PHP 之CI框架+GatewayWorker+AmazeUI低仿微信聊天网页版

    html5开发的仿微信网页版聊天,采用html5+css3+jquery+websocket+amazeui等技术混合架构开发,实现了微信网页版的主要功能. 一.效果图 二.前端参考代码 <!D ...

  8. python之路基础篇

    基础篇 1.Python基础之初识python 2.Python数据类型之字符串 3.Python数据类型之列表 4.Python数据类型之元祖 5.Python数据类型之字典 6.Python Se ...

  9. Python之路(第一篇):Python简介和基础

    一.开发简介 1.开发:      开发语言:               高级语言:python.JAVA.PHP.C#..ruby.Go-->字节码                低级语言: ...

随机推荐

  1. spring session实现同域下单点登录

    Session会话管理 在Web项目开发中,Session会话管理是一个很重要的部分,用于存储与记录用户的状态或相关的数据:通常情况下session交由容器(tomcat)来负责存储和管理,但是如果项 ...

  2. work note

    eclipse git 察看历史 左边是提交的 import { NgModule } from '@angular/core'; import { IonicPageModule } from 'i ...

  3. java日志框架系列(3):logback框架配置详解

    1.Logback配置 1.配置步骤及默认配置 logback即可以通过编程式配置,也可以通过xml的形式配置. logback配置步骤: 1. 尝试在 classpath 下查找文件 logback ...

  4. (十二)mybatis 查询缓存

    目录 什么是查询缓存 图解查询缓存 一级缓存 二级缓存 禁用二级缓存 刷新缓存 二级缓存应用场景 二级缓存局限性 什么是查询缓存 mybatis 在查询数据的时候,会将数据存储起来,下次再次查询相同的 ...

  5. PB各对象常用事件

    1.window中的事件 事件名                  触发的时机 01.Activate            在窗口激活之前触发 02.Clicked             当用户用 ...

  6. kubernetes 健康检查和初始化容器

    Pod-hook:postStart:1.$ $ vim preStart-hook.yaml---apiVersion: v1kind: Podmetadata:  name: hook-demo1 ...

  7. 利用element-ui封装地址输入的组件

    我们前端做项目时,难免会遇到地址输入,多数情况下,我们都是提供一个省市三级联动,加上具体地址输入的Input输入框给用户,用以获取用户需要输入的真实地址.在需要对用户输入的数据进行校验的时候,我们会单 ...

  8. Django数据查询中对字段进行排序

    Django数据查询中对字段进行排序   第一种方法:使用order_by进行排序 Articlelist = Article.objects.filter(**kwargs).order_by('n ...

  9. gzip: stdin: not in gzip format 解决办法

    # sudo tar zxvf ./jdk-7ull-linux-i586.tar.gz -C /usr/lib/jvm gzip: stdin: not in gzip format tar: Ch ...

  10. bootstrap-table的简单使用

    先上效果图: 第一步:引用bootstrap-table的样式和js. @Styles.Render("~/assets/css/bootstrap.css") @Styles.R ...