携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情
视频存储
当前各大视频厂商都在升级视频存储架构,对于多数开发者来说,爬视频不像以前那么容易Get了。视频常规的存储方式有两种:
云端加密
阿里云和腾讯云都提供了对应的加密模式,甚至还提供了管道流的处理任务。你可以将录制好的视频上传到云端,云端可以实现:清晰,压缩,切割为更易保存的ts文件,加密等服务。
以阿里云加密为例,此类加密最难破解,每个文件都有独立的加密钥匙,通过子账号+播放凭证管理权限,并且它的key存储在内存中。
m3u8
通过此种方式可以存储ts片段,分段下载,每个ts都拥有同一个解密key。此类加密方式破解相对容易一些,曲折点在于寻找m3u8
的地址,然后通过视频处理工具ffmpeg
下载并合并即可。
视频商转码技术
类似爱奇艺,优酷这些视频商,为了稳住饭碗,一般会通过转码方式加密。设计特定的格式和密钥技术,阻止破解。并且只有通过特定的客户端才能解密和解码视频。 非重点,不多说。
视频破解
我们以常见的m3u8的加密为例,刨析其核心技术点。通过观察m3u8文件,我们可以看到它存储了每个ts的文件名称,通过目标地址拼接就可以下载。
有很多直接解析m3u8的在线网站,甚至你在夜晚看的Video也是这样的地址。
你以为如此简单就太小看视频的攻防战了,多数情况下你看到的是这样的文件内容
这是因为他们对ts文件做了加密,需要在m3u8和JS代码中找到解密的key和方法。具体的需要根据每个站点不同来寻找。有兴趣可以看我之前基于python开发的Lagou爬虫库,不过目前他们也升级了加密算法。
解析m3u8
const fs = require("fs");
var source = fs.readFileSync("aslkdas.m3u8","utf-8"); //读取 m3u8
var arr = source.split("
");
arr = arr.filter((item)=>{
return item.match(/.ts$/);
});
down.js
const request = require("request");
const fs = require("fs");
const path = require("path");
const child_process = require('child_process');
const fsextra = require('fs-extra');
module.exports = function(opt){
opt = opt || {};
var arr = opt.arr || []; //所有 ts的文件名或者地址
var host = opt.host || ""; //下载 ts 的 域名,如果 arr 里面的元素已经包含,可以不传
var outputName = opt.name || `output${(new Date()).getTime()}.mp4`; //导出视频的名称
const tsFile = path.join(__dirname,`./source/${arr[0].split(".")[0]}`,);
createDir(tsFile);//递归创建文件
console.log("本次资源临时文件:",tsFile);
const resultDir = path.join(__dirname,"./result");
createDir(resultDir);//递归创建文件
const resultFile = path.join(resultDir,outputName);
var localPath = [] ; //下载到本地的路径
//开始下载ts文件
load();
function load(){
if(arr.length > 0){
var u = arr.shift();
var url = host + u;
console.log("progress---:",url);
down(url);
}else{
//下载完成
console.log("下载完成--开始生成配置");
localPath.unshift("ffconcat version 1.0");
try{
fs.writeFileSync(path.join(tsFile,"./input.txt"), localPath.join("
") , undefined, 'utf-8')
}catch(e){
console.log("写入配置出错--",e);
return ;
}
//开始依赖配置合成
console.log("开始合成-----");
child_process.exec(`cd ${tsFile} && ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc ${resultFile}`,function(error, stdout, stderr){
if(error){
console.error("合成失败---",error);
}else{
console.log("合成成功--",stdout);
//删除临时文件
fsextra.remove(tsFile, err => {
if (err) return console.error("删除文件是失败",err)
console.log('删除文件成功!')
});
}
});
}
}
//下载 ts 文件
function down(url){
var p = url.split("?")[0];
var nm = path.parse(p);
var nme = nm["name"] + nm["ext"];
rpath = path.join(tsFile,nme);
localPath.push(`file ${nme}`); //缓存本地路径,用来合成
request({
url:url,
headers:{
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36',
'X-Requested-With': 'XMLHttpRequest'
}
},function (err, response, body) {
if (!err && response.statusCode == 200) {
load();
}else{
console.log("错误",err)
}
}).pipe(fs.createWriteStream(rpath));
}
//递归的创建文件夹
function mkdirs(dirpath) {
if (!fs.existsSync(path.dirname(dirpath))) {
mkdirs(path.dirname(dirpath));
}
fs.mkdirSync(dirpath);
}
function createDir(myPath){
fs.existsSync(myPath) == false && mkdirs(myPath);
}
}
// ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4
最后我们调用即可。
总结
本节课有些含蓄,毕竟学习为主,很多地方只能看破不说破,毕竟各位小可爱都是搞研发的。只要知其一便能破其百,对于研发来说语言只是一个工具而已。
下节课,我会开发一个基于js的爬虫脚手架,便于各位同学学习和研究爬虫原理。
如果喜欢我的文章,麻烦点个赞,评个论,收个藏,关个注。
手绘图,手打字,纯原创,摘自未发布的书籍:《高阶前端指北》,转载请获得本人同意。