介绍

  • WebSocket是HTML5下一种新的协议(websocket协议本质上是一个基于tcp的协议)
  • 它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯的目的
  • Websocket是一个持久化的协议
  • websocket约定了一个通信的规范,通过一个握手的机制,客户端和服务器之间能建立一个类似tcp的连接,从而方便它们之间的通信
  • 在websocket出现之前,web交互一般是基于http协议的短连接或者长连接
  • websocket是一种全新的协议,不属于http无状态协议,协议名为"ws"
  • 和HTTP都是基于tcp的,都是可靠性传输协议,都是应用层协议
  • WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息,但HTTP是单向的
  • WebSocket是需要浏览器和服务器握手进行建立连接的,而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接

JS实现

js websocket主要有以下几个方法:

//连接发生错误的回调方法
websocket.onerror = function () {
    setMessageInnerHTML("WebSocket连接发生错误");
};

//连接成功建立的回调方法
websocket.onopen = function () {
    setMessageInnerHTML("WebSocket连接成功");
}

//接收到消息的回调方法  接服务器信息的方法
websocket.onmessage = function (event) {
    setMessageInnerHTML(event.data);
}

//连接关闭的回调方法
websocket.onclose = function () {
    setMessageInnerHTML("WebSocket连接关闭");
}

当然这些都是浏览器环境

实战

逆向

此次目标为B站,随便找个直播间,来爬一下直播间的弹幕

首先找到给出WSS信息的API,这个是B站的

其实B站还有一个API能够拿到弹幕,详见 Bilibili直播API,我估计是为了填补WSS连接前的空档期

同时也可以在WS这看到建立的连接:

当然数据是加密的,通过发起程序或者搜索关键字找到代码处:
最终给我一顿找找到这里:

这个要说明一下就是WS建立可能会因为你调试导致失败,比如我这个就导致直播推流失败,但是弹幕仍然有效,因此并不影响

失败的会自动重新建立几次连接,记得关注URL适当放行几次,这个得自己把握,同时每次数据都会变,不像HTTP每次请求对应一次数据,像B站这个,点开就一个二进制数据选项,每次进每次数据不一样,但有一点可以确定的是数据加密过

在上下不断寻找后,最终找到可能的解密处:

其实现在可以有两种办法,一是植入代码,浏览器保持运行,本地一个客户端同时接受,即做一个转发,另一个种那就是本地客户端直接接受了,但是那种可能还需要处理心跳问题,这里直接试试不用逆向解密方法的植入方式。

植入代码

植入方式最大好处就是浏览器环境,难以被反爬了吧

准备好js代码:

!function(){
    const ws = new WebSocket('ws://127.0.0.1:7788');
    window.wsHook = ws;
    ws.onopen = function(e){
        ws.send('CONNECTED')
    }
    ws.onmessage = function(e){
        ws.send(JSON.stringify(n.body))
    }
}();

然后选择替换(或者覆盖),然后对对应的JS文件右键-保存以覆盖(记住选择未格式化的!),直接在浏览器中打开已保存的代码进行编辑,在这段解密后的代码后面添加上面的代码即可

当然这不够优雅,可以考虑以下代码:
在文件头部加入

!function(){
    const ws = new WebSocket('ws://127.0.0.1:7788');
    window.wsHook = ws;
    ws.onopen = function(e){
        ws.send('CONNECTED')
    }

}();

然后在对应地方加入以下代码:

window.wsHook.send(JSON.stringify(n.body));

现在确保开发者工具开启且启用覆盖,刷新一下页面,如果你恰好看着控制台,你会看到连接到127.0.0.1:7788失败的消息,这是因为还没有客户端来接收

再用一下下面抄来的Python代码(懒):

import asyncio
# encoding: utf-8
import asyncio
import websockets


async def echo(websocket):
    # 使用WebSocket在客户端和服务器之间建立全双工双向连接后,就可以在连接打开时调用send()方法。
    message = 'hello world'
    # 发送数据
    await websocket.send(message)
    return True


async def recv_msg(websocket):
    while 1:
        # 接收数据
        recv_text = await websocket.recv()
        print(recv_text)


async def main_logic(websocket, path):
    await echo(websocket)
    await recv_msg(websocket)


start_server = websockets.serve(main_logic, '127.0.0.1', 7788)
loop = asyncio.get_event_loop()
loop.run_until_complete(start_server)
loop.run_forever()

开RUN,可以看到直播间的消息已经都摆在你面前了

这时候是存入MongoDB还是现场就开始分析全看个人,仅仅插入一段代码,不用破解任何方法,正常看着直播就把弹幕爬了,这还扣啥代码,要什么自行车。

思路拓展
当遇到一个极难的网站,即使找到了解密方法,也很难扣的时候,是否可以使用这种办法转发解密后的数据给本地?这样虽然还是需要配合自动化但却省去了用BeautifulSoup等工具对网页的解析,自动化只需要做到能帮助翻页等功能即可。