0

0

如何获取斗鱼直播间的弹幕信息?

php中文网

php中文网

发布时间:2016-06-06 16:11:05

|

7324人浏览过

|

来源于php中文网

原创

如何获取如图红色框中的弹幕信息呢?想做一些基于弹幕信息的二次开发。


如何获取斗鱼直播间的弹幕信息?

回复内容:

在参考顶楼的回答和评论后,正确获取到了弹幕,来回报下社会,代码已经放在Github上了! douyu/danmu.cc at master · fishioon/douyu · GitHub
代码是C写的,本来用的python,感觉C更好用(在处理数据的时候)。

比较喜欢看yyf的dota2直播,但是最近yyf经常吹B,说有网友弹幕问“枫哥枫哥,你的XX怎么这么厉害啊”,水友们纷纷表示不信,所以就想着把弹幕爬下来,同时还有那些说赢了直播吃翔的,我通通都要记录下来!!!

回答正题,那我们该如何弄清楚这个协议呢?
弹幕属于实时消息,第一反应应该用websocket实现,打开chrome,F12,websocket中竟然找不到!那估计是通过Flash实现了。前端不熟,找了半天也没找到JS实现代码,只好祭出wireshark来分析了。
看了顶楼的提示,在wireshark中查看端口为8601的数据包,如下图所示。

如何获取斗鱼直播间的弹幕信息?

前三个数据包就是TCP的三次握手啦,复习下;接着我们看第四个数据包,如下图所示

如何获取斗鱼直播间的弹幕信息?

一般来说通信协议设计“内容长度”+“内容”,我们来看tcp数据包内容,前四个字节为0x59 = 5*16+9 = 89,再看整个数据包长度为93,正好符合长度+内容,差不多我们可以确定通信协议如下:
struct {
int len; //数据包长度
int code; //经抓包发现该字段一直与len字段一致
int magic; //不知道啥意思,发现请求都是0x2b1, 返回都是0x2b2
char content[0]; //消息具体内容
}
从图中的内容中可以看到:
1. 登录弹幕服务器: "type@=loginreq/username@%s=/password@=%s/roomid@=%d/ct@=2/",输入自己的账号密码(经测试发现,账号密码随便填都能成功),要看的房间id,如yyf的房间id为52428
通过socket发送上面的内容后,你回收到这样的一条数据,格式与发送格式一样:
“type@=loginres/userid@=0/roomgroup@=0/pg@=0/sessionid@=0/username@=/nickname@=/live_stat@=0/is_illegal@=0/npv@=0/best_dlev@=0/cur_lev@=0/”,没发现有啥有用的数据。
2. 收到上面的消息后,这时候要加入一个组,格式如下:
"type@=joingroup/rid@=%d/gid@=%d/",rid就是房间id,要注意的问题来了:
gid应该是group id,登录不同房间该id都不一样,每次我都是抓包来查看该id是多少,有知情人告诉我吗?(一楼评论中也提到这个问题)
发送上面的消息后,我们就可以安心的接收数据了,然后从数据中提取我们想要的就可以了,其中很多数据都不懂啥意思。
最后我们看下yyf房间的弹幕哈!(节奏带的飞起)

创伴
创伴

专为内容创作者打造的AI创作工具,覆盖选题灵感、脚本创作、素材生成到智能发布

下载
如何获取斗鱼直播间的弹幕信息?

Update 20160214 : 更新Python和Ruby客户端(请大家不要问我为什么情人节这一天为什么闲着没事更新代码)
Update 20160220 : 更新Python客户端,增加直播视频的Live获取,以及Mac平台下面的Mplayer的视频播放.代码均放在Github上面. GitHub - twocucao/danmu.fm: douyutv danmu 斗鱼TV 弹幕助手

由于zhihu没有法子贴动态图,那只好移步到我的博客一看了.(看博客之前记得点赞╮(╯_╰)╭)

Python程序员如何优雅的看斗鱼TV

===================优雅的看斗鱼TV的分割线==================================

-1.如果不想看长文,直接使用.则在安装好Python3或者Ruby2.0以上版本.

#安装Python客户端
pip3 install danmu.fm

# 比如主播的直播间
danmu.fm http://www.douyutv.com/16789
#或者
danmu.fm 16789
#安装Ruby客户端
gem install danmu
#使用
danmu douyu [room_id/url]
#比如
danmu douyu qiuri
danmu douyu http://www.douyutv.com/13861
[多图预警]

Update:2016.1.16
总结在博客里:抓取斗鱼直播弹幕
Github 项目地址:brucezz/DouyuCrawler
欢迎去提各种 issue & PR (代码水平不高)...


/********************************************************************************************/
Update:2016.1.14
最近几天会把 Java 项目发布到 Github 上面,大家提提意见 :)

/********************************************************************************************/
之前看到这个问题,感觉挺好玩的,就研究了下。
照着前面的思路,抓包分析。其他几位的回答,基本能抓到弹幕数据了,但是其中一个参数gid,需要手动抓包得到。我也查看了浏览器加载的js文件,没看到相关线索。最后看到排名第一的回答评论里有人说可以通过请求页面里server_config的ip。
然后我就顺着这条线索探索下去了。
具体过程如下:

先获取到直播页面中的server_config相关字段,发现是经过urlencode的。
如何获取斗鱼直播间的弹幕信息?然后进行urldecode,得到json数据,再格式化一下,
UrlEncode编码/UrlDecode解码
JSON在线编辑
就变成这样:
[{"ip":"119.90.49.107","port":"8035"},{"ip":"119.90.49.102","port":"8008"},{"ip":"119.90.49.110","port":"8050"},{"ip":"119.90.49.104","port":"8020"},{"ip":"119.90.49.107","port":"8034"},{"ip":"119.90.49.92","port":"8059"},{"ip":"119.90.49.95","port":"8071"},{"ip":"119.90.49.101","port":"8001"},{"ip":"119.90.49.93","port":"8063"},{"ip":"119.90.49.91","port":"8053"}]
##################20160325更新##########################
最近斗鱼修改了弹幕格式,大概长这个样子
b'\x81\x00\x00\x00\x81\x00\x00\x00\xb2\x02\x00\x00type@=chatmsg/rid@=9401/uid@=12635840/nn@=\xe8\xb6\x85\xe8\xb6\x8a\xe7\xa5\x9e\xe7\x9a\x84boy/txt@=\xe6\x89\x93\xe8\x84\xb8\xe5\x90\xa7/cid@=20e4e3fe427e410c67c5010000000000/level@=5/\x00'
优雅的获取弹幕和看弹幕:

如何获取斗鱼直播间的弹幕信息?

随便抓了100个房间大概半小时。
优雅的抓出那些说“屎尿屁”的“精神周星驰”:
(那么大家都发现了,疯狂说这些一般都是20级以下的人。斗鱼要到20级大概是充值3000-4000左右好像。那么你们明白什么了吗)
如何获取斗鱼直播间的弹幕信息? 如何获取斗鱼直播间的弹幕信息? 如何获取斗鱼直播间的弹幕信息? 如何获取斗鱼直播间的弹幕信息?

最后还有更好玩的(大家来找壕,看看壕都喜欢去什么房间):
如何获取斗鱼直播间的弹幕信息?






















当然编程的方式也要优雅:Python

至于方法,最高赞已经很干净清晰了的解答了,我这里做小小补充。

其实不需要那么多工序,用socket直接3个命令就可以了:

下面伪代码:

client_danmu = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#在客户端开启心跳维护
client_danmu.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1) 


#官方弹幕服务器'openbarrage.douyutv.com'
SERVER = socket.gethostbyname('openbarrage.douyutv.com')
PORT = 8601

client_danmu.connect((SERVER,PORT))
client_danmu.settimeout(2)    #记得设置延迟,不然卡着recv

#这里发的要处理下发送内容,用最高赞的方法获取封包格式就好了
封包1 = 'type@=loginreq/username@=visitor1234567/password@=/roomid@=房间号/'
#登陆弹幕服务器
client_danmu.send(封包1)

封包2 =  'type@=joingroup/rid@=房间号/gid@=-9999/'
#加入弹幕组,这里要注意,gid就是弹幕组号。-9999是斗鱼开放的第三方获取弹幕的组别。是能获取所有弹幕的。因为有些主播动辄几十万人,那弹幕量如果都发给所有用户,那卡死用户事小,斗鱼自己流量事大啊。所以一般都是分组发送,所以你平时在浏览器看的并不一定是所有弹幕。用-9999这个组别号是可以获取所有的
client_danmu.send(封包2)

#这里开始就是用while循环获取弹幕了,记得大概每30秒发一个心跳包过去。
封包3 =  'type@=mrkl/'
client_danmu.send(封包3)
那我说说?
是这样的,斗鱼的弹幕信息有个自己的服务器
danmu.douyutv.com 可以抓包看到,他通过TCP连接到斗鱼弹幕服务器,然后获取的信息,那么想要对它进行二次开发只需要模拟用户连接并登入弹幕服务器即可。
 弹幕连接 OnConn
(program):1  弹幕 UserLogin [type@=loginreq/username@=xxxx/password@=1234567890123456/roomid@=58718/]
(program):1 弹幕 网络数据 [type@=loginres/userid@=0/roomgroup@=67108896/pg@=0/sessionid@=6/username@=/nickname@=/is_signined@=432003648/signin_count@=32756/s@=% ô/live_stat@=125387584/npv@=32756/]
(program):1 弹幕登录成功
VM160:1 弹幕分组 [type@=joingroup/rid@=58718/gid@=0/]
2VM171:1 FMPNetStream连接状态:NetStream.Buffer.Full
VM175:1 弹幕 网络数据 [type@=userenter/rid@=58718/gid@=0/userinfo@=id@A=4295897@Sname@A=auto_DJUziSYiJo@Snick@A=芙苏不语丶怪力乱神@Srg@A=1@Spg@A=1@Srt@A=1416480352@Sbg@A=0@Sweight@A=12800@Sstrength@A=51800@Scps_id@A=0@Sps@A=1@Sver@A=0@Sm_deserve_lev@A=0@S/]
VM176:1 新用户进入信息: [id@=4295897/name@=auto_DJUziSYiJo/nick@=芙苏不语丶怪力乱神/rg@=1/bg@=0/pg@=1/rt@=1416480352/weight@=12800/strength@=51800/cps_id@=0/]
我记录了下抓取弹幕的流程,可以参考下。
地址: 斗鱼弹幕抓取
代码:github.com/ndrlslz/Douy 既然没人发nodejs版,那我就发一个吧
var net = require('net');
var uuid = require('node-uuid');
var md5 = require('md5');
var request = require('request');

var HOST = 'danmu.douyutv.com';
var PORT = 8602;

function send(socket, payload)
{	
	var data = new Buffer(4 + 4 + 4 + payload.length + 1)
	data.writeInt32LE(4 + 4 + payload.length + 1, 0); //length
	data.writeInt32LE(4 + 4 + payload.length + 1, 4); //code
	data.writeInt32LE(0x000002b1, 8); //magic
	data.write(payload, 12); //payload
	data.writeInt8(0, 4 + 4 + 4 + payload.length); //end of string
	socket.write(data)
}

function login(socket, roomid, user, password)
{
	var req = 'type@=loginreq/username@=' + user + '/password@=' + password + '/roomid@=' + roomid;
	send(socket, req);
}

function getGroupServer(roomid, callback)
{
	request({uri:'http://www.douyutv.com/' + roomid}, function(err, resp, body) {
		var server_config = JSON.parse(body.match(/room_args = (.*?)\}\;/g)[0].replace('room_args = ', '').replace(';', ''));
		server_config = JSON.parse(unescape(server_config['server_config']));
		callback(server_config[0].ip, server_config[0].port);
	});
}

function getGroupId(roomid, callback)
{
	var rt = new Date().now;
	var devid = uuid.v4().replace(/-/g, '');
	var vk = md5(rt + '7oE9nPEG9xXV69phU31FYCLUagKeYtsF' + devid)
	var req = 'type@=loginreq/username@=/password@=/roomid@=' + 
		roomid + '/ct@=0/vk@=' + vk + '/devid@=' + 
		devid + '/rt@=' + rt + '/ver=@20150929/';
	
	getGroupServer(roomid, function(server, port) {
		console.log('group server: ' + server + ':' + port);
		var socket = net.connect(port, server, function() {
			send(socket, req);
		});
		
		socket.on('data', function(data) {
			if (data.indexOf('type@=setmsggroup') >= 0) {
				var gid = data.toString().match(/gid@=(.*?)\//g)[0].replace('gid@=', '');
				gid = gid.substring(0, gid.length - 1);
				socket.destroy();
				callback(gid);
			}
		});	
	});
}

function monitorRoom(roomid)
{
	var socket = net.connect(PORT, HOST, function() {
		login(socket, 'visitor1234567', '1234567890123456');
	});
	
	setInterval(function() {
		send(socket, 'type@=keeplive/tick@=70/'); //send keep alive message repeatly
	}, 50000);
	
	socket.on('data', function(data) {
		//data is a Buffer here
		if (data.indexOf('type@=loginres') >= 0) {
			getGroupId(roomid, function(gid) {
				console.log('gid of room[' + roomid +'] is ' + gid)
				send(socket, 'type@=joingroup/rid@=' + roomid + '/gid@=' + gid + '/');
			});
		} else if (data.indexOf('type@=chatmessage') >= 0) {
			var msg = data.toString();
			var snick = msg.match(/snick@=(.*?)\//g)[0].replace('snick@=', '');
			var content = msg.match(/content@=(.*?)\//g)[0].replace('content@=', '');
			
			snick = snick.substring(0, snick.length - 1);
			content = content.substring(0, content.length - 1);
			console.log(snick + ': ' + content);// 弹幕
		} else if (data.indexOf('type@=userenter') >= 0 ||
			data.indexOf('type@=keeplive') >= 0 ||
			data.indexOf('type@=dgn/gfid@=131') >= 0 ||
			data.indexOf('type@=blackres') >= 0 ||
			data.indexOf('type@=dgn/gfid@=129') >= 0 || 
			data.indexOf('type@=upgrade') >= 0 ||
			data.indexOf('type@=ranklist') >= 0 ||
			data.indexOf('type@=onlinegift') >= 0) {
			//没用的消息
		} else if (data.indexOf('type@=spbc') >= 0) {
			var drid = data.toString().match(/drid@=(.*?)\//g)[0].replace('drid@=', '');
			drid = drid.substring(0, drid.length - 1);
			
			console.log('rocket! room id:' + drid);
		} else {
			console.log(data.toString());	//在这里显示其它类型的消息
		}
	});
}

monitorRoom('<这里填roomid>');
以前我们是做视频聊天站的。2014年8月-9月我们一个月的时间抄了一个斗鱼,没错是整个站。开始的时候由于内容上的高度重合,比如蜡笔小新、RM等,房间也没有人气,我将斗鱼某房间的弹幕扒下来转播到对应内容的房间中,并过滤掉一些关键字,足以以假乱真。虽然平台最终半死不活,且『技术本身并不可耻』,然而还是匿了。

技术核心我看大家都没答到一点,就是反编译flash播放器,因为所有与socket相关的实现都在flash里,反编译出源码后的逻辑、协议,一目了然,然后用python实现出来,很简单,不上码了。

这问题应该是纯技术交流吧,我这么想。 斗鱼已经开放第三方接口了,获取弹幕的流程变得简单了许多:
《斗鱼弹幕服务器第三方接入协议v1.4.1》发布信息 斗鱼开发者论坛
以下为大概流程
第三方接入弹幕服务器:
IP 地址:openbarrage.douyutv.com 端口:8601
1.客户端向服务器端发送登录请求,格式为;
type@=loginreq/roomid@=****/

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
JavaScript浏览器渲染机制与前端性能优化实践
JavaScript浏览器渲染机制与前端性能优化实践

本专题围绕 JavaScript 在浏览器中的执行与渲染机制展开,系统讲解 DOM 构建、CSSOM 解析、重排与重绘原理,以及关键渲染路径优化方法。内容涵盖事件循环机制、异步任务调度、资源加载优化、代码拆分与懒加载等性能优化策略。通过真实前端项目案例,帮助开发者理解浏览器底层工作原理,并掌握提升网页加载速度与交互体验的实用技巧。

1

2026.03.06

Rust内存安全机制与所有权模型深度实践
Rust内存安全机制与所有权模型深度实践

本专题围绕 Rust 语言核心特性展开,深入讲解所有权机制、借用规则、生命周期管理以及智能指针等关键概念。通过系统级开发案例,分析内存安全保障原理与零成本抽象优势,并结合并发场景讲解 Send 与 Sync 特性实现机制。帮助开发者真正理解 Rust 的设计哲学,掌握在高性能与安全性并重场景中的工程实践能力。

21

2026.03.05

PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

106

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

50

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

87

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

27

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

79

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

61

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

50

2026.02.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号