Hack On Douyu -- 3

在擁有了數(shù)據(jù)獲取和存儲(chǔ)的能力之后堰氓,如何利用好這些數(shù)據(jù)成為一個(gè)問題袄秩。
本來也一直打算把之前學(xué)習(xí)的flask框架用起來,現(xiàn)在有了這些數(shù)據(jù)裆赵,我打算利用這些數(shù)據(jù)做一個(gè)對(duì)斗魚網(wǎng)站的小型監(jiān)控頁面东囚。

初步構(gòu)想

通過彈幕可以基本上獲取到所有的斗魚火箭禮物發(fā)送和接收情況以及這些火箭發(fā)送的時(shí)間,因此可以利用這些數(shù)據(jù)得到每天每個(gè)小時(shí)斗魚全站火箭的發(fā)送情況战授,利用發(fā)送者的id和接收者的id可以統(tǒng)計(jì)出每天的火箭排名页藻。每天的數(shù)據(jù)在凌晨完成統(tǒng)計(jì)匯總桨嫁,可以構(gòu)成歷史數(shù)據(jù)。
在這個(gè)小項(xiàng)目中份帐,我主要用到兩種方法完成前后端數(shù)據(jù)交互:一種是直接根據(jù)請(qǐng)求url知道前端需要的數(shù)據(jù)具體是什么璃吧,我就直接在后端完成數(shù)據(jù)的獲取和處理,通過flask模板完成要返回的頁面废境;另一種方法則是實(shí)時(shí)性比較強(qiáng)的或者是需要和前端js有交集的我才用socket.io完成數(shù)據(jù)交互畜挨。

通過抓取斗魚直播間分類的靜態(tài)頁面可以按照人氣值進(jìn)行排名,獲取到當(dāng)前的熱門房間噩凹,利用這些房間id可以獲取到該直播間的直播視頻數(shù)據(jù)巴元,在flash插件上就可以直接轉(zhuǎn)播(本來一直很糾結(jié)視頻流如何獲取,在參考了dotamax的轉(zhuǎn)播方法之后就醒悟了)栓始。

另外务冕,在獲取實(shí)時(shí)聊天彈幕的時(shí)候可以獲取到火箭發(fā)送信息血当,將這些信息通過redis轉(zhuǎn)發(fā)幻赚,再由socket.io提供給前端頁面,實(shí)時(shí)推送火箭禮物消息臊旭。

火箭發(fā)送數(shù)據(jù)交互

上邊說到落恼,火箭發(fā)送信息通過彈幕獲取,在數(shù)據(jù)庫中存儲(chǔ)包括發(fā)送者id离熏、接收者id和發(fā)送時(shí)間佳谦,這些數(shù)據(jù)同樣可以通過redis完成pub/sub。
因此為了得到每日火箭逐小時(shí)發(fā)送情況滋戳,在后端完成了數(shù)據(jù)的預(yù)處理:

def sortbyDay(date):
    if isinstance(date, datetime):
        year = date.year
        m = date.month
        d = date.day
        singledate = datetime(year, m, d)
        print singledate
        singledata = []
        count = 0
        hour = range(0, 24)
        for h in hour:
            data = []
            value = {}
            start = datetime(year, m, d, h, 0, 0)
            end = datetime(year, m, d, h, 59, 59)
            daydata = col.find(
                {'date': {'$gt': start, '$lt': end}}, {'_id': 0})
            if daydata is not None:
                for item in daydata:
                    data.append(item)
                    count = count + 1
            else:
                data = None
            value['hour'] = h
            value['rockets'] = data
            singledata.append(value)
        if count != 0:
            insertdata = {
                'date': singledate,
                'data': singledata,
                'count': count
            }
            return insertdata
        else:
            return None

通過輸入指定的日期date钻蔑,在數(shù)據(jù)庫中檢索該天內(nèi)rocket表中所有數(shù)據(jù),返回當(dāng)天火箭禮物總量奸鸯、以及每個(gè)小時(shí)火箭的發(fā)送量咪笑。
在獲取到每天所有火箭數(shù)據(jù)之后,統(tǒng)計(jì)每天發(fā)送者和接收者排名:

def valuebyHour(date):
    daydata = sortbyDay(date)
    count = 0
    hourvalue = []
    sendervalue = {}
    recvervalue = {}
    if daydata is not None:
        count = daydata['count']
        hourdata = daydata['data']
        for h in hourdata:
            hourvalue.append(len(h['rockets']))
            sender = 'sender_id'
            recver = 'recver_id'
            rocket = h['rockets']
            if len(rocket) != 0:
                sendervalue = sortNames(rocket, sender, sendervalue)
                recvervalue = sortNames(rocket, recver, recvervalue)
        return (count, hourvalue, sendervalue, recvervalue)
    else:
        return None

最終返回當(dāng)天火箭發(fā)送總量娄涩,火箭逐小時(shí)發(fā)送情況窗怒,發(fā)送者對(duì)應(yīng)的發(fā)送量和接收者對(duì)應(yīng)的接收量。

完成數(shù)據(jù)預(yù)處理之后蓄拣,就可以根據(jù)前端的請(qǐng)求返回對(duì)應(yīng)的數(shù)據(jù)了扬虚。由于前端獲取數(shù)據(jù)之后前端js根據(jù)數(shù)據(jù)完成不同圖表的繪制,我是采用socket.io的方式完成數(shù)據(jù)交互球恤。
在后端辜昵,我是直接使用flask的一個(gè)擴(kuò)展flask_socketio完成socket.io服務(wù)器的搭建(當(dāng)然,socket.io擁有不同開發(fā)語言版本的實(shí)現(xiàn)咽斧,可根據(jù)具體情況有不同選擇)堪置。在flask中建立一個(gè)socket.io服務(wù)器是很簡便的:

from flask_socketio import SocketIO
from flask_socketio import send, emit
# flask 主程序
app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret'
socketio = SocketIO(app)
if __name__ == '__main__':
    socketio.run(app, host='0.0.0.0', port=3000)

建立socket服務(wù)器之后贷洲,只需要根據(jù)具體情況建立不同的監(jiān)聽事件。

# historyDate事件晋柱,接收來自前端的日期date优构,將其解析之后調(diào)用上述數(shù)據(jù)獲取方法
@socketio.on('historyDate')
def sendDate(date):
    if date:
        print date
        time = date.split('-')
        timevalue = [int(item) for item in time]
        y = timevalue[0]
        m = timevalue[1]
        d = timevalue[2]
        recordDate = datetime(y,m,d)
        returnValue = valuebyHour(recordDate)
        if returnValue:
            (a, b, c, d) = returnValue
            if b is not None:
                socketio.emit('historyRockets', b)
        else:
            socketio.emit('historyRockets',None)
# historyRockets事件,接收來自historyDate的數(shù)據(jù)并將其返回給前端           
@socketio.on('historyRockets')
def sendHistory(data):
    emit('historyRockets',data)

服務(wù)器端的內(nèi)容弄完之后雁竞,前端只需要建立連接钦椭,并在恰當(dāng)?shù)臅r(shí)候觸發(fā)對(duì)應(yīng)的事件即可:

// 連接socket.io服務(wù)器
var socket = io.connect('http://ip:' + port);
// 觸發(fā)事件
socket.emit('historyDate',date);
//接收數(shù)據(jù)
socket.on('historyRockets', function(msg){
    dosth();
 });

每天的數(shù)據(jù)只需要前端發(fā)送一個(gè)指定的date就可以獲取到了。

最熱房間數(shù)據(jù)獲取

通過socket.io可以完成數(shù)據(jù)交互碑诉,但是這些數(shù)據(jù)都需要建立額外的連接彪腔、消耗額外的網(wǎng)絡(luò)資源,一些直接可以從后端獲取的數(shù)據(jù)則可直接通過flask模板完成渲染进栽。
以最熱房間為例德挣,由于通過爬蟲抓取到的數(shù)據(jù)都在數(shù)據(jù)庫中,頁面在加載的時(shí)候直接調(diào)用即可快毛。

首先獲取人氣排名前21位(頁面顯示3*7)格嗅。獲取這些房間的房間標(biāo)題、主播名唠帝、房間編號(hào)和房間封面屯掖。

# 按照觀眾人數(shù),前20名房間
def HotRoom():
    hotroom = roomcol.find({}, {"_id": 0, "date": 0}).sort(
        "audience", pymongo.DESCENDING).limit(21)
    rooms = []
    if hotroom:
        for item in hotroom:
            rooms.append(item)
    return rooms

數(shù)據(jù)獲取之后襟衰,只需要在flask的router中添加對(duì)應(yīng)規(guī)則并則模板中完成元素添加即可:

# 添加路由規(guī)則
@app.route('/chatmsg')
def chatmsg():
    rooms = HotRoom()
    return render_template('gift.html', hotroom=rooms, flag=0)    

完成模板:

    <h2 style="color:rgba(228, 230, 232, 0.53);">熱門房間</h2>
    {%if hotroom%}
        {%for room in hotroom%}
            {%if flag%3==0 %}
            <div class="row nopx">
            {%endif%}
            <div class="col-sm-6 col-md-4 nopx">
                <div class="thumbnail" style="border:1px solid rgba(135, 144, 160, 0.15);line-height: 0px;
background-color: rgba(8, 8, 8, 0.72);">
                    <img src={{room['img']}} alt="http://eclipsesv.com:4321/tv/{{room['roomid']}}">
                    <div class="caption">
                        <h4 style="color:rgba(228, 230, 232, 0.53);">{{room['roomtitle']}}</h4>
                        <p>
                            <a  target="_blank">
                                {{room['anchor']}}@{{room['tag']}}
                            </a>
                        </p>
                    </div>
                </div>
            </div>
            {%set flag=flag+1%}
            {%if flag%3==0 %}
            </div>
            {%endif%}
        {%endfor%}
    {%endif%}

這樣就可以啦贴铜。

視頻流轉(zhuǎn)播

熱門房間頁面完成之后,想要直接在頁面中觀看視頻而不是跳轉(zhuǎn)到斗魚瀑晒,之前一直想復(fù)雜了绍坝,在看了dotamax直播視頻之后看了源碼就恍然大悟了。原來只需要一個(gè)直播間id就可以了苔悦。

在flask中添加路由規(guī)則轩褐,通過roomid返回對(duì)應(yīng)頁面:

@app.route('/tv/<int:roomid>')
def tvstream(roomid):
    if roomid:
        return render_template('tv.html', roomid=roomid)

前端模板只需要這樣即可:

{%if roomid%}
 <object type="application/x-shockwave-flash" data="http://staticlive.douyutv.com/common/share/play.swf?room_id={{roomid}}" width="1200px" height="750px" allowscriptaccess="always" allowfullscreen="true" allowfullscreeninteractive="true"><param name="quality" value="high"><param name="bgcolor" value="#000000"><param name="allowscriptaccess" value="always"><param name="allowfullscreen" value="true"><param name="wmode" value="transparent"><param name="allowFullScreenInteractive" value="true">
            <param name="quality" value="high">
            <param name="bgcolor" value="#000000">
            <param name="allowscriptaccess" value="always">
            <param name="allowfullscreen" value="true">
            <param name="wmode" value="transparent">
            <param name="allowFullScreenInteractive" value="true">
 </object>
{%endif%}

這樣就可以盜用斗魚的視頻啦,哈哈哈间坐。

TODO

到目前為止灾挨,站點(diǎn)基本上可以正常運(yùn)行,由于前端的內(nèi)容都是初步接觸竹宋,總體上應(yīng)該還有較大的可改進(jìn)余地劳澄。接下來準(zhǔn)備完成的點(diǎn)主要包括這些:

  • 數(shù)據(jù)庫結(jié)構(gòu)優(yōu)化,充分考慮利用mongodb自身aggregation來完成數(shù)據(jù)預(yù)處理
  • 提供完善的restful api 完成數(shù)據(jù)調(diào)用
  • 提供針對(duì)直播分類和主播的人氣監(jiān)控

暫時(shí)就先這些蜈七,繼續(xù)努力秒拔!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市飒硅,隨后出現(xiàn)的幾起案子砂缩,更是在濱河造成了極大的恐慌作谚,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庵芭,死亡現(xiàn)場離奇詭異妹懒,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)双吆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門眨唬,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人好乐,你說我怎么就攤上這事匾竿。” “怎么了蔚万?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵岭妖,是天一觀的道長。 經(jīng)常有香客問我反璃,道長昵慌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任版扩,我火速辦了婚禮废离,結(jié)果婚禮上侄泽,老公的妹妹穿的比我還像新娘礁芦。我一直安慰自己,他們只是感情好悼尾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布柿扣。 她就那樣靜靜地躺著,像睡著了一般闺魏。 火紅的嫁衣襯著肌膚如雪未状。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天析桥,我揣著相機(jī)與錄音司草,去河邊找鬼。 笑死泡仗,一個(gè)胖子當(dāng)著我的面吹牛埋虹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播娩怎,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼搔课,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了截亦?” 一聲冷哼從身側(cè)響起爬泥,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤柬讨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后袍啡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體踩官,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年境输,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了卖鲤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畴嘶,死狀恐怖蛋逾,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情窗悯,我是刑警寧澤区匣,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站蒋院,受9級(jí)特大地震影響亏钩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜欺旧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一姑丑、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辞友,春花似錦栅哀、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鲫尊,卻和暖如春痴柔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疫向。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工咳蔚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人搔驼。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓谈火,卻偏偏與公主長得像,于是被迫代替她去往敵國和親匙奴。 傳聞我的和親對(duì)象是個(gè)殘疾皇子堆巧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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