WebSocket爬蟲之爬取龍珠彈幕

我是個宅男,喜歡看很多人直播,以前可以看一天直播不出門。現在主要看這麼些主播,虎牙的韋神、Dopa,鬥魚的狗賊噓噓。

對於其中的彈幕文化,非常感興趣,就研究下,發現彈幕是用WebSocket實現的,那首先來說說什麼是WebSocket。

WebSocket是什麼

詳細內容可以看看這個問題 https://www.zhihu.com/question/20215561

簡單解釋下:

HTTP 協議是一種無狀態的、無連接的、單向的應用層協議。它採用了請求/響應模型。通信請求只能由客戶端發起,服務端對請求做出應答處理。

這種通信模型有一個弊端:HTTP 協議無法實現服務器主動向客戶端發起消息。大多數 Web 應用程序將通過頻繁的異步JavaScript和XML(AJAX)請求實現長輪詢。輪詢的效率低,非常浪費資源(因爲必須不停連接,或者 HTTP 連接始終打開)。

WebSocket的最大特點就是,服務器可以主動向客戶端推送信息,客戶端也可以主動向服務器發送信息。

WebSocket 如何工作

一個非常典型的WebSocket創建方式如下(來自某巨頭):

function r() {
    if (!d) {
        var t = i();
        I.info("%cconnecting " + t, p("#0000E3")),
            u = new WebSocket(t),
            u.onopen = n,
            u.onclose = o,
            u.onerror = a,
            u.onmessage = h
    }
}

WebSocket獲取龍珠直播彈幕

本次使用的Python第三方庫是 https://github.com/websocket-client/websocket-client

看看官方例子:

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    print(error)

def on_close(ws):
    print("### closed ###")

def on_open(ws):
    def run(*args):
        for i in range(3):
            time.sleep(1)
            ws.send("Hello %d" % i)
        time.sleep(1)
        ws.close()
        print("thread terminating...")
    thread.start_new_thread(run, ())


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("ws://echo.websocket.org/",
                              on_message = on_message,
                              on_error = on_error,
                              on_close = on_close)
    ws.on_open = on_open
    ws.run_forever()

是不是非常熟悉,和上面講到的一模一樣,4種主要思想方法都是一致的,可以直接調用。

那麼到了實踐環節,本次選取的是龍珠直播,爲啥不是虎牙、鬥魚呢?這個待會再說,我們打開龍珠某個直播間

在網絡裏面選擇ws這一項,即可看到相關連接,而且這些消息是加密過的,別急,我們打開m站試試

這個時候傳輸的彈幕消息已經沒有加密過,直接對比,看到了一條“哈哈哈”的消息,所以我們現在可以確定就是這個websocket連接在傳輸相關消息。

依葫蘆畫瓢,我們嘗試用Python來連接

Curl:

curl 'wss://mbgows.plu.cn:8806/?room_id=2185&group=0' -H 'Pragma: no-cache' -H 'Origin: http://m.longzhu.com' -H 'Accept-Encoding: gzip, deflate, br' -H 'Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7' -H 'Sec-WebSocket-Key: n72+EfLt2iSrQ0EswTZ+2A==' -H 'User-Agent: Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36' -H 'Upgrade: websocket' -H 'Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits' -H 'Cache-Control: no-cache' -H 'Cookie: pluguest=81D781D68480BE065D952CA699B38E6627B61756AEF57338B39053154850A9502BC7FD850F86922BDF3DBD7F774BFDE5CBC80838A34B8F26' -H 'Connection: Upgrade' -H 'Sec-WebSocket-Version: 13' --compressed

Python代碼

#!/usr/bin/env python  
# -*- coding: utf-8 -*-
"""
@author: zhangslob
@file: longzhu_websocket.py 
@time: 2018/11/17
@desc: simple websocket client to connect longzhu
"""

import websocket
try:
    import thread
except ImportError:
    import _thread as thread
import time


def on_message(ws, message):
    import json
    try:
        print(json.loads(message))
    except:
        print(message)


def on_error(ws, error):
    print(error)


def on_close(ws):
    print("### closed ###")


def on_open(ws):
    pass
    # def run(*args):
    #     for i in range(3):
    #         time.sleep(1)
    #         ws.send("Hello %d" % i)
    #     time.sleep(1)
    #     ws.close()
    #     print("thread terminating...")
    # thread.start_new_thread(run, ())


headers = {
    'Pragma': 'no-cache',
    'Origin': 'http://m.longzhu.com',
    'Accept-Encoding': 'gzip, deflate, br',
    'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8,zh-TW;q=0.7',
    # 'Sec-WebSocket-Key': 'n72+EfLt2iSrQ0EswTZ+2A==',
    'User-Agent': 'Mozilla/5.0 (Linux; Android 8.0.0; Pixel 2 XL Build/OPD1.170816.004) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Mobile Safari/537.36',
    'Upgrade': 'websocket',
    'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits',
    'Cache-Control': 'no-cache',
    'Connection': 'Upgrade',
    'Sec-WebSocket-Version': '13',
}


if __name__ == "__main__":
    websocket.enableTrace(True)
    ws = websocket.WebSocketApp("wss://mbgows.plu.cn:8806/?room_id=2185&group=0",
                                on_message=on_message,
                                on_error=on_error,
                                on_close=on_close,
                                header=headers)
    ws.on_open = on_open
    ws.run_forever()

你可以直接運行上面的代碼,看看會有什麼結果

龍珠直播這個有點奇怪,你根本不用去向服務器發送什麼消息,服務器無腦給你推送彈幕,常見的是客戶端需要先告訴服務器“我是誰”,然後服務器再推送消息給你,並且還有有心跳檢測,“我”告訴服務器我還在看呢,你繼續給我彈幕,看看虎牙和鬥魚。

圖中綠色的是發送的消息,紅色是接受的消息。像這種情況就需要自己去看js代碼是如何處理消息的。鬥魚的話有公開自己的彈幕服務器第三方接入協議。

copy代碼可以閱讀原文

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章