从零开始仿写一个B站客户端之抓包接口

章节

从零开始仿写一个B站客户端之-编译ijkplayerandroid

从零开始仿写一个B站客户端之-抓包B站接口git

从零开始仿写一个B站客户端之-使用ijkplayer打造一个通用的播放器github

从零开始仿写一个B站客户端之-总体架构设计和网络请求封装web

既然要仿写一个客户端,那么数据从哪儿来呢?刚开始打算用springboot本身写后端,而后去学吧,第一个hello world项目就运行不起了(;′⌒`),这不是明示我劝退吗。因此果断抓包B站接口,不须要花费时间写后端代码,还还不缺数据来源。spring

抓包工具使用的是fiddler,对手机上的B站客户端进行抓包。须要注意的是必须确保安装fiddler的电脑和手机在同一个局域网环境下 。json

电脑上的配置

  1. 配置fiddler代理端口号
  2. 配置https,也能够不配,目前抓包出来的B站接口都是http的,以防万一仍是配上吧。

安装好fiddler以后,在Tools->Options->Connections中这样配:后端

Fiddler listens on port是手机链接fiddler时的代理端口号,.Allow remote computers to connect是容许远程(手机端)发送请求。api

配置Https:浏览器

点击ok以后,在手机上访问电脑ip+8888,电脑ip查看方式是在cmd中输入ipconfig:安全

手机上的配置

我这里的ip是192.168.0.134,手机端用自带的浏览器访问: http://192.168.0.134:8888下载证书并安装。点击最下面那个蓝色连接安装证书:

emmm...小米手机不能直接安装,须要从更多设置 ->系统安全->加密与凭据->从sd卡中安装证书

安装证书完成以后,须要修改wifi的网络,手动设置代理,代理服务器主机名为电脑的IP地址,代理端口为在fiddler里设置的端口号,保存后,fiddler将可以收到手机上的请求信息:

准备就绪,能够在手机上打开B站客户端开始抓包了:

接口分析

以直播up主的粉丝榜接口为例,抓到的接口是这个样子的:

http://api.live.bilibili.com/rankdb/v2/RoomRank/mobileMedalRank?actionKey=appkey&appkey=1d8b6e7d45233436&build=5400000&channel=bilibiil140&device=android&mobi_app=android&page=1&platform=android&roomid=2910685&ruid=33588706&ts=1556159005&sign=fdc0eb4340508ad9a62d1a27146a4183
复制代码

这太长了,能够剔除一些无用信息,变为下面这样:

http://api.live.bilibili.com/rankdb/v2/RoomRank/mobileMedalRank?page=1&roomid=2910685&ruid=33588706
复制代码

其中page表示第一页,roomid表示房间号,ruid表示up主的uid。

有些接口是能够剔除的,可是有些接口是必需要一家人整整齐齐的,好比获取直播up主的uid信息就须要所有参数:

http://api.live.bilibili.com/xlive/app-room/v1/index/getInfoByRoom?actionKey=appkey&appkey=1d8b6e7d45233436&build=5400000&channel=bilibiil140&device=android&mobi_app=android&platform=android&room_id=2910685&ts=1556157467&sign=811b018c9e54efad87e4ec16a76cd111
复制代码

前面的参数几乎都是固定的,除了最后的roomidts以及sign,若是有一个参数不正确,服务器就会返回下面的错误:

{
	"code": -3,
	"message": "API校验密匙错误",
	"ttl": 1
}
复制代码

API校验密匙就是最后一个参数sign,它是经过前面的参数排序以后,加上SecretKey 作md5生成的,其中SecretKey存放在了so库中,具体操做参考了@Misery_Dx的:

仿B站Android客户端系列(启动篇)

获取sign的代码:

fun getSign(map: Map<String, Any>): String {
        //拼接参数(按顺序) + SecretKey
        val orignSign = getUrlParamsByMap(map) + SECRET_KEY
        //进行MD5加密
        var sign = ""
        try {
            sign = MD5Util.getMD5(orignSign).trim()
            Log.i(TAG, "加密后的sign: $sign")
        } catch (e: NoSuchAlgorithmException) {
            Log.e(TAG, "sign encryption failed: ${e.printStackTrace()}")
        }
        return sign
    }

/** * 将map转换成url参数 * @param map * @return */
    fun getUrlParamsByMap(map: Map<String, Any>): String {
        var params =  StringBuffer()
        val it = map.iterator()
        while (it.hasNext()) {
            val str = it.next()
            params.append(str.key)
            params.append("=")
            params.append(str.value)
            if (it.hasNext()) {
                params.append("&")
            }
        }
        return params.toString()
    }
复制代码

能获取到sign,大部分的问题就解决了。

直播弹幕的获取参考@lovelyyoshino直播弹幕 WebSocket 协议,嗯。。。没搞定,不知道是我发送的封包数据有问题仍是B站直播弹幕协议变了,若是有大佬能搞定,但愿能不吝赐教,感谢感谢~。

这是发送数据封包的方法:

/** * @param cmd 命令 * @param data 数据包 */
    private fun sendCmd(cmd: Int, data: ByteArray, webSocket: WebSocket){
        var buffer = ByteBuffer.allocate(16 + data.size)
        buffer.order(ByteOrder.BIG_ENDIAN)  //字节序为大端模式
        buffer.putInt(16 + data.size)
        buffer.putShort(16)  //头部长度
        buffer.putShort(1)  //协议版本,目前是1
        buffer.putInt(cmd)  //操做码(封包类型)
        buffer.putInt(1)  //sequence,能够取常数1
        buffer.put(data)
        webSocket.send(ByteString.of(buffer))
    }
复制代码

这是使用okhttp3自带的websocket实现的加入房间:

private var webSocket: okhttp3.WebSocket? =null
    fun joinRoom(roomId:Int){
        var client = OkHttpClient.Builder().build()
        var request = Request
            .Builder()
            .url(GlobalProperties.LIVE_DANMAKU_URL)
            .build()
        webSocket = client.newWebSocket(request,object : WebSocketListener(){
            override fun onOpen(webSocket: okhttp3.WebSocket, response: Response) {
                super.onOpen(webSocket, response)
                var inRoomMessage = JSONObject()
                inRoomMessage.put("clientver","1.6.3")
                inRoomMessage.put("platform","web")
                inRoomMessage.put("protover",2)
                inRoomMessage.put("roomid",roomId)  //必填
                inRoomMessage.put("type",2)
                var bytes = inRoomMessage.toString().toByteArray(Charsets.UTF_8)
                sendCmd(7, bytes, webSocket)
                Log.d(TAG,"websocket链接成功,发送进房消息$inRoomMessage")
            }

            override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
                super.onMessage(webSocket, bytes)
                Log.d(TAG,"websocket接收消息$bytes")
            }
            override fun onClosed(webSocket: okhttp3.WebSocket, code: Int, reason: String) {
                super.onClosed(webSocket, code, reason)
                Log.d(TAG,"websocket断开链接")
                exitRoom()
            }

            override fun onFailure(webSocket: okhttp3.WebSocket, t: Throwable, response: Response?) {
                super.onFailure(webSocket, t, response)
                Log.d(TAG,"websocket链接失败: $response , throw: $t")
                exitRoom()
            }
        })
    }
复制代码

第一次链接成功以后,发送进房消息,而后链接就当即断开了,应该是我发送的数据不对,才致使服务端主动断开链接的。

目前暂时是使用直播历史评论抓取,而不是实时的:

http://api.live.bilibili.com/xlive/app-room/v1/dM/gethistory?room_id=2910685
复制代码

直播弹幕的获取暂时就放后面再说了~

目前我抓取的接口都放在GlobalProperties这个类里面,不想本身抓包的同窗能够去这里看:

项目地址:仿BiliBili客户端

相关文章
相关标签/搜索