网上搜索各种平台的直播源地址都是满天飞,但是经常会有失效的时候,因为官方也会定期的升级系统修改各种参数或链接让直播源不能永久,所以敝人一直崇尚的是授人以鱼不如授人以渔
,与其给直播源别人,不如教大家如何去爬取直播源,就算失效了也不怕。
0. 前言
继虎牙直播后,网上说斗鱼的直播源是最难抓的,哦?
在抓取之前,需要了解视频直播源的分类和区别,可以自行了解hls,flv,m3u8等知识。
Tips: 本教程只是教大家如何利用前端调试技巧和爬虫基本操作,不作为商业用途,各位童鞋耗子尾汁。
所用工具:
- 浏览器
- Postman
- WebStorm
- VLC media player
所用技术:
- 前端
- PHP
1. 直播源查找
先打开斗鱼的一个直播间,当然先看电影频道啦:
在页面的请求中搜一下.flv
、.m3u8
竟然找不到:
那么地址显然不是直接从后台带来的,那么就是ajax请求来的,从ajax请求中找到了一个请求:
很显然,这里有个地址,使用VLC播放一下,额。。。不好意思,刚好电影放完。但至少这个地址是可用的。
2. 浏览器请求过程分析及思路
Request URL: https://m.douyu.com/api/room/ratestream
Form Data:
- v: 250120210110
- did: 5533423942ce86e564901f2200001631
- tt: 1610256007
- sign: 00121038e82b65413972da17c6fdaa1d
- ver: 22011191
- rid: 3637778
- rate: -1
多打开几个直播间,可以发现有几个参数是固定的:did
、ver
、rid
是直播间id、v
是"2501"加当前日期、tt
是当前时间戳、sign
是签名、rate
是清晰度(-1是默认的)。
那么现在就只有sign是比较麻烦的,它和当前时间戳是相关联的,后面解析可以看到。
找到发ajax
请求的位置,在请求参数data那一行打一个断点,然后再刷新页面或者切换清晰度即可开始调试:
选择window[(0, s.default)(256042, "9f4f419501570ad13334")]
然后右键Evaluate selected text in console
,在控制台可以看到一个函数ub98484234
,经测试这个函数名是固定的,但是函数体里的内容不是固定的
,所以复制出函数没有用。然后r,o,c
分别是直播间ID、did、时间戳,后面"&ver=" + u.VERSION + "&rid=" + r + "&rate=" + e + (f ? "&aid=" + f : "")
都是固定的。
断点进到这个函数里面去看,可以看到这个函数是在页面请求回来的,而每个直播间的这个函数体都是不同的,里面还有个变量需要保存,而这个变量名也不是固定的,但是仔细一看,这个变量和这个函数的三个参数形参名是有关系的,仔细看下(是不是很像):
搜一下,这个变量就在这个函数的下面,是个数组:
思路就有了:
- get请求直播间,获取
ub98484234
函数和变量代码 - 运行这个函数获取
ratestream
请求的参数 - 发送post请求获取直播源地址
3. 模拟实现
服务器代码(PHP版):
room.php
<?php
// 加以下代码是为了防止跨域不能访问
header('Content-Type: text/html;charset=utf-8');
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin');
$page = file_get_contents('https://m.douyu.com/'.$_GET['rid']);
$patt1 = '/function ub98484234([\w\W]*?)function k927cea2d4369/';
preg_match_all($patt1, $page, $rs);
echo str_replace("function k927cea2d4369","",$rs[0][0]);
url.php
<?php
// 加以下代码是为了防止跨域不能访问
header('Content-Type: text/html;charset=utf-8');
header('Access-Control-Allow-Origin:*');
header('Access-Control-Allow-Methods:POST,GET,OPTIONS,DELETE');
header('Access-Control-Allow-Credentials: true');
header('Access-Control-Allow-Headers: Content-Type,Content-Length,Accept-Encoding,X-Requested-with, Origin');
$post = array(
"v" => $_POST['v'],
"did" => 'b9f39b4e631b7973c103209800001631',
"tt" => $_POST['tt'],
"sign" => $_POST['sign'],
"ver" => 22011191,
"rid" => $_POST['rid'],
"rate" => -1
);
$data = curl_post('https://m.douyu.com/api/room/ratestream',$post);
echojson($data);
function curl_post($url,$post_data){
$ch = curl_init();
$headers = array(
'Accept' => '*/*',
'User-Agent' => 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1 Edg/87.0.4280.88',
'Accept-Language' => 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Content-Type' => 'application/x-www-form-urlencoded'
);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_IPRESOLVE, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($post_data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, FALSE);
$output = curl_exec($ch);
curl_close($ch);
return $output;
}
function getParam($key, $default=''){
return trim($key && is_string($key) ? (isset($_POST[$key]) ? $_POST[$key] : (isset($_GET[$key]) ? $_GET[$key] : $default)) : $default);
}
function echojson($data){
header('Content-type: application/json');
$callback = getParam('callback');
if($callback){
die(htmlspecialchars($callback).'('.$data.')');
} else {
die($data);
}
}
前端html
<script src="node_modules/crypto-js/crypto-js.js"></script>
<script src="node_modules/jquery/dist/jquery.js"></script>
<script>
let rid = 3637778; // 房间号
let did = "b9f39b4e631b7973c103209800001631";
let tt = parseInt((new Date).getTime() / 1e3, 10);
let rate = -1;
$.ajax({
type: 'get',
url: 'https://xxx.com/player/douyu/room.php',
data: {rid:rid},
success: function(res){
eval(res);
let param = ub98484234(rid, did, tt) + "&ver=22011191&rid=" + rid + "&rate=" + rate ;
$.ajax({
type: 'post',
url: 'https://xxx.com/player/douyu/url.php',
data: param,
success: function(res){
console.log(res);
}
});
}
});
</script>
php
文件放到自己的服务器,然后前端就可以请求到直播源地址:
4. 整合播放器
这里使用的是ckpayer
,官网:https://www.ckplayer.com/
<script type="text/javascript" src="ckplayer/ckplayer/ckplayer.js" charset="utf-8" data-name="ckplayer"></script>
<div class="video" style="width: 600px;height: 400px;"></div>
<script type="text/javascript">
//定义一个变量:videoObject,用来做为视频初始化配置
var videoObject = {
container: '.video', //“#”代表容器的ID,“.”或“”代表容器的class
variable: 'player', //播放函数名称,该属性必需设置,值等于下面的new ckplayer()的对象
html5m3u8: true,
video: 'http://hlstct.douyucdn2.cn/dyliveflv1a/3637778raLSXdOdu_2000.m3u8?txSecret=0a8093b41cbff59ba0a56bc4ac39ebce&txTime=5ffacfc5&token=h5-douyu-0-3637778-8fe14247bbe00e18529094c5dcf70532&did=b9f39b4e631b7973c103209800001631&origin=tct&vhost=play2',
playbackRate: false
};
var player = new ckplayer(videoObject);//初始化播放器
</script>
秋名山车神来了!
至于整合就很简单,在ajax获取真实地址后,把url设置到ckplayer就好啦!
5. 【附】真实源地址
这里再提一句,真实无加密源地址的链接其实很短,如下:
http://tx2play1.douyucdn.cn/live/空白替换.flv?uuid=
空白部分就是加密地址的一部分,如加密地址是:http://hlstct.douyucdn2.cn/dyliveflv1a/3637778raLSXdOdu_2000.m3u8?txSecret=bc08048624cbe33fe6492c981a21f415&txTime=5ffadd7c&token=h5-douyu-0-3637778-f9f6e2e79ab5b1ba10762fe2412c8c6b&did=b9f39b4e631b7973c103209800001631&origin=tct&vhost=play2
,那么空白部分就是3637778raLSXdOdu
,结合起来就是:
http://tx2play1.douyucdn.cn/live/3637778raLSXdOdu.flv?uuid=
Tips: 直播间关播后需要重新获取,关闭播放后也需要(否则会报错时间校验出错)。而真实地址是不需要,但是官方也可能修改。
6. 总结
找到直播源很简单,关键是调试并实现它,不要怕麻烦,本人能力有限,斗鱼直播源花了三个晚上吧,各位如有更好的方法请留言讨论,多多指教。