Hack On Douyu -- 1

距離上次更新又有一段時間了,畢業答辯之后,確實和同學們一起出去嗨了一段時間,由于還沒入職,在家清凈的環境中可以好好學一下一直感興趣的東西啦。

一直對網絡爬蟲很感興趣,所以就開始學習很想學的python,用了之后也是感覺非常棒。期間抓過包括知乎、豆瓣、煎蛋還有個壁紙網站的數據,而抓去最多的還是直播網站斗魚。數據抓下來之后如何使用是個問題,我的辦法是用這些數據通過python的web框架flask搭建一個網站,也算是這段時間的學習成果。網站的構建自然少不了前端,也是硬著頭皮學習了bootstrap,了解了一些css、javascript的知識。這段時間的學習成果主要是LearningFlaskBeautifulPicsDanmuDouyuFan這四個項目(由于也是剛接觸python,代碼質量可能不是太高 -。-)。而最后這個DouyuFan算是對前邊幾個項目的總結。DouyuFan主要是通過斗魚網站彈幕信息的抓取,獲取直播禮物的分布情況,歷史數據記錄以及當前最熱門房間信息。
接下來我就用三次分別介紹我在數據抓取、后臺搭建以及前后端數據通訊中學到的知識和遇到的問題。

開播房間數據獲取

使用python抓取過數據的同學肯定對requestBeautiful Soup這兩個庫不陌生。
號稱http for humans的requests缺失不是沽名釣譽,他在頁面數據的抓取上確實簡單明了。
通常情況下,requests和Beautiful Soup配合使用。以對斗魚當前直播房間的抓取為例:

# -*- coding: utf-8 -*-
from bs4 import BeautifulSoup  # 導入BeautifulSoup,提取網頁中目標元素
import re                       # re 正則表達式,在快速查找和過濾元素中有出色表現
import requests                 # reqeusts 用以獲取頁面數據
from datetime import datetime
from pymongo import MongoClient

首先導入上述這幾個庫,然后可以偽造http請求header,這樣可以減少爬蟲被服務器ban掉的可能。

HOST = "http://www.douyu.com"
Directory_url = "http://www.douyu.com/directory?isAjax=1"
Qurystr = "/?page=1&isAjax=1"

agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.86 Safari/537.36'
accept = "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8"
connection = "keep-alive"
CacheControl = "no-cache"
UpgradeInsecureRequests = 1
headers = {
    'User-Agent': agent,
    'Host': HOST,
    'Accept': accept,
    'Cache-Control': CacheControl,
    'Connection': connection,
    'Upgrade-InsecureRequests': UpgradeInsecureRequests
}

然后就是開播房間數據的獲取和入庫:

cli = MongoClient(host="ip",port=xxx)
db = cli["Douyu"]
col = db["Roominfo"]


def get_roominfo(data):
    if data:
        firstpage = BeautifulSoup(data)
        roomlist = firstpage.select('li')
        print len(roomlist)
        if roomlist:
            for room in roomlist:
                try:
                    roomid = room["data-rid"]
                    roomtitle = room.a["title"]
                    roomtitle = roomtitle.encode('utf-8')
                    roomowner = room.select("p > span")
                    roomtag = room.select("div > span")
                    roomimg = room.a
                    roomtag = roomtag[0].string
                    date = datetime.now()
                    # now = datetime.datetime(
                    # date.year, date.month, date.day, date.hour, date.minute)
                    if len(roomowner) == 2:
                        zbname = roomowner[0].string
                        audience = roomowner[1].get_text()
                        audience = audience.encode('utf-8').decode('utf-8')
                        image = roomimg.span.img["data-original"]
                        word = u"萬"    # 在頁面中獲取的房間人數以萬為單位的str需要轉換為int型,以便入庫
                        if word in audience:
                            r = re.compile(r'(\d+)(\.?)(\d*)')
                            data = r.match(audience).group(0)
                            audience = int(float(data) * 10000)
                        else:
                            audience = int(audience)
                        roominfo = {
                            "roomid": int(roomid),
                            "roomtitle": roomtitle,
                            "anchor": zbname,
                            "audience": audience,
                            "tag": roomtag,
                            "date": date,
                            "img" : image
                        }
                        col.insert_one(roominfo)
                    # print roomid,":",roomtitle
                except Exception, e:
                    pass


def insert_info():
    session = requests.session()
    pagecontent = session.get(Directory_url).text
    pagesoup = BeautifulSoup(pagecontent)
    games = pagesoup.select('a')
    col.drop()
    for game in games:
        links = game["href"]
        gameurl = HOST + links + Qurystr
        print gameurl
        gamedata = session.get(gameurl).text
        get_roominfo(gamedata)

我平常習慣使用mongodb作為數據存儲,首先建立與數據庫的連接,然后通過獲取斗魚當前所有房間分類,接著逐一獲取每個分類中開播的房間數據,并記錄每個房間的roomid(房間號,斗魚直播間唯一標識)、roomtitle(房間標題)、anchor(主播id)、audience(觀眾人數)、tag(房間所屬分類)、date(數據獲取時間)、img(直播間封面圖片)。通過定時執行此腳本,可以獲取當前觀眾人數最多的房間(通常大都是lol的直播 =。=),也可以在之后通過roomid查詢到關于對應直播間必要的信息。

彈幕數據獲取

經常看斗魚直播的同學肯定知道“彈幕大神”這個詞,我最初想要抓取彈幕的目的是想通過大量的獲取直播間彈幕數據進行一些自然語言分析,由于那些東西一直沒學習,也就沒再弄,但是,通過彈幕,可以獲取到在全頻道廣播的火箭信息,長時間監測這些數據應該也是一件有意思的事情。
說干就干,想要獲取到直播間的彈幕數據不同于上邊所說的頁面數據抓取,好在斗魚官方也提供了一個獲取彈幕的途徑斗魚彈幕服務器第三方接入協議,文檔中對如何獲取彈幕數據、以及彈幕信息類型有具體的說明,這也大大降低了獲取彈幕數據的難度。
看過這個協議之后,通過建立與彈幕服務器的tcp連接,可以不斷的獲取到彈幕數據,我使用的是socket這個庫。

HOST = 'openbarrage.douyutv.com'
PORT = 8601
RID = 97376
LOGIN_INFO = "type@=loginreq/username@=qq_aPSMdfM5" + \
    "/password@=1234567890123456/roomid@=" + str(RID) + "/"
JION_GROUP = "type@=joingroup/rid@=" + str(RID) + "/gid@=-9999" + "/"
ROOM_ID = "type@=qrl/rid@=" + str(RID) + "/"
KEEP_ALIVE = "type@=keeplive/tick@=" + \
    str(int(time.time())) + "/vbw@=0/k@=19beba41da8ac2b4c7895a66cab81e23/"
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

在這里,需要注意幾個變量: host port roomid gid 。其中HOST是彈幕服務器地址,port是對外開放的端口,roomid則是主播間對應的id,gid是要加入的彈幕頻道,-9999頻道可以獲取到所有彈幕,也就是“海量彈幕”頻道。

def get_Hotroom():
    hotroom = roomcol.find().limit(1).sort(
        [("audience", pymongo.DESCENDING), ("date", pymongo.DESCENDING)])
    for item in hotroom:
        return item["roomid"]
        
def create_Conn():
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    RID = get_Hotroom()
    print "當前最熱房間:", RID
    LOGIN_INFO = "type@=loginreq/username@=qq_aPSMdfM5" + \
        "/password@=1234567890123456/roomid@=" + str(RID) + "/"
    print LOGIN_INFO
    JION_GROUP = "type@=joingroup/rid@=" + str(RID) + "/gid@=-9999" + "/"
    print JION_GROUP
    s.sendall(tranMsg(LOGIN_INFO))
    s.sendall(tranMsg(JION_GROUP))
    return s

之后,通過get_Hotroom()獲取到當前最熱門房間(人數最多的房間),通過create_Conn()建立與服務器的連接。連接建立之后就可以開心的獲取并保存彈幕數據了:

def insert_msg(sock):
    sendtime = 0
    while True:
        if sendtime % 20 == 0:
            print "----------Keep Alive---------"
            try:
                sock.sendall(tranMsg(KEEP_ALIVE))
            except socket.error:
                print "alive error"
                sock = create_Conn()
                insert_msg(sock)
        sendtime += 1
        print sendtime
        try:
            data = sock.recv(4000)
            if data:
                strdata = repr(data)
                if "type@=spbc" in strdata:
                    get_rocket(data)
                if "type@=chatmsg" in strdata:
                    get_chatmsg(data)
        except socket.error:
            print "chat error"
            sock = create_Conn()
            insert_msg(sock)
        time.sleep(1)

每20秒向服務器發送一條KEEP_ALIVE用以使連接保活,通過獲取到的數據特點,將普通聊天彈幕和火箭廣播彈幕區分開來,并且保存在不同的數據庫中從而為之后提供不同的用途。
彈幕數據獲取大致是這樣的:


獲取彈幕
獲取彈幕

后續內容

至此,此項目的數據獲取工作已經完成,在接下來兩篇內容會分別介紹如何使用這些數據構建頁面,以及在此過程中遇到的問題。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容