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收到消息,生 ...
随机推荐
- linux各种版本查看方法
1.linux内核版本 cat /proc/version Linux version 4.13.0-39-generic (buildd@lgw01-amd64-038) (gcc version ...
- ACM程序设计选修课——Problem F:(ds:图)旅游规划(优先队列+SPFA)
问题 F: (ds:图)旅游规划 时间限制: 1 Sec 内存限制: 128 MB 提交: 14 解决: 4 题目描述 有了一张自驾旅游路线图,你会知道城市间的高速公路长度.以及该公路要收取的过路 ...
- Linux环境CentOS6.9安装配置Elasticsearch6.2.2最全详细教程
Linux环境CentOS6.9安装配置Elasticsearch6.2.2最全详细教程 前言 第一步:下载Elasticsearch6.2.2 第二步:创建应用程序目录 第四步:创建Elastics ...
- 事件获取目标 currentTarget target srcElement三者之间的区别和联系
currentTarget 指的是触发事件的当前对象,可以是冒泡和捕获的对象,不一定是点击或者鼠标移入等事件的直接触发对象.可以是他的父元素等. target 指的是事件触发的直接对象.IE有兼容 ...
- 喵星球上的点名(bzoj 2754)
Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...
- APUE 学习笔记(五) 进程环境
1.main函数 C程序总是从main函数开始执行,当内核执行C程序时,在调用main函数之前先调用exec函数从内核获取命令行参数和环境变量值 2.进程终止 正常终止: (1)在main函数内执 ...
- vue2.0组件入门
如何定义一个组件 在根目录src/components/文件夹下新建组件的文件夹Footer.vue组件 在Footer.vue中 <template> <div class=&qu ...
- 学习在requirejs下如何使用underscore.js模板
近期在学习underscore.js 这个小而美的js库,是前端 MVC 框架backbone依赖库,他的模板方法主要应用场景是ajax交互过程到页面需要大量的字符串拼接,这部分如果一旦不够仔细就很容 ...
- ng-include
ng-include可以作为标签或者属性来使用,作用是引入公用文件. <div ng-include="'header.html'"></div> 注意里面 ...
- gpio_direction_output vs gpio_set_value之间的使用关系
在Linux驱动中常常会碰到gpio_set_value(port_num,0/1)或gpio_direction_output (port_num,0/1) 这两者有什么关系呢gpio_set_va ...