基于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命令行解析之parse_known_args()函数和parse_args()使用区别介绍
Jan 24 Python
python实现旋转和水平翻转的方法
Oct 25 Python
windows7 32、64位下python爬虫框架scrapy环境的搭建方法
Nov 29 Python
Python设计模式之工厂方法模式实例详解
Jan 18 Python
基于PyQt4和PySide实现输入对话框效果
Feb 27 Python
python使用Plotly绘图工具绘制气泡图
Apr 01 Python
详解Python图像处理库Pillow常用使用方法
Sep 02 Python
Python调用JavaScript代码的方法
Oct 27 Python
安装pyinstaller遇到的各种问题(小结)
Nov 20 Python
解决python 在for循环并且pop数组的时候会跳过某些元素的问题
Dec 11 Python
python实现ping命令小程序
Dec 28 Python
python利用while求100内的整数和方式
Nov 07 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连接SQLSERVER 注意事项(附dll文件下载)
2012/06/28 PHP
php循环创建目录示例分享(php创建多级目录)
2014/03/04 PHP
List Installed Software Features
2007/06/11 Javascript
Javascript表达式中连续的 && 和 || 之赋值区别
2010/10/17 Javascript
JavaScript中的私有/静态属性介绍
2012/07/26 Javascript
Uploadify上传文件方法
2016/03/16 Javascript
js如何准确获取当前页面url网址信息
2020/09/13 Javascript
JS实现显示带倒影的图片横排居中放大展示特效实例【测试可用】
2016/08/23 Javascript
laydate.js日期时间选择插件
2017/01/04 Javascript
微信小程序开发的四十个技术窍门总结(推荐)
2017/01/23 Javascript
js图片加载效果实例代码(延迟加载+瀑布流加载)
2017/05/12 Javascript
在bootstrap中实现轮播图实例代码
2017/06/11 Javascript
Vue项目全局配置页面缓存之按需读取缓存的实现详解
2018/08/01 Javascript
详解a标签添加onclick事件的几种方式
2019/03/29 Javascript
微信小程序访问豆瓣电影api的实现方法
2019/03/31 Javascript
基于jQuery实现挂号平台首页源码
2020/01/06 jQuery
详细分析vue响应式原理
2020/06/22 Javascript
Python使用cookielib模块操作cookie的实例教程
2016/07/12 Python
Python 从相对路径下import的方法
2018/12/04 Python
python脚本开机自启的实现方法
2019/06/28 Python
10分钟用python搭建一个超好用的CMDB系统
2019/07/17 Python
python-Web-flask-视图内容和模板知识点西宁街
2019/08/23 Python
基于python调用psutil模块过程解析
2019/12/20 Python
Pycharm 跳转回之前所在页面的操作
2021/02/05 Python
HTML5在IE10、火狐下中文乱码问题的解决方法
2013/11/18 HTML / CSS
Stubhub英国:购买体育、演唱会和剧院门票
2018/06/10 全球购物
WEB控件及HTML服务端控件能否调用客户端方法?如果能,请解释如何调用?
2015/08/25 面试题
高中体育课教学反思
2016/02/16 职场文书
2019毕业典礼主持词!
2019/07/05 职场文书
php中pcntl_fork详解
2021/04/01 PHP
教你漂亮打印Pandas DataFrames和Series
2021/05/29 Python
go goroutine 怎样进行错误处理
2021/07/16 Golang
java设计模式--三种工厂模式详解
2021/07/21 Java/Android
68行Python代码实现带难度升级的贪吃蛇
2022/01/18 Python
SQL Server中锁的用法
2022/05/20 SQL Server
mysql查看表结构的三种方法总结
2022/07/07 MySQL