Python實時爬取鬥魚彈幕

LarryHai6發表於2019-01-22

Python實時爬取鬥魚彈幕


實現目標:

輸入鬥魚房間號實時獲取彈幕資訊,實現效果如下:

img_ab195bcc9bb0203cfdf6d3bda7c5892c.gif

douyu.gif

邏輯梳理

  • 首先說明下鬥魚是開放了彈幕API的,可以直接去他們開發者論壇檢視文件,按照文件中要求一步一步的來就好了,我這邊就簡單梳理下:
  • 建立兩個執行緒:一個與彈幕伺服器建立連線然後獲取資料,一個定時傳送心跳資訊給彈幕伺服器保持連線。

建立連線

  1. 通過TCP協議連線到彈幕伺服器;

IP 地址:openbarrage.douyutv.com 埠:8601

  1. 向彈幕伺服器傳送登入請求,登入彈幕伺服器,訊息格式type@=loginreq/roomid@=房間號/,不需要賬號密碼;
  2. 登陸成功之後伺服器會給你返回一個登入成功資訊,這部分不用管,繼續向伺服器傳送一個進入彈幕分組請求,格式type@=joingroup/rid@=房間號/gid@=-9999/,gid使用-9999就好,表示海量彈幕模式;
  3. 接下來接收訊息就好了,當然伺服器返回的不止彈幕資訊,還包括禮物/特殊人物進入房間等訊息,這部分可以通過返回訊息的type進行判斷,選擇自己需要的就好,詳細的參見文件;

心跳訊息

  • 為保持連線需要每隔段時間向彈幕伺服器傳送心跳訊息,長時間未收到心跳訊息伺服器就會斷開連線了,心跳訊息格式:type@=keeplive/tick@=1439802131/,其中tick為當前秒級時間戳。

程式碼部分

# -*- coding:utf-8 -*-
import socket
import re
import time
import struct
import threading

def connect():
    '''
    第三方客戶端通過 TCP 協議連線到彈幕伺服器(依據指定的 IP 和埠); 
    第三方接入彈幕伺服器列表:
    IP 地址:openbarrage.douyutv.com 埠:8601
    '''
    print '-----*-----DouYu_Spider-----*-----\n'
    host = socket.gethostbyname("openbarrage.douyutv.com")
    port = 8601
    global s
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host,port))

def send_msg(msg):
    data_length = len(msg) + 8
    code = 689
    msgHead = struct.pack('<i',data_length) \
          + struct.pack('<i',data_length) + struct.pack('<i',code)
    s.send(msgHead)
    sent = 0
    while sent < len(msg):
        tn = s.send(msg[sent:])
        sent = sent + tn

def danmu(room_id):
    '''
    1.客戶端向彈幕伺服器傳送登入請求
    2.客戶端收到登入成功訊息後傳送進入彈幕分組請求給彈幕伺服器
    '''
    login = 'type@=loginreq/roomid@=%s/\0'%room_id
    login = login.encode('utf-8')
    send_msg(login)
    joingroup = 'type@=joingroup/rid@=%s/gid@=-9999/\0'%room_id
    joingroup = joingroup.encode('utf-8')
    send_msg(joingroup)
    while True:
        content = s.recv(1024)
        if judge_chatmsg(content):
            nickname = nick_name(content)
            chatmsg = chat_msg(content)
            print '%s : %s'%(nickname,chatmsg)
        else:
            pass


def keep_alive():
    '''
    客戶端每隔 45 秒傳送心跳資訊給彈幕伺服器
    '''
    while True:
        msg = 'type@=keeplive/tick@=%s/\0'%str(int(time.time()))
        send_msg(msg)
        time.sleep(45)

def nick_name(content):
    '''
    彈幕訊息:
    type@=chatmsg/rid@=301712/gid@=-9999/uid@=123456/nn@=test /txt@=666/level@=1/
    判斷type,彈幕訊息為chatmsg,txt為彈幕內容,nn為使用者暱稱
    '''
    pattern = re.compile(r'nn@=(.*)/txt@')
    nickname = pattern.findall(content)[0]
    return nickname

def chat_msg(content):
    '''
    彈幕訊息:
    type@=chatmsg/rid@=301712/gid@=-9999/uid@=123456/nn@=test /txt@=666/level@=1/
    判斷type,彈幕訊息為chatmsg,txt為彈幕內容,nn為使用者暱稱
    '''
    pattern = re.compile(r'txt@=(.*)/cid@')
    chatmsg = pattern.findall(content)[0]
    return chatmsg

def judge_chatmsg(content):
    '''
    判斷是否為彈幕訊息
    '''
    pattern = re.compile(r'type@=(.*)/rid@')
    data_type = pattern.findall(content)
    try:
        if data_type[0] == 'chatmsg':
            return True
        else:
            return False
    except Exception, e:
        return False



if __name__ == '__main__':
    connect()
    t1 = threading.Thread(target=danmu,args=(2947432,))
    t2 = threading.Thread(target=keep_alive)
    t1.start()
    t2.start()

最後

鬥魚提供的文件已經是一年前的了,裡面傳回的訊息內容增加了不少,但整體邏輯還是沒變,我這邊只取了彈幕裡面的暱稱和文字內容,其他的訊息各位可以先列印出來看了再寫正規表示式去匹配就好。

相關文章