三锡溯、wss連接B站彈幕

環(huán)境 ws4py+

pip install ws4py
from ws4py.client.threadedclient import WebSocketClient

一、websocket協(xié)議

  1. 先與wss://broadcastlv.chat.bilibili.com/sub建立連接
  2. 發(fā)送登錄包
    {
    "uid": 0表示未登錄哑姚,否則為用戶ID,
    "roomid": 房間ID,
    "protover": 1,
    "platform": "web",
    "clientver": "1.4.0"
    }
  3. 每隔一段時間發(fā)送心跳包(30秒)
    Python只有延遲功能threading.Timer(delay,fun)
    需要自定義一個不斷循環(huán)的定時器
  4. 接收響應(yīng)
    響應(yīng)由頭部和數(shù)據(jù)組成


    圖片.png

    圖片.png
  5. 解析響應(yīng)得到數(shù)據(jù)
    這里有個細節(jié) b站可能一次返回了好幾幀數(shù)據(jù)
    先解析操作碼再由操作碼解析數(shù)據(jù)段
  6. 把數(shù)據(jù)交給主程序去處理
    需要自定義一個事件類
    用來實現(xiàn)事件的注冊on和分發(fā)功能emit
    二祭饭、工具層 utils.py
  7. 定時器類
    可取消
  8. 事件類
    注冊事件 (可重復(fù))
    分發(fā)事件
    取消事件
class Timer():
    def __init__(self,delay,fun):
        self.delay,self.f=delay,fun
        self.t=threading.Timer(self.delay,self.fun)
        self.t.start()
    def fun(self):
        if self.f:self.f()
        self.t=threading.Timer(self.delay,self.fun)
        self.t.start()
    def cancel(self):
        self.t.cancel()
        print("threading cancel")

class Event():
    def __init__(self):
        self.map=[]
        self.keys=[]
    def index(self,k):
        i=-1
        for key in self.keys:
            i+=1
            if key==k:return i
        return -1
    def on(self,key,fun):
        i=self.index(key)
        if i==-1:
            self.map.append({"key":key,"funs":[fun]})
            self.keys.append(key)
        else:
            self.map[i]["funs"].append(fun)
    def emit(self,key,data=None):
        i=self.index(key)
        if i==-1:
            print("no regist event:"+str(key))
            return
        for f in self.map[i]["funs"]:f(data)
    def rm(self,key,fun):
        i=self.index(key)
        if i==-1:
            print("no regist event:"+str(key))
            return
        funs=self.map[i]["funs"]
        for j in range(len(funs)):
            if funs[j]==fun:funs[j]=None
        self.map[i]["funs"]=list(filter(None,funs))

三、服務(wù)層 DanmuWS.py
opened(self) 連接建立后父類會自動調(diào)用
closed(self,code,reason) 連接關(guān)閉后父類會自動調(diào)用
需要進行斷線重連
received_message(self,message) 接收到數(shù)據(jù)時會自動調(diào)用
在這里解析數(shù)據(jù)并分發(fā)給主程序處理
send(self,data)父類發(fā)送數(shù)據(jù)的方法
sendLoginPacket 發(fā)送登錄包
sendHeartBeatPacket 發(fā)送心跳包
bind 綁定主程序處理數(shù)據(jù)和事件的函數(shù)

import threading
import json
import struct
from ws4py.client.threadedclient import WebSocketClient
from utils import Event,Timer
event=Event()
class DanmuWebSocket(WebSocketClient):
    def __init__(self,info,serveraddress='wss://broadcastlv.chat.bilibili.com/sub'):
        self.serveraddress=serveraddress
        WebSocketClient.__init__(self,serveraddress)
        DanmuWebSocket.event=event
        DanmuWebSocket.headerLength=16
        self.Info=info
    def opened(self):
        self.sendLoginPacket(self.Info['uid'],self.Info['roomid'],self.Info['protover'],self.Info['platform'],self.Info['clientver'])
        self.sendHeartBeatPacket();
        self.heartBeatHandler = Timer(20,self.sendHeartBeatPacket)
        print("opened")
    def delay_close(self):
        dws=DanmuWebSocket(self.Info,self.serveraddress)
        event.emit('reconnect',dws);
    def closed(self, code, reason=None):
        print("Closed", code, reason)
        if hasattr(self,"heartBeatHandler"):self.heartBeatHandler.cancel();
        if code == 1000: return
        threading.Timer(5,self.delay_close).start()
        print("Closed", code, reason)
    def received_message(self, message):
        position,length=0,len(message.data)-1
        while position<length:
            header_pack=struct.unpack(">IHHII",message.data[position:position+16])
            length_pack=header_pack[0]
            operation=header_pack[3]
            if operation==3:
                num=header_pack[1]+position
                num=struct.unpack(">I",message.data[num:num+4])[0]
                event.emit('heartbeat',num)
            elif operation==5:
                data=json.loads(message.data[position+16:position+length_pack])
                event.emit('cmd',data)
                #print("recv:"+data["cmd"])
            else:
                event.emit('login');
            position+=length_pack
    def sendData(self,data, protover = 1, operation = 2, sequence = 1):
        if type(data)==dict:
            data=json.dumps(data).encode()
        elif type(data)==str:
            data=data.encode()
        header=struct.pack(">IHHII",DanmuWebSocket.headerLength+len(data),DanmuWebSocket.headerLength,protover,operation,sequence)
        self.send(header+data)
    def sendLoginPacket(self,uid, roomid, protover = 1, platform = 'web', clientver = '1.4.6'):
        # Uint(4byte) + 00 10 + 00 01 + 00 00 00 07 + 00 00 00 01 + Data 登錄數(shù)據(jù)包
        data = {
            'uid': int(uid),
            'roomid': int(roomid),
            'protover': protover,
            'platform': platform,
            'clientver': clientver
        }
        print("sendLoginPacket")
        data=json.dumps(data)
        data=data.replace(' ','')
        self.sendData(data.encode(),1,7,1)
    def sendHeartBeatPacket(self):
        # Uint(4byte) + 00 10 + 00 01 + 00 00 00 02 + 00 00 00 01 + Data 心跳數(shù)據(jù)包
        self.sendData(b'[object Object]', 1, 2, 1);
    def bind(self,onreconnect=None,onlogin=None,onheartbeat=None,oncmd=None,onreceive =None):
        if "cmd" in event.keys:return
        if hasattr(onreconnect,"__call__"):event.on("reconnect",onreconnect)
        if hasattr(onlogin,"__call__"):event.on("login",onlogin)
        if hasattr(onheartbeat,"__call__"):event.on("heartbeat",onheartbeat)
        if hasattr(oncmd,"__call__"):event.on("cmd",oncmd)
        if hasattr(onreceive,"__call__"):event.on("receive",onreceive)

四叙量、測試代碼
利用前面寫的二維碼登錄代碼
登錄上Bilibili獲取個人uid
oncmd 處理彈幕數(shù)據(jù)
onlogin 連接成功時的處理函數(shù)
onreconnect 斷線重連時調(diào)用更新ws
onheartbeat 處理服務(wù)器發(fā)送的直播間人氣值

from server import Login
headers={
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0',
    'Accept': 'application/json, text/plain, */*',
    'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2',
    'Accept-Encoding': 'gzip, deflate, br',
    'Referer': 'https://live.bilibili.com/',
    'Origin': 'https://live.bilibili.com',
    'Connection': 'keep-alive'
    }
s=session(headers,'cookie.txt')
login=Login(s)
while not login.isLogin():
    login.get_vdcode()
    login.loop_vdcode()
info={
  "uid": login.info['uid'],
  "roomid": 7603080,
  "protover": 1,
  "platform": "web",
  "clientver": "1.4.0"
}
from DanmuWS import DanmuWebSocket
def oncmd(data):
    cmd=data["cmd"]
    if cmd=="SYS_MSG":
        print(data)
    elif cmd=="SPECIAL_GIFT":
        print(data)
    else:
        print(data)
def onlogin(data):
    print("login success")
def onreconnect(dws):
    global ws
    ws=dws
def onheartbeat(num):
    print(num)
try:
    ws = DanmuWebSocket(info,'wss://broadcastlv.chat.bilibili.com/sub')
    ws.connect()
    ws.bind(None,onlogin,onheartbeat,oncmd)
    ws.run_forever()
except:
    ws.close()
圖片.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末倡蝙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子绞佩,更是在濱河造成了極大的恐慌寺鸥,老刑警劉巖猪钮,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異胆建,居然都是意外死亡烤低,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門眼坏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來拂玻,“玉大人,你說我怎么就攤上這事宰译¢苎粒” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵沿侈,是天一觀的道長闯第。 經(jīng)常有香客問我,道長缀拭,這世上最難降的妖魔是什么咳短? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蛛淋,結(jié)果婚禮上咙好,老公的妹妹穿的比我還像新娘。我一直安慰自己褐荷,他們只是感情好勾效,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叛甫,像睡著了一般层宫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上其监,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天萌腿,我揣著相機與錄音,去河邊找鬼抖苦。 笑死毁菱,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的睛约。 我是一名探鬼主播鼎俘,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼辩涝!你這毒婦竟也來了贸伐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤怔揩,失蹤者是張志新(化名)和其女友劉穎捉邢,沒想到半個月后脯丝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡伏伐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年宠进,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藐翎。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡材蹬,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吝镣,到底是詐尸還是另有隱情堤器,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布末贾,位于F島的核電站闸溃,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏拱撵。R本人自食惡果不足惜便瑟,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一龄广、第九天 我趴在偏房一處隱蔽的房頂上張望宠哄。 院中可真熱鬧违孝,春花似錦、人聲如沸集索。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抄谐。三九已至,卻和暖如春扰法,著一層夾襖步出監(jiān)牢的瞬間蛹含,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工塞颁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留浦箱,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓祠锣,卻偏偏與公主長得像酷窥,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子伴网,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蓬推,服務(wù)發(fā)現(xiàn),斷路器澡腾,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 22年12月更新:個人網(wǎng)站關(guān)停沸伏,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,181評論 22 257
  • 一塊肥瘦相間的五花肉,夠解饞還不會讓你膩姆另,不管怎么煮都散發(fā)著濃郁的肉香味喇肋!不說了,我得趕緊買肉去迹辐!不然口水都要流干...
    乂君子好逑閱讀 650評論 6 4
  • 曉月晨星夢日邊蝶防, 清風(fēng)寂寥已成仙。 東曙送暖千秋歲右核, 妙筆軒齋渾然篇慧脱。
    暮雨傾弦閱讀 217評論 4 8
  • 小的時候想長大,長大就不用考試贺喝,可以和爸媽一樣隨時看電視菱鸥。 未成年時,想早點拿到身份證躏鱼,這樣可以自由出入網(wǎng)吧氮采,可以...
    一朵媽咪閱讀 309評論 2 1