Hack On Douyu -- 3

在拥有了数据获取和存储的能力之后,如何利用好这些数据成为一个问题。
本来也一直打算把之前学习的flask框架用起来,现在有了这些数据,我打算利用这些数据做一个对斗鱼网站的小型监控页面。

初步构想

通过弹幕可以基本上获取到所有的斗鱼火箭礼物发送和接收情况以及这些火箭发送的时间,因此可以利用这些数据得到每天每个小时斗鱼全站火箭的发送情况,利用发送者的id和接收者的id可以统计出每天的火箭排名。每天的数据在凌晨完成统计汇总,可以构成历史数据。
在这个小项目中,我主要用到两种方法完成前后端数据交互:一种是直接根据请求url知道前端需要的数据具体是什么,我就直接在后端完成数据的获取和处理,通过flask模板完成要返回的页面;另一种方法则是实时性比较强的或者是需要和前端js有交集的我才用socket.io完成数据交互。

通过抓取斗鱼直播间分类的静态页面可以按照人气值进行排名,获取到当前的热门房间,利用这些房间id可以获取到该直播间的直播视频数据,在flash插件上就可以直接转播(本来一直很纠结视频流如何获取,在参考了dotamax的转播方法之后就醒悟了)。

另外,在获取实时聊天弹幕的时候可以获取到火箭发送信息,将这些信息通过redis转发,再由socket.io提供给前端页面,实时推送火箭礼物消息。

火箭发送数据交互

上边说到,火箭发送信息通过弹幕获取,在数据库中存储包括发送者id、接收者id和发送时间,这些数据同样可以通过redis完成pub/sub。
因此为了得到每日火箭逐小时发送情况,在后端完成了数据的预处理:

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,在数据库中检索该天内rocket表中所有数据,返回当天火箭礼物总量、以及每个小时火箭的发送量。
在获取到每天所有火箭数据之后,统计每天发送者和接收者排名:

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

最终返回当天火箭发送总量,火箭逐小时发送情况,发送者对应的发送量和接收者对应的接收量。

完成数据预处理之后,就可以根据前端的请求返回对应的数据了。由于前端获取数据之后前端js根据数据完成不同图表的绘制,我是采用socket.io的方式完成数据交互。
在后端,我是直接使用flask的一个扩展flask_socketio完成socket.io服务器的搭建(当然,socket.io拥有不同开发语言版本的实现,可根据具体情况有不同选择)。在flask中建立一个socket.io服务器是很简便的:

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服务器之后,只需要根据具体情况建立不同的监听事件。

# historyDate事件,接收来自前端的日期date,将其解析之后调用上述数据获取方法
@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的数据并将其返回给前端           
@socketio.on('historyRockets')
def sendHistory(data):
    emit('historyRockets',data)

服务器端的内容弄完之后,前端只需要建立连接,并在恰当的时候触发对应的事件即可:

// 连接socket.io服务器
var socket = io.connect('http://ip:' + port);
// 触发事件
socket.emit('historyDate',date);
//接收数据
socket.on('historyRockets', function(msg){
    dosth();
 });

每天的数据只需要前端发送一个指定的date就可以获取到了。

最热房间数据获取

通过socket.io可以完成数据交互,但是这些数据都需要建立额外的连接、消耗额外的网络资源,一些直接可以从后端获取的数据则可直接通过flask模板完成渲染。
以最热房间为例,由于通过爬虫抓取到的数据都在数据库中,页面在加载的时候直接调用即可。

首先获取人气排名前21位(页面显示3*7)。获取这些房间的房间标题、主播名、房间编号和房间封面。

# 按照观众人数,前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

数据获取之后,只需要在flask的router中添加对应规则并则模板中完成元素添加即可:

# 添加路由规则
@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 href="http://eclipsesv.com:4321/tv/{{room['roomid']}}" target="_blank">
                                {{room['anchor']}}@{{room['tag']}}
                            </a>
                        </p>
                    </div>
                </div>
            </div>
            {%set flag=flag+1%}
            {%if flag%3==0 %}
            </div>
            {%endif%}
        {%endfor%}
    {%endif%}

这样就可以啦。

视频流转播

热门房间页面完成之后,想要直接在页面中观看视频而不是跳转到斗鱼,之前一直想复杂了,在看了dotamax直播视频之后看了源码就恍然大悟了。原来只需要一个直播间id就可以了。

在flask中添加路由规则,通过roomid返回对应页面:

@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

到目前为止,站点基本上可以正常运行,由于前端的内容都是初步接触,总体上应该还有较大的可改进余地。接下来准备完成的点主要包括这些:

  • 数据库结构优化,充分考虑利用mongodb自身aggregation来完成数据预处理
  • 提供完善的restful api 完成数据调用
  • 提供针对直播分类和主播的人气监控

暂时就先这些,继续努力!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容