用Python实现一个模仿UP主弹幕控制的直播间功能

灵感来源

之前在B站看到一个有意思的视频:

【B站】【亦】终极云游戏!五千人同开一辆车,复现经典群体智慧实验

大家可以看看,很有意思。

up主通过代码实现了实时读取直播间里的弹幕内容,进而控制自己的电脑,把弹幕翻译成指令操控《赛博朋克2077》游戏。

观众也越来越多,最后甚至还把直接间搞崩了(当然,其实是因为那天B站全站崩了)。

我十分好奇到底是怎么做到的。

外行看热闹,内行看门道,作为半个内行,我们就模仿UP主的想法,自己做一个。

所以今天我的目标就是复刻一个 通过弹幕控制直播间 的代码,并且最终在自己的直播间开播。

先给大家看看最终我的成品小视频:

【B站】模仿UP主,做一个弹幕控制的直播间!

看起来是不是很像样了。

初版设计思路

首先在脑海里规划一个大致的思路,如下图:

这个思路看起来很简单,不过还是得解释一下,首先我们要搞清楚,弹幕的内容是怎么抓到的。

大部分我们常见的直播平台,在浏览器端,弹幕都是通过WebSocket来推送给观众的。在手机平板等客户端(非Web端),可能会有一些更加复杂的TCP进行弹幕的推送。

关于TCP的消息投递,有个很好的文章,就是美团的这个:美团终端消息投递服务Pike的演进之路

归根结底,这些弹幕都是通过在客户端和服务端建立长链接来实现的。

所以,我们需要做的就是用代码作为客户端,与直播平台进行长链接。这样就能拿到弹幕。

我们只是需要实现整个弹幕控制的流程,所以弹幕的抓取也不是本文的重点,我们来淘一个现成的轮子!在Github上一顿找,找到了一个非常不错的开源库,里面能够获取很多直播平台的弹幕:

https://github.com/wbt5/real-url

获取斗鱼&虎牙&哔哩哔哩&抖音&快手等 58 个直播平台的真实流媒体地址(直播源)和弹幕,直播源可在 PotPlayer、flv.js 等播放器中播放。

我们把代码clone下来,运行main函数,随便输入一个Bilibili直播间地址,就能拿到直播间实时的弹幕流:

代码里把获取到的一条条弹幕(包括用户名)直接打印在了控制台。

他是如何做到的呢?核心的Python代码如下(不熟悉Python?不要紧,就当做伪代码,很容易看懂):

wss_url = 'wss://broadcastlv.chat.bilibili.com/sub'
heartbeat = b'\x00\x00\x00\x1f\x00\x10\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x5b\x6f\x62\x6a\x65\x63\x74\x20' \
                b'\x4f\x62\x6a\x65\x63\x74\x5d '
  heartbeatInterval = 60

@staticmethod
async def get_ws_info(url):
    url = 'https://api.live.bilibili.com/room/v1/Room/room_init?id=' + url.split('/')[-1]
    reg_datas = []
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            room_json = json.loads(await resp.text())
            room_id = room_json['data']['room_id']
            data = json.dumps({
                'roomid': room_id,
                'uid': int(1e14 + 2e14 * random.random()),
                'protover': 1
            }, separators=(',', ':')).encode('ascii')
            data = (pack('>i', len(data) + 16) + b'\x00\x10\x00\x01' +
                    pack('>i', 7) + pack('>i', 1) + data)
            reg_datas.append(data)

    return Bilibili.wss_url, reg_datas

它连上了Bilibili的直播弹幕WSS地址,也就是WebSocket地址,然后伪装成客户端,接受弹幕推送。

OK,做完了第一步,下一步就是用消息队列将弹幕发送出来。开启单独的消费者接收弹幕。

为了实现上尽量简单,就不上那些专业的消息队列了,这里用了redis的list作为队列,将弹幕内容放进去。

发送者核心代码如下:

# 链接Redis
def init_redis():
    r = redis.Redis(host='localhost', port=6379, decode_responses=True)
    return r

# 消息发送者
async def printer(q, redis):
    while True:
        m = await q.get()
        if m['msg_type'] == 'danmaku':
            print(f'{m["name"]}:{m["content"]}')
            list_str = list(m["content"])
            print("弹幕拆分:", list_str)
            for char in list_str:
                if char.lower() in key_list:
                    print('推送队列:', char.lower())
                    redis.rpush(list_name, char.lower())

完成了弹幕内容的发送后,需要写一个消费者,消费这些弹幕,把里面的指令都提取出来。

并且,在消费者收到弹幕后,如何消费呢?我们需要一个能够用代码指令控制电脑的办法。

咱继续本着不造轮子的原则,找到了一个Python的自动化控制库PyAutoGUI

PyAutoGUI is a cross-platform GUI automation Python module for human beings. Used to programmatically control the mouse & keyboard.

安装上这个库,在代码中引入,便可以通过他的API控制电脑鼠标和键盘执行对应的操作。简直是完美啊!

消费者(控制电脑)核心Python代码如下:

# 链接Redis
def init_redis():
    r = redis.Redis(host='localhost', port=6379, decode_responses=True)
    return r

# 消费者
def control(key_name):
    print("key_name =", key_name)
    if key_name == None:
        print("本次无指令发出")
        return
    key_name = key_name.lower()
    # 控制电脑指令
    if key_name in key_list:
        print("发出指令", key_name)
        pyautogui.keyDown(key_name)
        time.sleep(press_sec)
        pyautogui.keyUp(key_name)
        print("结束指令", key_name)

if __name__ == '__main__':
    r = init_redis()
    print("开始监听弹幕消息, loop_sec =", loop_sec)
    while True:
        key_name = r.lpop(list_name)
        control(key_name)
        time.sleep(loop_sec)

ok,大功告成,我们打开弹幕发送队列和消费者,这个不断循环消费的队列就开始运行了。一旦弹幕中有wsad这种控制游戏常用的按键,电脑就会自己给自己发出指令。

初版运行中的问题

我兴冲冲的打开自己的B站直播间,开始调试,结果发现我还是太天真了。这个初版代码暴露了非常多的问题。我们一个个来说下是什么问题,我是如何解决的。

指令不人性化

水友们其实很喜欢发送类似www dddd这类重复单词(叠词),但初版的实现只支持单个字幕,水友们发现不得劲,没有作用后,就从直播间走了。

这点很容易解决,把弹幕内容拆分成每个单词,然后再推送给队列。

解决方法:拆解弹幕,把DDD,拆成D,D,D,发送个消费者。

危险指令

首先是玩家的指令超出了应该有的范围。

在我把赛博朋克游戏打开,让弹幕观众控制游戏里的开车时,有个神秘观众进了直播间,默默发了个“F”,然后。。。

然后游戏里的V(主角名)就从车里下来了,淦,我是让你们开车的,不是让你们下来和警察斗殴的。。。

解决方法:添加弹幕过滤器。

# 将弹幕进行拆分,只发送指定的指令给消费者
key_list = ('w', 's', 'a', 'd', 'j', 'k', 'u', 'i', 'z', 'x', 'f', 'enter', 'shift', 'backspace')
list_str = list(m["content"])
            print("弹幕拆分:", list_str)
            for char in list_str:
                if char.lower() in key_list:
                    print('推送队列:', char.lower())
                    redis.rpush(list_name, char.lower())

上面两个问题解决后,发送者就像下面这样运行了:

弹幕指令堆积

这是个很大的问题,如果处理所有水友发送的全部弹幕指令,一定会存在消费不过来的问题。

解决方法:需要固定时间处理弹幕,其他抛弃。

if __name__ == '__main__':
    r = init_redis()
    print("开始监听弹幕消息, loop_sec =", loop_sec)
    while True:
        key_name = r.lpop(list_name)
        # 每次只取出一个指令,然后把list清空,也就是这个时间窗口内其他弹幕都扔掉!
        r.delete(list_name)
        control(key_name)
        time.sleep(loop_sec)

弹幕从发出到观众看到结果有延迟

在最开始的视频里,你们也能感受到了,从观众的指令发出,到最终被观众看到,大概要经历5秒的延迟。其中,起码有3秒,都是网络直播流的延迟,这一点,很难去优化。

回炉重造后的版本

经过一系列调优和涉及,我们的版本也算是从V0.1到了V0.2了。猛虎落泪。

下面是重构后的结构图:

后记

在写完这个项目后,我在直播间试了很多次,体验已经无限接近UP主当时的视频了。我开播挂在那边好久,但是,人气最高的时候,也只有20几个人,寥寥十几条弹幕,还有很多是我发的。我还期望着观众能够拉更多人进来一起玩呢,事与愿违啊。

由此可得出结论,我,先得有粉丝,才能玩得起来啊,呜呜呜呜。大家要是不介意,可以关注下我的B站账号,也叫:蛮三刀酱。我会偶尔抽风发点有趣的技术视频的。

本文实现的全部代码已经开源在了Github上,大家可以在自己的直播间里试试呀:

https://github.com/qqxx6661/live_comment_control_stream

到此这篇关于用Python实现一个模仿UP主弹幕控制的直播间功能的文章就介绍到这了,更多相关python弹幕控制的直播间内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Python爬虫爬取Bilibili弹幕过程解析

    先来思考一个问题,B站一个视频的弹幕最多会有多少? 比较多的会有2000条吧,这么多数据,B站肯定是不会直接把弹幕和这个视频绑在一起的. 也就是说,有一个视频地址为https://www.bilibili.com/video/av67946325,你如果直接去requests.get这个地址,里面是不会有弹幕的,回想第一篇说到的携程异步加载数据的方式,B站的弹幕也一定是先加载当前视频的界面,然后再异步填充弹幕的. 接下来我们就可以打开火狐浏览器(平常可以火狐谷歌控制台都使用,因为谷歌里面因为插件

  • Python爬虫 bilibili视频弹幕提取过程详解

    两个重要点 1.获取弹幕的url是以 .xml 结尾 2.弹幕url的所需参数在视频url响应的 javascript 中 先看代码 import requests from lxml import etree import re # 使用手机UA headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like

  • 详解python爬取弹幕与数据分析

    很不幸的是,由于疫情的关系,原本线下的AWD改成线上CTF了.这就很难受了,毕竟AWD还是要比CTF难一些的,与人斗现在变成了与主办方斗. 虽然无奈归无奈,但是现在还是得打起精神去面对下一场比赛.这个开始也是线下的,决赛地点在南京,后来是由于疫情的关系也成了线上. 当然,比赛内容还是一如既往的得现学,内容是关于大数据的. 由于我们学校之前并没有开设过相关培训,所以也只能自己琢磨了. 好了,废话先不多说了,正文开始. 一.比赛介绍 大数据总体来说分为三个过程. 第一个过程是搭建hadoop环境.

  • Python爬取英雄联盟MSI直播间弹幕并生成词云图

    一.环境准备 安装相关第三方库 pip install jieba pip install wordcloud 二.数据准备 爬取对象:2021年5月23号,RNG夺冠直播间的弹幕信息 爬取对象路径: 方式1.根据开发者工具(F12),获取请求url.请求头.cookie等信息: 方式2:根据直播地址url,前+字符i 我们这里演示的是,采用方式2. 三.代码如下 import requests, re import jieba, wordcloud """ # 以下是练习代

  • 用Python实现一个模仿UP主弹幕控制的直播间功能

    灵感来源 之前在B站看到一个有意思的视频: [B站][亦]终极云游戏!五千人同开一辆车,复现经典群体智慧实验 大家可以看看,很有意思. up主通过代码实现了实时读取直播间里的弹幕内容,进而控制自己的电脑,把弹幕翻译成指令操控<赛博朋克2077>游戏. 观众也越来越多,最后甚至还把直接间搞崩了(当然,其实是因为那天B站全站崩了). 我十分好奇到底是怎么做到的. 外行看热闹,内行看门道,作为半个内行,我们就模仿UP主的想法,自己做一个. 所以今天我的目标就是复刻一个 通过弹幕控制直播间 的代码,并

  • Python使用Selenium模块模拟浏览器抓取斗鱼直播间信息示例

    本文实例讲述了Python使用Selenium模块模拟浏览器抓取斗鱼直播间信息.分享给大家供大家参考,具体如下: import time from multiprocessing import Pool from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.web

  • 使用Python编写一个模仿CPU工作的程序

    今天早上早些时候,在我的Planet Python源中,我读到了一篇有趣的文章"开发CARDIAC:纸板计算机(Developing upwards: CARDIAC: The Cardboard Computer)",它是关于名为Cardiac的纸板计算机的.我的一些追随者和读者应该知道,我有一个名为简单CPU(simple-cpu)的项目,过去的数月我一直工作于此,并且已经发布了源代码.我真的应该给这个项目提供一个合适的许可证,这样,其他人可能更感兴趣,并在他们自己的项目中使用.不

  • 基于Python实现给喜欢的主播自动发弹幕

    目录 前言 实现步骤 全部代码 前言 发弹幕只是其中一个小小的功能,还可以自动点赞.收藏.投币.自动播放.私信等等,但是我们只演示这个,其它的不做展示. 实现步骤 先打开一个视频或者直播,F12打开开发者工具,点击network. 然后点这个清空一下 再发送一个弹幕,然后可以看到这个send,有一个post请求. 点击payload可以看到我们刚刚发送的弹幕相关数据 然后来写代码 首先导入模块 import random import time 这是我们的url url = 'https://a

  • Python 中PyQt5 点击主窗口弹出另一个窗口的实现方法

    1.先使用Qt designer设计两个窗口,一个是主窗口,一个是子窗口   其中主窗口是新建-Main Window,子窗口是Dialog窗体. 两个窗口不能是同一类型,否则会崩溃. 并保存为EyeTracking_main.ui和EyeTracking_process.ui(因为我在做眼动追踪,因此窗体命名与此相关,后同),使用UIC工具转成.py文件. 2.写一个驱动函数调用两个窗体 主窗体Eyetracking_main.py from PyQt5 import QtCore, QtGu

  • 使用Python获取爱奇艺电视剧弹幕数据的示例代码

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理. 以下文章来源于数据STUDIO,作者龙哥带你飞 Python分析抖音用户行为数据视频讲解地址 https://www.bilibili.com/video/BV1yp4y1q7ZC/ 数据获取是数据分析中的重要的一步,数据获取的途径多种多样,在这个信息爆炸的时代,数据获取的代价也是越来越小.因此如此,仍然有很多小伙伴们无法如何获取有用信息.此处以最近的热播排行榜第一名的<流金岁月>为例,手把手

  • 用Python做一个哔站小姐姐词云跳舞视频

    目录 一.前言 二.实现思路 1. 下载视频 2. 获取弹幕内容 3. 从视频中提取图片 4. 利用百度AI进行人像分割 5. 小姐姐跳舞词云生成 6. 合成跳舞视频 7. 视频插入音频 一.前言 B站上的漂亮的小姐姐真的好多好多,利用 you-get 大法下载了一个 B 站上跳舞的小姐姐视频,利用视频中的弹幕来制作一个漂亮小姐姐词云跳舞视频,一起来看看吧. 二.实现思路 1. 下载视频 安装 you-get 库 pip install you-get -i http://pypi.douban

  • Python实现B站UP主小助手详解开发流程

    功能点 显示日期时间 显示树莓派当前局域网IP 显示当前UP主粉丝数 显示B站未读消息 显示B站视频总计播放数 显示视频总计获赞数 显示总计获得充电次数 显示直播间人气值 显示直播间弹幕 将直播间弹幕念出来(TTS) 大家好,我是 大帅 ,一个 老 程序 猿 . 这是我第一次写Python哟,写得不好请多多指教 前言 前段时间在掘金社区写文章,得了一台 树莓派3B ,加上之前在闲鱼曾经淘到一块 1280x400 的长条屏,就想着把一直想要的哔哩哔哩UP主助手给完成了. 先上效果 有弹幕的时候是这

  • 修改Python的pyxmpp2中的主循环使其提高性能

    引子 之前clubot使用的pyxmpp2的默认mainloop也就是一个poll的主循环,但是clubot上线后资源占用非常厉害,使用strace跟踪发现clubot在不停的poll,查看pyxmpp2代码发现pyxmpp2的poll在使用超时阻塞时使用最小超时时间,而最小超时时间一直是0,所以会变成一个没有超时的非阻塞poll很浪费资源,不打算更改库代码,所以自己仿照poll的mainloop写了一个更加高效的epoll的mainloop 实现 #!/usr/bin/env python #

  • 使用Python编写一个最基础的代码解释器的要点解析

    一直以来都对编译器和解析器有着很大的兴趣,也很清楚一个编译器的概念和整体的框架,但是对于细节部分却不是很了解.我们编写的程序源代码实际上就是一串字符序列,编译器或者解释器可以直接理解并执行这个字符序列,这看起来实在是太奇妙了.本文会用Python实现一个简单的解析器,用于解释一种小的列表操作语言(类似于python的list).其实编译器.解释器并不神秘,只要对基本的理论理解之后,实现起来也比较简单(当然,一个产品级的编译器或解释器还是很复杂的). 这种列表语言支持的操作: veca = [1,

随机推荐