2

前言

工作之余突然对微信的网页版的协议通信感兴趣可以搞一波了解下。毕竟网页版容易抓,靠一个浏览器的开发者工具的Network就开始抓包解析了。
这是我2019年03月写的文档。因为微信的协议可能随时存在变动,以下内容可以参考一波。本人爬虫的方法很简单,就是尽一切方法模仿被爬虫者的行为。
该文档目前只写到获取最新消息。先记录一波。有空更新

项目地址:楠尼玛大帝 / wxWebR(Java版)

流程步骤

(这里只说到成功登录到微信并获取最新消息,因为只是模仿到这里,其他的全部都已经清清楚楚只是看你调不调用而已了。该拿到的参数都有了)

  1. 去获取一个uuid,可以根据这个uuid获取一张二维码登录的图片
  2. 微信客户端扫描该二维码,在客户端确认登录。
  3. 浏览器不停的调用一个接口,如果返回登录成功,则调用登录接口
  4. 循环遍历一个检查是否有新消息的接口。
  5. 如果新消息的接口返回有新消息的状态码去获取消息接口。(完)

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数据哦(好像没啥用)
注: 像这种成功的话得到这种参数自己一定存起来,下一步肯定要用即ticketlanguuidscan

第三步扫描成功去获取你的登录凭据

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;

致谢

本项目受到以下项目的启发:


楠宝宝
264 声望14 粉丝

啊哈哈哈哈哈嗝,我休息一下,啊哈哈哈哈哈哈嗝