bilibili直播间观察

本文介绍了一个简易系统,用于抓取B站直播间的弹幕数据,并通过Flask和Echarts实现实时可视化展示。系统涵盖从前端到后端的完整流程,包括弹幕抓取、数据处理及展示等关键环节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

现在直播平台很多,对这些平台的数据攫取相信也有很多 .一些很深的技术不讲也不会,可以大概说下我的思路,以及实现过程,这是一个简单的系统,包括了前台与后台的全部工作.很丑.不嫌弃…

  1. 系统架构
  2. 实现细节
  3. 踩过的坑

1.系统架构

这里写图片描述
说明 :
bilibili弹幕机:爬虫,爬取直播间的弹幕信息
数据处理服务:以合适的方式检索redis数据库中的数据,生成目标通过Redis的消息服务发布.
Redis:本项目中使用的数据库,提供存储服务与消息服务.
界面:动态可视化目标数据.

2.实现细节

bilibili弹幕机
基于aiohttp,能够异步处理大量并发,效果非常好,后期也计划支持动态删减任务.
主要代码:

async def fetch_all_danmu(roomlist):
    tasks = []
    for i in roomlist:
        danmuji = bilibiliClient()
        r = redis.Redis(host='172.17.0.2', port=6379,db=11)
        q = queue.Queue()
        task = asyncio.ensure_future(danmuji.connectServer(r,q,roomid=i,w=True))
        tasks.append(task)
        task = asyncio.ensure_future(danmuji.HeartbeatLoop())
        tasks.append(task)
        print("this is romm ",i)
        # _=await asyncio.gather(*tasks)

async def print_hello(i):
    await asyncio.sleep(10)
    print("this is ",i)

if __name__ == '__main__':
    scheduler = AsyncIOScheduler()
    # scheduler.add_job(fetch_all, args=[URL_LIST], trigger='interval', seconds=2)
    lists = []
    lists.append("865138")
    lists.append("5269")
    scheduler.add_job(fetch_all_danmu, args=[lists])
    # scheduler.add_job(print_hello,args=[1])
    scheduler.start()
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))

    # Execution will block here until Ctrl+C (Ctrl+Break on Windows) is pressed.
    try:
        asyncio.get_event_loop().run_forever()
    except (KeyboardInterrupt, SystemExit):
        pass

没有贴爬虫的具体内容,网上方法也比较多.我是参考GIT上的bilibili_danmu实现的.

数据处理服务
由redis中读取数据
使用redis_timeseries作为时间序列数据的存储的工具.

pip install redis_timeseries

这个工具的存储方式其实也比较简单,通过检索keys可以发现,其实是对数据以及时间进行了封装.封装结构为

':'.join([self.base_key, granularity, str(timestamp_key), str(key)])

所以在定义了你想要的数据结构后,也可以使用redis的原生检索方法进行检索.
定义key:"roomid{0}:{1}".format(roomid,data)

data = redis.Redis(host='172.17.0.2', port=6379,db=11)
def mess_pub():
    my_granularities={}
    my_granularities['1second']={'ttl': redis_timeseries.days(1), 'duration': redis_timeseries.seconds(1)}
    ts = redis_timeseries.TimeSeries(data,base_key="lyt",granularities=my_granularities)
    while True:
        count = len(ts.scan_keys("1second",10,"roomid5269:*"))
        wcount = ts.scan_keys('1second',60,'roomwatcher5269:*')[-1].split(":")[-1]
        print("pub count and wcount :{0},{1}".format(count ,wcount))
        s=json.dumps({"count":float(count),"wcount":float(wcount)})#变为bytes数组
        data.publish('summary', u'%s' % (s))
        time.sleep(2)

if __name__ == '__main__':
    mess_pub()

Redis

数据存储服务:已经说过了,最终选择了redis_timeseries作为存储工具,能够很好的支持时间序列数据.
消息发布服务:使用redis原生的消息发布订阅组件.

界面

使用Flask+echarts实现.这一部分代码比较多,重点是可视化部分.前端本人不太会,所以很丑.
效果:
这里写图片描述
HTML代码:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>Proudly presented by ECharts</title>
    {% for jsfile_name in script_list %}
        <script src="{{host}}/{{jsfile_name}}.js"></script>
    {% endfor %}
    <script>
        var source = new EventSource("{{ url_for('live.stream') }}");
        source.onmessage = function (event) {
            var summary = JSON.parse(event.data);
            document.getElementById("text").value =  summary.count.toString() + " " + summary.wcount.toString();};

    </script>
</head>

<body>
    <form>
        <input type="text" id="text" size="35" />
        <a id="update">0</a>
        <div style="width: 800px;height: 100px;"><textarea id="info"  style="width: 700px;height: 90px;"></textarea></div>
        <a>bilibili直播间观看人数与活跃人数对比(勿对号入座)</a>
        <div id="main" style="width: 800px;height:300px;"></div>
        <script language=javascript>
            function create_graph() {
                document.getElementById("text").value="test";
                var myChart = echarts.init(document.getElementById('main'));
                function randomData() {
                    now = new Date(+now + oneMin);
                    value = value + Math.random() * 21 - 10;
                    return {
                        name: now.toString(),
                        value: [
                            time_str(now),
                            Math.round(0)
                        ]
                    }
                }
                var data = [];
                var data1 = [];
                var t = new Date()
                var now = +new Date(t.getFullYear(), t.getMonth()+1, t.getDate(),0,0,0);
                var oneDay = 24 * 3600 * 1000;
                var oneMin = 60 * 1000;
                var value = Math.random() * 1000;
                for (var i = 0; i < 60; i++) {
                    data.push(randomData());
                    data1.push(randomData());
                }

                var option = {
                    title: {
                        text: ''
                    },
                    legend: {    //图表上方的类别显示
                        show:true,
                        data:['观看人数','活跃人数']
                    },
                    tooltip: {
                        trigger: 'axis',
                        formatter: function (params) {
                            document.getElementById("info").innerText = "观看人数:" + params[0].value + "\n"+"活跃人数:" + params[1].value;
                            params = params[0];
{#                            var date = new Date(params.name);#}
{#                            return "观看人数:" + params[0].value + "\n"+"活跃人数:" + params[1].value;#}
                        },
                        axisPointer: {
                            animation: false
                        }
                    },
                    xAxis: {
                        type: 'time',
                        splitLine: {
                            show: false
                        }
                    },
                    yAxis: {
                        type: 'value',
                        boundaryGap: [0, '100%'],
                        splitLine: {
                            show: false
                        }
                    },
                    series: [{
                        name: '观看人数',
                        type: 'line',
                        showSymbol: false,
                        hoverAnimation: false,
                        data: data
                    },
                        {
                            name: '活跃人数',
                            type: 'line',
                            showSymbol: false,
                            hoverAnimation: false,
                            data: data1
                        }]
                };
                myChart.setOption(option)
                window.line = self.setInterval(function () {
                    var ele = document.getElementById("text");
                    var value = ele.value.toString().split(" ")
                    now = new Date(+now + oneMin);
                    data.shift();
                    data.push({
                        name: now.toString(),
                        value: [
                            time_str(now),
                            Math.round(value[1])
                        ]
                    });
                    data1.shift();
                    data1.push({
                        name: now.toString(),
                        value: [
                            time_str(now),
                            Math.round(value[0])
                        ]
                    });
                    myChart.setOption({
                        series: [{
                            data: data
                        },
                            {
                                data: data1
                            }]
                    });
                }, 2000);};
            function time_str(now) {
                Y = now.getFullYear()+'/';
                M = (now.getMonth()+1)+'/';
                D = now.getDate()+" ";
                h = now.getHours()+":";
                m = now.getMinutes();
                return Y+M+D+h+m;
            }
            create_graph();
        </script>
    </form>
</body>
</html>

网页中使用eventHandle处理来自Redis消息,代码为

def event_stream():
    pubsub = red.pubsub()
    pubsub.subscribe('summary')
    mess = ""
    for message in pubsub.listen():
        if message['data']!=1:
            # print(message['data'].decode("utf-8"))
            mess = {k : v  for k, v in json.loads(message['data'].decode("utf-8")).items()}
        yield 'data:{0}\n\n'.format(json.dumps(mess))
@live.route('/stream')
def stream():
    return flask.Response(event_stream(),
                          mimetype="text/event-stream")

坑:
爬虫需要很长的时间调试参数
整个流程虽然简单,也是消耗了一天的时间,废…
…很多 ,不想写了

以上…..

<think>我们正在处理用户关于Bilibili直播间API接口文档的请求。根据用户的问题,他想要获取Bilibili直播间的信息,因此我们需要提供相关的API接口信息。根据之前的引用,我们有一个bilibili-api-python库,它可能封装了Bilibili的API,包括直播相关的接口。但是,用户直接询问的是API接口文档,因此我们应该提供官方的API文档链接,同时也可以介绍如何使用这个库来简化操作。然而,需要注意的是,Bilibili官方可能没有完全公开所有API的文档,特别是直播相关的API。因此,我们可能需要结合官方文档和社区经验。根据引用[1],我们有一个BiliBili API官方文档的链接,但具体链接没有给出。我们可以建议用户查看Bilibili的开发者平台。另外,引用[2]提到了弹幕API的调用,但用户需要的是直播间信息,这可能包括直播间状态、主播信息、观众数等,与弹幕API不同。 因此,我们的回答应该包括: 1.官方API文档的指引(尽管可能不够详细)。2.使用bilibili-api-python库(如果支持直播相关功能)来获取直播间信息。3.如果库不支持,则直接调用API的方法(通过分析网络请求等)。 经过快速查阅,bilibili-api-python库确实支持直播相关功能。其GitHub仓库(https://github.com/MoyuScript/bilibili-api)和文档(https://bilibili-api.readthedocs.io/)中应该有相关说明。具体到获取直播间信息,我们可以使用`live.get_room_info`方法(具体方法名可能有所变化,需要确认)。 因此,回答计划如下:1.介绍官方API文档的位置(尽管可能没有详细的直播API文档)。2.推荐使用bilibili-api-python库,并给出示例代码。3.如果用户需要直接调用API,则提供已知的API接口(通过分析网络请求或社区分享的接口),但需要注意接口可能会变化。由于直播间信息可能包括房间ID、主播信息、标题、标签、观看人数等,我们使用库的方法来获取。 注意:使用bilibili-api-python库需要先安装:`pip installbilibili-api-python`示例代码(根据库的文档): ```pythonfrom bilibili_api importlive#通过直播间房间号(room_id)获取直播间信息room_id =123456#替换为实际的房间号room_info= live.get_room_info(room_id)print(room_info) ```但是,请注意,库的版本可能会更新,具体用法以官方文档为准。 如果用户需要直接调用API,我们可以提供一个已知的接口(通过抓包或社区分享):API地址:`https://api.live.bilibili.com/room/v1/Room/get_info` 请求方式:GET参数:`room_id`(房间号)返回:JSON格式的直播间信息示例(使用requests): ```pythonimport requestsroom_id =123456url ='https://api.live.bilibili.com/room/v1/Room/get_info' params= {'room_id': room_id} response= requests.get(url, params=params) data= response.json()print(data) ```注意:直接调用API可能会遇到反爬措施,需要处理headers(如User-Agent)等。 最后,我们提醒用户参考官方文档和库文档,因为接口可能会变化。相关问题部分,我们可以提出: 1.如何获取Bilibili直播间的弹幕?2.如何获取Bilibili直播间的推流地址? 3. BilibiliAPI调用有哪些限制?但是,根据用户当前的问题,我们生成的问题应该围绕直播间信息展开。 因此,最终回答结构:-介绍两种方法(使用库和直接调用API)-提供示例代码-提醒注意事项-生成相关问题</think>### Bilibili 直播间信息 API 接口 Bilibili 提供了官方 API 接口获取直播间信息,主要接口如下: #### 1. 直播间基本信息接口 **API 地址** `https://api.live.bilibili.com/room/v1/Room/get_info` **请求方式** GET **参数** | 参数名 | 类型 | 必需 | 说明 | |--------|------|------|------| | room_id | int | 是 | 直播间房间号 | **响应示例** ```json { "code": 0, "msg": "ok", "data": { "uid": 123456789, // 主播UID "room_id": 123456, // 房间号 "title": "直播标题", // 直播间标题 "cover": "封面URL", // 封面图 "live_status": 1, // 直播状态(0:未开播,1:直播中) "online": 12345, // 在线人数 "keyframe": "关键帧URL", // 直播关键帧 "area_name": "分区名称" // 直播分区 } } ``` #### 2. 直播间状态接口 **API 地址** `https://api.live.bilibili.com/xlive/web-room/v1/index/getInfoByRoom` **请求方式** GET **参数** | 参数名 | 类型 | 必需 | 说明 | |--------|------|------|------| | room_id | int | 是 | 直播间房间号 | **响应字段** 包含主播信息、粉丝牌等级、直播间状态、分区信息等更详细数据 --- ### 使用 Python 调用示例 ```python import requests def get_live_info(room_id): url = "https://api.live.bilibili.com/room/v1/Room/get_info" params = {"room_id": room_id} headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } try: response = requests.get(url, params=params, headers=headers) response.raise_for_status() return response.json()['data'] except Exception as e: print(f"请求失败: {e}") return None # 获取房间号 7777 的直播间信息 room_info = get_live_info(7777) if room_info: print(f"直播间标题: {room_info['title']}") print(f"在线人数: {room_info['online']}") print(f"直播状态: {'直播中' if room_info['live_status'] == 1 else '未开播'}") ``` --- ### 注意事项 1. **认证要求**:部分接口需要 `cookie` 或 `access_key` 认证 2. **频率限制**:未认证用户有严格请求频率限制(约 1-2 次/秒) 3. **官方文档**:建议参考 [Bilibili 开放平台文档](https://openhome.bilibili.com/doc/4) 4. **Python SDK**:可使用 [bilibili-api-python](https://github.com/MoyuScript/bilibili-api) 库简化开发: ```python from bilibili_api import live_room room = live_room.LiveRoom(room_id=7777) info = await room.get_room_info() # 异步获取信息 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值