文章所使用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. KMP算法最浅显理解——一看就明白

    https://blog.csdn.net/starstar1992/article/details/54913261 说明 KMP算法看懂了觉得特别简单,思路很简单,看不懂之前,查各种资料,看的稀里 ...

  2. 【转载】SpringBoot-配置发送邮件遇到的一些问题

    前言:前一天调用163邮箱发送邮件还么有问题,今天再调用就各种发送不成功,害的我都关闭授权,还花了一毛钱短信费重新开启授权,最后百度到了一篇文章,非常贴切,在此转载下. 本人遇到的错误代码是554,邮 ...

  3. java方法可变参数研究

    1 问题引出 (1)缘由 最近在研究如何在项目中引入Redis缓存,于是遇到可变参数这个疑惑点,之前没有好好研究过,为了避免项目后期出现问题. (2)项目相关技术 SpringBoot Redis K ...

  4. Nopcommerce 使用Task时dbcontext关闭问题

    1.开启一个线程 Task.Run(() => { CreatPrintImage(preViewModel.DiyProductGuid); }); 2.线程代码 /// <summar ...

  5. Javascript的学习清单

    Javascript的学习清单 Javascript学习资源 程序员必读书籍 深入理解JavaScript系列 es6教程 jQuery中文文档 vue官网 zeptojs中文版 常用的插件与UI组件 ...

  6. MUI 跨域请求web api

    由于刚接触MUI框架,所以在跨域问题上花了一点时间.希望我的方式能帮你少走点弯路(大神就直接过里吧)! 首先,遇到这个问题,各种百度.其中说法最多的是将mui,js文件里的 setHeader('X- ...

  7. MVP架构的一个小例子

    主角: MVP是一种编程的架构模式,M=Model,负责提供数据:V=View,负责显示数据:P=Presenter,负责处理数据. 应用例子: csharp写的一个qq机器人. 一.Model层 获 ...

  8. 多线程--volatile

    在解释volatile关键字之前,先说说java的指令重排以及代码的执行顺序. 指令重排: public void sum(){ int x = 1; int y = 2; int x = x + 1 ...

  9. Image Processing and Analysis_8_Edge Detection:Finding Edges and Lines in Images by Canny——1983

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...

  10. Mysql(四):数据操作

    一 介绍 MySQL数据操作: DML ======================================================== 在MySQL管理软件中,可以通过SQL语句中的 ...