基于asyncio 异步协程框架实现收集B站直播弹幕


Posted in Python onSeptember 11, 2016

前言

虽然标题是全站,但目前只做了等级 top 100 直播间的全天弹幕收集。

弹幕收集系统基于之前的B 站直播弹幕姬 Python 版修改而来。具体协议分析可以看上一篇文章。

直播弹幕协议是直接基于 TCP 协议,所以如果 B 站对类似我这种行为做反制措施,比较困难。应该有我不知道的技术手段来检测类似我这种恶意行为。

我试过同时连接 100 个房间,和连接单个房间 100 次的实验,都没有问题。>150 会被关闭链接。

直播间的选取

现在弹幕收集系统在选取直播间上比较简单,直接选取了等级 top100。

以后会修改这部分,改成定时去 http://live.bilibili.com/all 查看新开播的直播间,并动态添加任务。

异步任务和弹幕存储

收集系统仍旧使用了 asyncio 异步协程框架,对于每一个直播间都使用如下方法来加进 loop 中。

danmuji = bilibiliClient(url, self.lock, self.commentq, self.numq)
task1 = asyncio.ensure_future(danmuji.connectServer())
task2 = asyncio.ensure_future(danmuji.HeartbeatLoop())

其实若将心跳任务 HeartbeatLoop 放入 connectorServer 中去启动,代码看起来更优雅一些。但这么做是因为我需要维护一个任务列表,后面会有描述。

在弹幕存储上我花了些时间选择。

数据库存储是一个同步 IO 的过程,Insert 的时候会阻塞弹幕收集的任务。虽然有 aiomysql 这种异步接口,但配置数据库太麻烦,我的设想是这个小系统能够方便地部署。

最终我选择使用自带的 sqlite3。但 sqlite3 无法做并行操作,故开了一个线程单独进行数据库存储。在另一个线程中,100 * 2 个任务搜集所有的弹幕、人数信息,并塞进队列 commentq, numq 中。存储线程每隔 10s 唤醒一次,将队列中的数据写进 sqlite3 中,并清空队列。

在多线程和异步的配合下,网络流量没有被阻塞。

可能的连接失败场景处理

弹幕协议是直接基于 TCP,位与位直接关联性较强,一旦解析错误,很容易就抛 Exception(个人感觉,虽然 TCP 是可靠传输,但B站服务器自身发生错误也是有可能的)。所以有必要设计一个自动重连机制。

在 asyncio 文档中提到,

Done means either that a result / exception are available, or that the future was cancelled.

函数正常返回、抛出异常或者是被 cancel,都会退出当前任务。可以使用 done() 来判断。

每一个直播间对应两个任务,解析任务是最容易挂的,但并不会影响心跳任务,所以必须找出并将对应心跳任务结束。
在创建任务的时候使用字典记录每个房间的两个任务,

self.tasks[url] = [task1, task2]

在运行过程中,每隔 10s 做一次检查,

for url in self.tasks:
  item = self.tasks[url]
  task1 = item[0]
  task2 = item[1]
  if task1.done() == True or task2.done() == True:
    if task1.done() == False:
      task1.cancel()
    if task2.done() == False:
      task2.cancel()
    danmuji = bilibiliClient(url, self.lock, self.commentq, self.numq)
    task11 = asyncio.ensure_future(danmuji.connectServer())
    task22 = asyncio.ensure_future(danmuji.HeartbeatLoop())
    self.tasks[url] = [task11, task22]

实际我只见过一次任务失败的场景,是因为主播房间被封了,导致无法进入直播间。

结论

  1. B站人数是按照连接弹幕服务器的链接数量统计的。通过操纵链接量,可以瞬间增加任意人数观看,有商机?
  2. 运行的这几天中,发现即使大部分房间不在直播,也能有 >5 的人数,包括凌晨。我只能猜测也有和我一样的人在 24h 收集弹幕。
  3. top100 平均一天 40M 弹幕数据。
  4. 收集的弹幕能做什么?还没想好,可能可以拿来做用户行为分析 -_^

最后附上本源码的GITHUB地址 https://github.com/lyyyuna/bilibili_danmu_colloector

Python 相关文章推荐
更改Python命令行交互提示符的方法
Jan 14 Python
Python类的用法实例浅析
May 27 Python
python实现自动网页截图并裁剪图片
Jul 30 Python
python3.6数独问题的解决
Jan 21 Python
Python读取stdin方法实例
May 24 Python
浅谈python元素如何去重,去重后如何保持原来元素的顺序不变
Feb 28 Python
jupyter notebook 添加kernel permission denied的操作
Apr 21 Python
详解python中groupby函数通俗易懂
May 14 Python
使用OpenCV去除面积较小的连通域
Jul 05 Python
Python3+RIDE+RobotFramework自动化测试框架搭建过程详解
Sep 23 Python
如何使用python自带IDLE的几种方法
Oct 10 Python
Pytorch之扩充tensor的操作
Mar 04 Python
asyncio 的 coroutine对象 与 Future对象使用指南
Sep 11 #Python
Python中使用asyncio 封装文件读写
Sep 11 #Python
Python 如何访问外围作用域中的变量
Sep 11 #Python
Python优化技巧之利用ctypes提高执行速度
Sep 11 #Python
Python 中的with关键字使用详解
Sep 11 #Python
Python冒泡排序注意要点实例详解
Sep 09 #Python
通过5个知识点轻松搞定Python的作用域
Sep 09 #Python
You might like
PHP 获取远程文件大小的3种解决方法
2013/07/11 PHP
解析php中如何调用用户自定义函数
2013/08/06 PHP
LINUX下PHP程序实现WORD文件转化为PDF文件的方法
2016/05/13 PHP
ThinkPHP框架下微信支付功能总结踩坑笔记
2019/04/10 PHP
PHP实现基于状态的责任链审批模式详解
2019/05/31 PHP
javascript 处理事件绑定的一些兼容写法
2009/12/24 Javascript
javascript一个无懈可击的实例化XMLHttpRequest的方法
2010/10/13 Javascript
js FLASH幻灯片字符串中有连接符&的处理方法
2012/03/01 Javascript
js 获取radio按钮值的实例
2013/08/17 Javascript
js实现幻灯片效果(基于jquery插件)
2013/11/05 Javascript
jQuery选择器源码解读(八):addCombinator函数
2015/03/31 Javascript
javascript 常见功能汇总
2015/06/11 Javascript
javascript中checkbox使用方法简单实例演示
2015/11/17 Javascript
使用UrlConnection实现后台模拟http请求的简单实例
2017/01/04 Javascript
Node.js中的require.resolve方法使用简介
2017/04/23 Javascript
RxJS的入门指引和初步应用
2019/06/15 Javascript
Vue-cli3.x + axios 跨域方案踩坑指北
2019/07/04 Javascript
浅析Angular 实现一个repeat指令的方法
2019/07/21 Javascript
layui 富文本编辑器和textarea值的相互传递方法
2019/09/18 Javascript
node.js使用zlib模块进行数据压缩和解压操作示例
2020/02/12 Javascript
获取python文件扩展名和文件名方法
2018/02/02 Python
python 使用re.search()筛选后 选取部分结果的方法
2018/11/28 Python
python sklearn库实现简单逻辑回归的实例代码
2019/07/01 Python
Django高级编程之自定义Field实现多语言
2019/07/02 Python
解决Django中修改js css文件但浏览器无法及时与之改变的问题
2019/08/31 Python
Python操作多维数组输出和矩阵运算示例
2019/11/28 Python
使用PyOpenGL绘制三维坐标系实例
2019/12/24 Python
Python TCPServer 多线程多客户端通信的实现
2019/12/31 Python
Keras之fit_generator与train_on_batch用法
2020/06/17 Python
详解CSS3的box-shadow属性制作边框阴影效果的方法
2016/05/10 HTML / CSS
Chemist Warehouse中文网:澳洲连锁大药房
2021/02/05 全球购物
史上最全面的Java面试题汇总!
2015/02/03 面试题
关于旷工的检讨书
2014/02/02 职场文书
党校毕业心得体会
2014/09/13 职场文书
学习保证书100字
2015/02/26 职场文书
2016年大学生暑期社会实践方案
2015/11/26 职场文书