前言
工作之余突然对微信的网页版的协议通信感兴趣可以搞一波了解下。毕竟网页版容易抓,靠一个浏览器的开发者工具的Network就开始抓包解析了。
这是我2019年03月写的文档。因为微信的协议可能随时存在变动,以下内容可以参考一波。本人爬虫的方法很简单,就是尽一切方法模仿被爬虫者的行为。
该文档目前只写到获取最新消息。先记录一波。有空更新
项目地址:楠尼玛大帝 / wxWebR(Java版)
流程步骤
(这里只说到成功登录到微信并获取最新消息,因为只是模仿到这里,其他的全部都已经清清楚楚只是看你调不调用而已了。该拿到的参数都有了)
- 去获取一个uuid,可以根据这个uuid获取一张二维码登录的图片
- 微信客户端扫描该二维码,在客户端确认登录。
- 浏览器不停的调用一个接口,如果返回登录成功,则调用登录接口
- 循环遍历一个检查是否有新消息的接口。
- 如果新消息的接口返回有新消息的状态码去获取消息接口。(完)
WebWechat API
(这里也只说到成功登录到微信并获取最新消息。其他可以自行去抓包或者参考其他文档比如码云参考->python版(老版本)
第一步获取UUID(参考方法 getUUID)
API | 获取 UUID |
---|---|
url | https://login.wx.qq.com/jslogin |
method | POST |
data | URL Encode(text/javascript) |
params |
appid: 应用ID 参考:wx782c26e4c19acffb redirect_uri 转发地址 参考:https://wx.qq.com/cgi-bin/mmw...fun: 应用类型 参考:new lang: 语言 参考:zh_CN _: 时间戳 参考:当前时间毫秒级13位数的时间戳 |
返回数据(String):
window.QRLogin.code = 200; window.QRLogin.uuid = "xxx"
注:目前看来参数除了_当前时间毫秒级13位数的时间戳
其他都是固定的值。在微信给的js里面index_ad43596.js里搜索API_jsLogin可以得到这些参数。而且大部分不理解参数都可以从这个js文件得到答案。![]()
显示二维码就不浪费时间直接就是拿第一步拿到的uuid拼接就是二维码地址:https://login.weixin.qq.com/q...{uuid}
第二步等待扫码登录即微信确认登录
API | 获取二维码扫描登录状态 |
---|---|
url | https://login.wx.qq.com/cgi-b... |
method | GET(text/javascript) |
params |
tip :这个东西根据浏览器走的话就是第一次为1,后面都是为0,网上说是扫码状态 loginicon 参考:true uuid : 获取到的uuid _ : 当前时间毫秒级13位数的时间戳 |
返回数据(String):
window.code=xxx;
xxx:
【未扫码的话】 -> window.code=408;
【手机扫码但是未登录】 -> window.code = 201;
【手机取消登录】 -> window.code=400;
【手机授权登录】 -> window.code=200;
当返回200时:
wechatLoginStatus:window.code=200; window.redirect_uri=
"https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=XXX&uuid=XXX&lang=XXX&scan=XXX";
当返回201时可以获取扫码用户头像的base64数据哦(好像没啥用)
注: 像这种成功的话得到这种参数自己一定存起来,下一步肯定要用即ticket
,lang
,uuid
,scan
第三步扫描成功去获取你的登录凭据
API | 获取登录凭据 |
---|---|
url | https://wx2.qq.com/cgi-bin/mm... |
method | GET(text/plain;charset=utf-8) |
params |
ticket : ticket uuid : uuid lang : lang scan : scan fun : 参考:new version 参考:v2 |
返回数据(XML):
<error>
<ret>0</ret>
<message>OK</message>
<skey>xxx</skey>
<wxsid>xxx</wxsid>
<wxuin>xxx</wxuin>
<pass_ticket>xxx</pass_ticket>
<isgrayscale>1</isgrayscale>
</error>
核心数据点: skey
, wxsid
, wxuin
, pass_ticket
注: 切记,第三步会返回cookie,存下来,这里这个接口会得到cookie的。 cookie
哪里用到我会说明的。
第四步微信初始化
API | 微信初始化 |
---|---|
url |
https://wx2.qq.com/cgi-bin/mm...lang &pass_ticket=pass_ticket
|
method | POST |
data | JSON(application/json; charset=UTF-8) |
header | Content-Type: application/json; charset=UTF-8 |
params | { BaseRequest: { Uin: wxuin , Sid: wxsid , Skey: skey , DeviceID: xxx, } } |
注:r
这个参数是通过js的一个 ~new Date 这是一个类似二进制反转的一个,可自行百度或者打开控制台输出一下就知道了。DeviceID
这个参数是一个简单js拼接出来的参数 "e"+(Math.random().toFixed(15)).substring(2, 17)
返回数据(JSON):
{
"BaseResponse": {
"Ret": 0,
"ErrMsg": ""
},
"Count": 11,
"ContactList": [...],
"SyncKey": {
"Count": 4,
"List": [
{
"Key": 1,
"Val": 635705559
},
...
]
},
"User": {
"Uin": xxx,
"UserName": xxx,
"NickName": xxx,
"HeadImgUrl": xxx,
"RemarkName": "",
"PYInitial": "",
"PYQuanPin": "",
"RemarkPYInitial": "",
"RemarkPYQuanPin": "",
"HideInputBarFlag": 0,
"StarFriend": 0,
"Sex": 1,
"Signature": "Apt-get install B",
"AppAccountFlag": 0,
"VerifyFlag": 0,
"ContactFlag": 0,
"WebWxPluginSwitch": 0,
"HeadImgFlag": 1,
"SnsFlag": 17
},
"ChatSet": xxx,
"SKey": xxx,
"ClientVersion": 369297683,
"SystemTime": 1453124908,
"GrayScale": 1,
"InviteStartCount": 40,
"MPSubscribeMsgCount": 2,
"MPSubscribeMsgList": [...],
"ClickReportInterval": 600000
}
第四步中获取 SyncKey
, User
后面的消息监听用。
第五步消息检查是否有新消息(这里要带上cookie)
API | synccheck |
---|---|
url |
https://webpush.wx2.qq.com/cg...skey &sid=wxsid &uin=wxuin &deviceid=xxx&synckey=Synckey &_=xxx |
method | POST |
data | JSON(text/javascript) |
header | ContentType: application/json; charset=UTF-8 |
params |
返回数据(String):
window.synccheck={retcode:"xxx",selector:"xxx"}
retcode:
0 正常
1100 失败/登出微信
1101 在其他地方登录了微信
1102 无凭据或者凭据已失效
selector:
0 正常
2 新的消息
7 进入/离开聊天界面
注: deviceid 和上面的DeviceID
这个参数是一样的:一个简单js拼接出来的参数 "e"+(Math.random().toFixed(15)).substring(2, 17)
我们叫他时间戳取反
吧
_是一个当前时间毫秒级13位数的时间戳
第六步获取最新消息(要带上Cookie)
API | webwxsync |
---|---|
url |
https://wx2.qq.com/cgi-bin/mm...wxsid &skey=Skey
|
method | POST |
data | JSON(text/plain) |
header | ContentType: application/json; charset=UTF-8 |
params | { BaseRequest: { Uin: User下的Uin , Sid: wxsid , Skey: skey , DeviceID: DeviceID }, SyncKey: syncKey , rr: 时间戳取反 } |
返回数据(JSON):
{
'BaseResponse': {'ErrMsg': '', 'Ret': 0},
'SyncKey': {
'Count': 不确定个数,
'List': [
{'Val': 636214192, 'Key': 1},
...
]
},
'ContinueFlag': 0,
'AddMsgCount': 1,
'AddMsgList': [
{
'FromUserName': '',
'PlayLength': 0,
'RecommendInfo': {...},
'Content': "",
'StatusNotifyUserName': '',
'StatusNotifyCode': 5,
'Status': 3,
'VoiceLength': 0,
'ToUserName': '',
'ForwardFlag': 0,
'AppMsgType': 0,
'AppInfo': {'Type': 0, 'AppID': ''},
'Url': '',
'ImgStatus': 1,
'MsgType': 51,
'ImgHeight': 0,
'MediaId': '',
'FileName': '',
'FileSize': '',
...
},
...
],
'ModChatRoomMemberCount': 0,
'ModContactList': [],
'DelContactList': [],
'ModChatRoomMemberList': [],
'DelContactCount': 0,
...
}
注 这一块比较要特别注意,得到消息后发现返回的信息也是有SyncKey
这个要更新一波直接拿下来替换自己的旧的,不然第五步检查消息是会出现问题就是刷的特别快,而且消息是不正确的。因为真正成功访问的到是微信的请求不会立即返回,一个请求会跑的比较久至少几十秒,因为防止疯狂遍历,不用担心因为一旦有数据返回微信服务器会立刻返回数据给你进入下一阶段的循环。这一步也是比较坑的就是SyncKey
要记得更新就行了。还有一个很骚的点就是_
这个参数虽然是毫秒级的13位时间戳,但是在微信第一次获取后他不会再去new了,就是一直+1+1;
致谢
本项目受到以下项目的启发:
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。