web微信开发
群里接收消息时,使用广播,但需要刷新页面才能接收到广播内容。 - 轮询: 定时每秒刷新一次,当群不活跃时,群里的每个客户端都在刷新,对服务端压力太大。 - 长轮询:客户端连服务端,服务端一直不断开,也不回消息。夯住请求(Web微信,WebQQ),
假设夯住60s,60s后统一断开,然后客户端和服务端连接失败。然后紧接着再发送一次请求。相当于每分钟发送一次请求。
夯住不动只要有一个人发送消息,立刻断开带着新信息返回。只要消息来了就返回断开,这样就实时接收消息。 - 无消息,超时之后断开,客户端立即发送请求;
- 有消息,立即返回 轮询和长轮询利用的是http协议,这种请求是单向的,目前长轮询使用广泛。 - WebSocket
相比轮询和长轮询更好,客户端和服务端不断开,客户端和服务端可以相互接收消息。但是不是所有的浏览器都支持。目前还未大批量使用,以后是趋势。 1. 显示二维码
打开微信网页微信二维码登录时,未扫码登录时二维码登录页面和微信服务端一直在长轮询状态。
当手机扫码时,手机向微信服务端发送请求,直接拿到结果给微信网页端,页面登录状态改变。
二维码本质是图片,每次刷新页面图片都不同,每次后缀都不同。
向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&_=1504151392313(其中1504151392313是时间戳)
发送请求获取响应window.QRLogin.code = 200; window.QRLogin.uuid = "wdLetLlNoQ==",二维码随机字符串uuid:"wdLetLlNoQ==",根据uuid创建二维码。
src="https://login.weixin.qq.com/qrcode/wdLetLlNoQ=="
src="https://login.weixin.qq.com/qrcode/YeOkCQK4FQ==" - 获取uuid
- 根据uuid创建二维码 发送消息:
post_data = {
"BaseRequest": {
'xxx': 123123123123,
form
to
msg: 中文
'xxx': 123123123123,
}
}
# requests.post(json=post_data,headers={'cotnen':'json'})
# requests.post(data=json.dumps(post_data),headers={'cotnen':'json'}) # requests.post(data=json.dumps(post_data,ensure_ascii=False),headers={'cotnen':'json'})
开发web微信
- 打开wechat, 查看登录页面,猜想: 手机、web、微信服务器 - 二维码 - 扫码 - 确定登录
- 登录cookie
200,
redirict_url: ticket
- 凭证cookie - 初始化: 最近信息 - 显示头像
因为跨域头像无法显示显示:
我们的自己写的网站 http://127.0.0.1:
浏览器上保存这个网站http://127.0.0.1相关cookie
访问我们自己的网站的图片时,携带我们自己的cookie <img src='http://127.0.0.1' /> 自己写的网站访问微信的图片时,携带着我们网站的cookie,这就跨域了,不能带着我们本地的cookie去。 <img src='http://wx.qq.com.....' />
<img src='http://wx.qq.com.....' /> GET请求,get请求没有请求体,只有请求头
请求头:url: http://wx.qq.com.....
cookie: xxxx, # 没有微信的cookie
referer: http://127.0.0.1... **** # 因为是自己写的网站访问微信图片,referer默认当着当前url,微信可以通过referer阻拦访问,同样cookie也可以阻止访问 所以不直接向微信发消息获取头像,上面是浏览器发的消息,没法伪造请求头请求体cookie。可以向我们后台自己发,因为python的requests模块可以伪造这些信息。 <img src='http://127.0.0.1/img' />
v= requests.get(...,cookie,headers)
python的requests模块通过获取cookie,请求体信息。获取微信头像数据信息,然后再访问本地信息从而显示头像。 - 显示所有联系人
... - 发消息
current_user = req.session['INIT_DICT']['User']['UserName'] # session初始化,User.UserName
to = req.POST.get('to') # @dfb23e0da382f51746575a038323834a
msg = req.POST.get('msg')# asdfasdfasdf # session Ticket
# session Cookie
ticket_dict = req.session['TICKED_DICT']
ctime = int(time.time()*1000) post_data = {
"BaseRequest":{
"DeviceID": "e384757757885382",
'Sid': ticket_dict['wxsid'],
'Uin': ticket_dict['wxuin'],
'Skey': ticket_dict['skey'],
},
"Msg":{
"ClientMsgId":ctime,
"LocalID":ctime,
"FromUserName": current_user,
"ToUserName":to,
"Content": msg,
"Type": 1
},
"Scene": 0
} url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}".format(ticket_dict['pass_ticket'])
# res = requests.post(url=url,json=post_data) # application/json,json.dumps(post_data)
# res = requests.post(url=url,data=json.dumps(post_data),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) res = requests.post(url=url,data=json.dumps(post_data,ensure_ascii=False).encode('utf-8'),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data)
print(res.text) - 收消息
见代码
总结:
a. 分析Http请求
- 请求方式
- URL
- 浏览器看到数据的二种形式
Form Data? # form表单数据类型,request.post中取
{
k: 1,
k: “fds”,
k: [11,2,3,4],
k: {K:}, # 不能传字典,传字典只能把字典的key传到后台,发字典的时候需要转为字符串类型
}
request payload? # json数据类型,整个数据当成字符串发到后台。request.body中取
{
k: 1,
k: “fds”,
k: [11,2,3,4],
k: {K:},
} requests.post()
- 请求头:(爬网站进不去时,下面五个设置注意下,大部分可以爬取了)
user-agent: 当前用户使用的设备,知乎爬虫需要带user-agent。
Referer: "xxx"
content-type: application/json,
host cookie关键,cookie依附在请求头中 b. 代理
封IP时,代理设置
具体代码如下:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^login.html$', views.login),
url(r'^check_login.html$', views.check_login),
url(r'^index.html$', views.index),
url(r'^avatar.html$', views.avatar),
url(r'^contact_list.html$', views.contact_list),
url(r'^send_msg.html$', views.send_msg),
url(r'^get_msg.html$', views.get_msg),
]
urls.py
from django.shortcuts import render,HttpResponse
import requests
import time
import re
import json def ticket(html):
from bs4 import BeautifulSoup
ret = {}
soup = BeautifulSoup(html,'html.parser')
for tag in soup.find(name='error').find_all():
ret[tag.name] = tag.text
return ret def login(req):
if req.method == 'GET':
uuid_time = int(time.time() * 1000) base_uuid_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}"
uuid_url =base_uuid_url.format(uuid_time)
r1 = requests.get(uuid_url)
result = re.findall('= "(.*)";',r1.text)
uuid = result[0] req.session['UUID_TIME'] = uuid_time
req.session['UUID'] = uuid return render(req,'login.html',{'uuid':uuid})
def check_login(req):
response = {'code': 408,'data':None} ctime = int(time.time()*1000)
# base_login_url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-735595472&_={1}"
base_login_url = "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid={0}&tip=0&r=-735595472&_={1}"
# "https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=AbPhQTMl9w==&tip=0&r=-736896961&_=1503975440649"
login_url = base_login_url.format(req.session['UUID'],ctime)
r1 = requests.get(login_url)
if 'window.code=408' in r1.text:
# 无人扫码
response['code'] = 408
elif 'window.code=201' in r1.text:
# 扫码,返回头像
response['code'] = 201
response['data'] = re.findall("window.userAvatar = '(.*)';",r1.text)[0]
elif 'window.code=200' in r1.text:
# 扫码,并确认登录
req.session['LOGIN_COOKIE'] = r1.cookies.get_dict()
base_redirect_url = re.findall('redirect_uri="(.*)";',r1.text)[0]
redirect_url = base_redirect_url + '&fun=new&version=v2' # 获取凭证
r2 = requests.get(redirect_url)
ticket_dict = ticket(r2.text)
req.session['TICKED_DICT'] = ticket_dict
req.session['TICKED_COOKIE'] = r2.cookies.get_dict() # 初始化,获取最近联系人信息:工作号
post_data = {
"BaseRequest":{
"DeviceID": "e384757757885382",
'Sid': ticket_dict['wxsid'],
'Uin': ticket_dict['wxuin'],
'Skey': ticket_dict['skey'],
}
}
print('初始化开始...')
# 用户初始化,讲最近联系人个人信息放在session中
init_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=-740036701&pass_ticket={0}".format(ticket_dict['pass_ticket'])
r3 = requests.post(
url=init_url,
json=post_data
)
r3.encoding = 'utf-8'
init_dict = json.loads(r3.text)
req.session['INIT_DICT'] = init_dict
response['code'] = 200 return HttpResponse(json.dumps(response)) def avatar(req):
prev = req.GET.get('prev') # /cgi-bin/mmwebwx-bin/webwxgeticon?seq=602427528
username = req.GET.get('username') # @fb736164312cbcdb9abe746d81e24835
skey = req.GET.get('skey') # @crypt_2ccf8ab9_4414c9f723cbe6e9caca48b7deceff93
img_url = "https://wx.qq.com{0}&username={1}&skey={2}".format(prev,username,skey) cookies= {}
cookies.update(req.session['LOGIN_COOKIE'])
cookies.update(req.session['TICKED_COOKIE'])
print(img_url)
res = requests.get(img_url,cookies=cookies,headers={'Content-Type': 'image/jpeg'})
return HttpResponse(res.content) def index(req):
"""显示最近联系人"""
# https://wx.qq.com
return render(req,'index.html') def contact_list(req):
"""
获取所有联系人
:param req:
:return:
"""
ctime = int(time.time()*1000)
base_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&r={0}&seq=0&skey={1}"
url = base_url.format(ctime,req.session['TICKED_DICT']['skey'])
cookies = {}
cookies.update(req.session['LOGIN_COOKIE'])
cookies.update(req.session['TICKED_COOKIE']) r1 = requests.get(url,cookies=cookies)
r1.encoding = 'utf-8' user_list = json.loads(r1.text) return render(req, 'contact_list.html',{'user_list':user_list}) def send_msg(req):
"""
发送消息
:param req:
:return:
"""
current_user = req.session['INIT_DICT']['User']['UserName'] # session初始化,User.UserName
to = req.POST.get('to') # @dfb23e0da382f51746575a038323834a
msg = req.POST.get('msg')# asdfasdfasdf # session Ticket
# session Cookie
ticket_dict = req.session['TICKED_DICT']
ctime = int(time.time()*1000) post_data = {
"BaseRequest":{
"DeviceID": "e384757757885382",
'Sid': ticket_dict['wxsid'],
'Uin': ticket_dict['wxuin'],
'Skey': ticket_dict['skey'],
},
"Msg":{
"ClientMsgId":ctime,
"LocalID":ctime,
"FromUserName": current_user,
"ToUserName":to,
"Content": msg,
"Type": 1
},
"Scene": 0
} url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket={0}".format(ticket_dict['pass_ticket'])
# res = requests.post(url=url,json=post_data) # application/json,json.dumps(post_data)
# res = requests.post(url=url,data=json.dumps(post_data),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data) res = requests.post(url=url,data=json.dumps(post_data,ensure_ascii=False).encode('utf-8'),headers={'Content-Type': "application/json"}) # application/json,json.dumps(post_data)
print(res.text)
return HttpResponse('...') def get_msg(req):
"""
长轮询获取消息
:param req:
:return:
"""
# 检查是否有消息到来
ctime = int(time.time()*1000)
ticket_dict = req.session['TICKED_DICT']
check_msg_url = "https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck" cookies = {}
cookies.update(req.session['LOGIN_COOKIE'])
cookies.update(req.session['TICKED_COOKIE']) synckey_dict = req.session['INIT_DICT']['SyncKey']
synckey_list = []
for item in synckey_dict['List']:
tmp = "%s_%s" %(item['Key'],item['Val'])
synckey_list.append(tmp)
synckey = "|".join(synckey_list) r1 = requests.get(
url=check_msg_url,
params={
'r': ctime,
"deviceid": "e384757757885382",
'sid': ticket_dict['wxsid'],
'uin': ticket_dict['wxuin'],
'skey': ticket_dict['skey'],
'_': ctime,
'synckey': synckey
},
cookies=cookies
)
print(r1.text)
if '{retcode:"0",selector:"0"}' in r1.text:
return HttpResponse('...') # 有消息,获取消息
base_get_msg_url = "https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid={0}&skey={1}&lang=zh_CN&pass_ticket={2}"
get_msg_url = base_get_msg_url.format(ticket_dict['wxsid'],ticket_dict['skey'],ticket_dict['pass_ticket']) post_data = {
"BaseRequest":{
"DeviceID": "e384757757885382",
'Sid': ticket_dict['wxsid'],
'Uin': ticket_dict['wxuin'],
'Skey': ticket_dict['skey'],
},
'SyncKey': req.session['INIT_DICT']['SyncKey']
}
r2 = requests.post(
url = get_msg_url,
json=post_data,
cookies=cookies
)
r2.encoding = 'utf-8'
# 接受到消息: 消息,synckey
msg_dict = json.loads(r2.text)
print(msg_dict)
for msg in msg_dict['AddMsgList']:
print('您有新消息到来:',msg['Content'])
init_dict = req.session['INIT_DICT']
init_dict['SyncKey'] = msg_dict['SyncKey']
req.session['INIT_DICT'] = init_dict return HttpResponse('...')
views.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div>
<img style="height: 400px;width: 400px;" id="img" src="https://login.weixin.qq.com/qrcode/{{uuid}}">
</div> <script src="/static/jquery-1.12.4.js"></script> <script> $(function () {
checkLogin();
}); function checkLogin() {
$.ajax({
url: '/check_login.html',
type: 'get',
data: {},
dataType: 'JSON',
success:function (arg) {
if(arg.code == 408){
checkLogin();
}else if(arg.code == 201){
$('#img').attr('src',arg.data);
checkLogin();
}else {
location.href = "/index.html"
} }
})
}
</script>
</body>
</html>
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>个人信息</h1>
<img src="/avatar.html?prev={{ request.session.INIT_DICT.User.HeadImgUrl }}">
<h2>{{ request.session.INIT_DICT.User.NickName }}</h2>
<h1>最近联系人</h1>
<ul>
{% for user in request.session.INIT_DICT.ContactList %}
<li><img src="/avatar.html?prev={{ user.HeadImgUrl }}"> {{ user.UserName }} {{ user.NickName }}</li>
{% endfor %}
</ul> <a href="/contact_list.html">更多联系人</a>
<h1>公众号信息</h1>
</body>
</html>
index
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>发送消息</h1>
<input placeholder="接受者" id="to" />
<input placeholder="消息内容" id="msg" />
<input type="button" value="发送" onclick="sendMsg();" />
<h1>用户列表({{ user_list.MemberCount }})</h1>
{% for user in user_list.MemberList %}
<div username="{{ user.UserName }}">
{# <img style="width: 50px;height: 50px;" src="/avatar.html?prev={{ user.HeadImgUrl }}"><span>{{ user.NickName }}</span>#}
<span>{{ user.NickName }}</span>
</div>
{% endfor %}
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function () {
getMsg();
}); function getMsg() {
$.ajax({
url: '/get_msg.html',
type: 'GET',
success:function (arg) {
//console.log(arg);
getMsg();
}
})
} function sendMsg() {
$.ajax({
url: '/send_msg.html',
type: "POST",
data: {'to': $('#to').val(), 'msg': $('#msg').val()},
success:function (arg) {
alert(arg);
}
})
}
</script>
</body>
</html>
contact_list.html
web微信开发的更多相关文章
- web微信开发总结
这两天使用Django开发了web微信,实现了显示联系人以及收发消息的功能. 总结下这过程中使用到的一些知识. 1 http请求 通过chrome浏览器自带的开发者工具查看每次请求的信息,分析请求,包 ...
- web微信开发前期准备最新详细流程
一.申请配置测试公众号与配置本地服务器 1.打开浏览器,输入:http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login,微信扫码确 ...
- Web微信开发工具无法输入中文?官方bug
Ctrl+shift+w 重启工具就OK啦
- 微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能
在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能. 如果需要在移动浏览器中实现拨打电话,发送email,美国服 ...
- 微信公众平台开发:Web App开发入门
WebApp与Native App有何区别呢?Native App:1.开发成本非常大.一般使用的开发语言为JAVA.C++.Objective-C.2.更新体验较差.同时也比较麻烦.每一次发布新的版 ...
- Asp.Net Web API开发微信后台
如果说用Asp.Net开发微信后台是非主流,那么Asp.Net Web API的微信后台绝对是不走寻常路. 需要说明的是,本人认为Asp.Net Web API在开发很多不同的请求方法的Restful ...
- 带领技术小白入门——基于java的微信公众号开发(包括服务器配置、java web项目搭建、tomcat手动发布web项目、微信开发所需的url和token验证)
微信公众号对于每个人来说都不陌生,但是许多人都不清楚是怎么开发的.身为技术小白的我,在闲暇之余研究了一下基于java的微信公众号开发.下面就是我的实现步骤,写的略显粗糙,希望大家多多提议! 一.申请服 ...
- [HTML] 微信开发之移动手机WEB页面(HTML5)Javascript实现一键拨号及短信发送功能
在做一个微信的微网站中的一个便民服务电话功能的应用,用到移动web页面中列出的电话号码,点击需要实现调用通讯录,网页一键拨号的拨打电话功能. 如果需要在移动浏览器中实现拨打电话,发送email,美国服 ...
- delphi 实现微信开发(1) (使用kbmmw web server)
原文地址:delphi 实现微信开发(1)作者:红鱼儿 大体思路: 1.用户向服务号发消息,(这里可以是个菜单项,也可以是一个关键词,如:注册会员.) 2.kbmmw web server收到消息,生 ...
随机推荐
- PHP魔术函数、魔术常量、预定义常量
一.魔术函数(13个) 1.__construct() 实例化对象时被调用, 当__construct和以类名为函数名的函数同时存在时,__construct将被调用,另一个不被调用. 2.__des ...
- C++单例模式实例
定义:在某些情况下,我们设计中的对象只需要一个,比方说:线程池(threadpool).缓存(cache).对话框.处理偏好设置和注册表对象.日志对象.充当打印机.显卡等设备的驱动程序的对象等.事实上 ...
- 【bzoj2521】[Shoi2010]最小生成树 网络流最小割
题目描述 Secsa最近对最小生成树问题特别感兴趣.他已经知道如果要去求出一个n个点.m条边的无向图的最小生成树有一个Krustal算法和另一个Prim的算法.另外,他还知道,某一个图可能有多种不同的 ...
- 【Luogu】P2598狼和羊的故事(最小割转最大流)
题目链接 最小割水题.入点向白点连边,白点向白点.黑点和空点连边,空点向空点和黑点连边,黑点向黑点和汇点连边.然后跑最大流即可. 话说Fd最近怎么光做水题啊……一点用都没有……qwq 我太菜了,做完一 ...
- 北京集训TEST12——PA( Mortal Kombat)
题目: Description 有一天,有N个外星人企图入侵地球.地球派出全球战斗力最强的M个人代表人类对抗外星人.根据外星的战斗规则,每个外星人应该分别与一名地球人对战(不同的外星人要与不同的地球人 ...
- javaweb学习总结(十八)——JSP属性范围(转)
所谓的属性范围就是一个属性设置之后,可以经过多少个其他页面后仍然可以访问的保存范围. 一.JSP属性范围 JSP中提供了四种属性范围,四种属性范围分别指以下四种: 当前页:一个属性只能在一个页面中取得 ...
- 【CF1020C】Elections(贪心)
题意: Berland地区的腐败现象非常常见. 马上有一场选举,你事先知道了选民和政党的数量,分别为 n 和 m ,对于每一位选民,你知道他将要选举哪一个政党, 不过,每一位选民都会在接受一定数额的金 ...
- 一个老忘且非常有用的jquery动画方法 网页上卷
$('html,body').animate({scrollTop:800+'px'},500) //网页上卷800像素 在半秒之内
- Atcoder CODE FESTIVAL 2017 qual B D - 101 to 010 dp
题目链接 题意 对于一个\(01\)串,如果其中存在子串\(101\),则可以将它变成\(010\). 问最多能进行多少次这样的操作. 思路 官方题解 转化 倒过来考虑. 考虑,最终得到的串中的\(' ...
- CUDA程序计时
之前写的CUDA程序,想测量一下性能,网上很多用的是CPU端计时,很不准确.翻了一下书,发现这里应该使用事件来计时. CUDA中的事件本质上是一个GPU时间戳,这个时间戳是在用户指定的时间点上记录的. ...