基于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中的len()函数的使用
Apr 07 Python
Python中的元类编程入门指引
Apr 15 Python
python开发环境PyScripter中文乱码问题解决方案
Sep 11 Python
windows下Virtualenvwrapper安装教程
Dec 13 Python
Django使用详解:ORM 的反向查找(related_name)
May 30 Python
python实现监控某个服务 服务崩溃即发送邮件报告
Jun 21 Python
Python 限制线程的最大数量的方法(Semaphore)
Feb 22 Python
详解Python 爬取13个旅游城市,告诉你五一大家最爱去哪玩?
May 07 Python
pytorch nn.Conv2d()中的padding以及输出大小方式
Jan 10 Python
pycharm 对代码做静态检查操作
Jun 09 Python
解决Pycharm 运行后没有输出的问题
Feb 05 Python
Python3+Flask安装使用教程详解
Feb 16 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
关于zend studio 出现乱码问题的总结
2013/06/23 PHP
yii2 数据库读写分离配置示例
2017/02/10 PHP
[原创]php token使用与验证示例【测试可用】
2017/08/30 PHP
PHP回调函数简单用法示例
2019/05/08 PHP
Centos7 Yum安装PHP7.2流程教程详解
2019/07/02 PHP
提高代码性能技巧谈—以创建千行表格为例
2006/07/01 Javascript
javascript showModalDialog 多层模态窗口实现页面提交及刷新的代码
2009/11/28 Javascript
基于JQuery的Select选择框的华丽变身
2011/08/23 Javascript
javascript的push使用指南
2014/12/05 Javascript
JavaScript设计模式之建造者模式介绍
2014/12/28 Javascript
jQuery中prevAll()方法用法实例
2015/01/08 Javascript
jQuery中$.click()无效问题分析
2015/01/29 Javascript
浅谈javascript的分号的使用
2015/05/12 Javascript
jQuery中extend函数详解
2015/07/13 Javascript
javascript与Python快速排序实例对比
2015/08/10 Javascript
AngularJs中route的使用方法和配置
2016/02/04 Javascript
JavaScript的React框架中的JSX语法学习入门教程
2016/03/05 Javascript
js实现类bootstrap模态框动画
2017/02/07 Javascript
angularJs select绑定的model取不到值的解决方法
2018/10/08 Javascript
Vue中使用Lodop插件实现打印功能的简单方法
2019/12/19 Javascript
requests和lxml实现爬虫的方法
2017/06/11 Python
Python探索之爬取电商售卖信息代码示例
2017/10/27 Python
Python基于多线程实现ping扫描功能示例
2018/07/23 Python
python计算无向图节点度的实例代码
2019/11/22 Python
Python手绘可视化工具cutecharts使用实例
2019/12/05 Python
关于tf.reverse_sequence()简述
2020/01/20 Python
python与pycharm有何区别
2020/07/01 Python
python通用数据库操作工具 pydbclib的使用简介
2020/12/21 Python
教师简历自我评价
2014/02/03 职场文书
超市活动计划书
2014/04/24 职场文书
2014年平安创建工作总结
2014/11/24 职场文书
2016年班主任培训心得体会
2016/01/07 职场文书
JavaScript分页组件使用方法详解
2021/07/26 Javascript
AJAX实现指定部分页面刷新效果
2021/10/16 Javascript
JavaScript 事件捕获冒泡与捕获详情
2021/11/11 Javascript
js基于div丝滑实现贝塞尔曲线
2022/09/23 Javascript