以前看视频的时候,直接找到 video标签,查看视频地址,然后下载下来。。

后来发现,好多 video 标签打开元素审查,如下:

blob开始的东西,下载不了啦。。。

其实我们打开 network 还是能看见,加载了一堆的 .ts 文件。其实.ts文件就是被切成一段一段的视频。 理论上,把这些文件都下载下来,再合并,就完成了,,,

利用nodejs,request包 定时爬去 网站视频ts接口,大概有1771个文件。

首先,获取到网站的ts视频分段配置文件,获取到后,放入本地文件,方便下次使用。

然后,定时调用下载函数,进行下载,

爬去过程中会有下载失败的,所有我在爬去完毕后,检查下载失败的,再次进行下载,

exec包执行cmd命令 进行合成一个ts文件

最后,理论一句话,代码上千行...

一、问题

1、ts文件到底有多少和,地址从哪来。。。

答案: ts 相关的信息,都存在一个叫 m3u8 的文件。 如果仔细点观察 network 是可以找到这个文件的请求的。该文件内容大致如下:

  

这个文件,很显然,存了每个 ts 的文件名称,当然也有存完整的地址的。。只需要提取出里面的ts文件名称,再加上目标网站的域名,就可以下载了。。

我这里是手动的把 m3u8 下载到了本地,当然也可以自己写脚本来下载m3u8文件

解析代码如下:

const fs  = require("fs");var source = fs.readFileSync("./test.m3u8","utf-8"); //读取 m3u8var arr  = source.split("\n");arr = arr.filter((item)=>{return item.match(/\.ts$/);});

2、使用什么技术来合并这些 ts

这里我尝试了两种办法

第一种: 使用node js 直接读取文件流,合并到一个文件。。。最后结果,合并确实成功了,也能播放,但是有卡顿现象,应该是视频帧被破坏了。

第二种: 使用一款强大的工具, ffmpeg 来合并,成功了。具体 ffmpeg 安装看这里 :https://www.cnblogs.com/xswl/p/10042195.html

其中  ffmpeg 的视频合成指令,我找到到了三类:

ffmpeg -i "concat:1.ts|2.ts" -acodec copy out.mp3

ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

前两类,都是要文件名称拼接到 指令里面,,考虑到 cmd 指令的长度有限制,所以并未采用。

采用了如下文件输入办法:

ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4

其中 input.txt 是一个输入配置文件,内容为需要合并的文件名称,如下:

ffconcat version 1.0

file  0.ts

file  1.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("\n") , 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 "concat:1.ts|2.ts" -acodec copy out.mp3//ffmpeg -i "concat:1.ts|2.ts" -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4// ffmpeg -i input.txt -acodec copy -vcodec copy -absf aac_adtstoasc output.mp4
/*
ffconcat version 1.0
file  0.ts
file  1.ts
*//*//文件移动function moveFile(oldPath,newPath){try {fs.renameSync(oldPath, newPath);}catch (e) {console.log("报错后强制移动",e);fs.renameSync(oldPath, newPath);}}*/

线程下载

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 headers = opt.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'}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() {var task = []var taskCount = 10  // 线程下载var flag = falsefor (let i = 0; i < taskCount; i++) {var u = arr.shift()if (!u) {flag = truebreak}var url = host + uconsole.log('progress---:', url)task.push(downPromise(url, headers))}Promise.all(task).then(function() {if (flag) {downOver()} else {load()}})}// 下载完成function downOver() { console.log('下载完成--开始生成配置')localPath.unshift('ffconcat version 1.0')try {fs.writeFileSync(path.join(tsFile, './input.txt'), localPath.join('\n'), 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 downPromise(url, headers) {return new Promise(function(resolve, reject) {var p = url.split('?')[0]var nm = path.parse(p)var nme = nm['name'] + nm['ext']var rpath = path.join(tsFile, nme)localPath.push(`file ${nme}`) // 缓存本地路径,用来合成request({url,headers}, function(err, response, body) {if (!err && response.statusCode == 200) {resolve(true)} else {console.log('错误', err)reject(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)}
}

然后再新建 main.js

const fs  = require("fs");
const down = require("./down");
var host = 'https://xxxx/'; //目标网站
var outputName = "output.mp4";var source = fs.readFileSync("./test.m3u8","utf-8"); //读取 m3u8
var arr  = source.split("\n");
arr = arr.filter((item)=>{return item.match(/\.ts$/);
});down({arr,host,name:outputName
})// host:https://youku.com-qq.net/20190502/181_7ffa42fa/1000k/hls/
// m3u8:https://youku.com-qq.net/20190502/181_7ffa42fa/1000k/hls/index.m3u8

里面使用到了 fs-extra 模块,所以先安装

npm i fs-extra

最后执行:

node main.js

利用 nodejs 解析 m3u8 格式文件,并下 ts 合并为 mp4相关推荐

  1. vba ado返回集合_利用ADO,实现同一文件夹下多个EXCEL工作表的数据汇总

    大家好,今天继续讲解<VBA数据库解决方案>,今日讲解的是第37讲,利用ADO,实现同一文件夹下多个EXCEL工作表的数据汇总.最近的内容实用性比较强,如今日的内容,只把需要汇总的EXCE ...

  2. Python爬取m3u8格式视频并解密ts文件合并转为mp4格式

    一. m3u8是什么格式 m3u8是苹果公司推出的视频播放标准,是m3u的一种,只是编码格式采用的是UTF-8. m3u8准确来说是一种索引文件,使用m3u8文件实际上是通过它来解析对应的放在服务器上 ...

  3. 利用cJSON解析JSON格式

    目录 一.JSON格式 二.cJSON下载 三.cJSON常用函数接口 四.cJSON解析JSON案例 1.一层键值 2.多层键值(两次为例) 3.json数组解析 五.JSON添加数据 (与链表类似 ...

  4. nodejs读取xlsx格式文件

    nodejs读取xlsx格式文件 安装 npm i node-xlsx -D // 或者 yarn add node-xlsx -D 使用 读取表格数据,并生成json对象 /*** @author ...

  5. 海康视频回放,rtsp视频接口转换成.m3u8格式文件

    通过海康接口返回的rtsp视频接口,转换成.m3u8格式文件,逻辑如下 1.采用ffmpeg实时转化rtsp链接视频,转化为m3u8,存放服务器固定地址 2.采用nginx代理视频出.m3u8视频链接 ...

  6. python多线程下载m3u8文件,python 实现多线程下载m3u8格式视频并使用fmmpeg合并

    如何把m3u8格式转换成mp4格式? 可以按照如下方式进行操作: 抑郁的人在水底,正常人在水面,小编沉浮在中间,上不去也下不来. 手机上面找到m3u8格式文件的存储位置,在打开方式里边选择" ...

  7. Golang解析yaml格式文件

    关注公众号 风色年代(itfantasycc) 300G微服务资料等你拿! 作者:会飞的鲶鱼 链接:Golang解析yaml格式文件 - 简书 來源:简书 简书著作权归作者所有,任何形式的转载都请联系 ...

  8. Windows 下 C++ 利用 OpenCV glob 函数获取文件夹下所有文件绝对路径

    绪论 本文记录 Windows 下 C++ 利用 OpenCv glob 函数得到 文件夹下所有文件的绝对路径(含文件名).本文还含有 std::string::find()等函数的记录.如果是 Py ...

  9. 利用matlab将.mat格式文件转换成wav文件

    利用matlab将.mat格式文件转换成wav文件 clc; clear all; clear all;filenames = dir('f16.mat') n = numel(filenames)f ...

  10. 第8.1.2解析mht格式文件

    有人建议改造一下mhtifier.py就可以解析mht格式文件,它的示例没有告诉我,怎么验证.而且我改造也失败了,可能是我的水平有限. Python :解析 word 文档(前程无忧简历),这篇文章的 ...

最新文章

  1. 微软麻将AI Suphx或引入“凤凰房”,与其他AI对打
  2. 垃圾邮件过滤 php,垃圾邮件过滤功能
  3. 详细了解BGP—边界网关协议
  4. 数据结构函与算法之函数与递归
  5. Vsphere初试——基本安装
  6. MYSQL小函数大用途之-------FIND_IN_SET
  7. 「leetcode」17.电话号码的字母组合【回溯算法】详解!
  8. 01-bilibilidemo配置
  9. 在Django框架下向MongoDB数据库中导入.scv文件数据
  10. 《查理·芒格:你是一条狗-雾满拦江》
  11. 英雄联盟商城登录界面
  12. HEAD detached at ---
  13. a标签中的href=javascript
  14. vue中中的组件通信
  15. hbuilderx 使用总结
  16. 关于python全局性解释锁(GIL)
  17. 机顶盒联机调试的方法
  18. win10上运行emwin
  19. js实现点击一键复制文本
  20. 何为数字化工厂?如何打造真正的数字化工厂?

热门文章

  1. ZT[记那对住在我隔壁储藏室的大学刚毕业的小夫妻]
  2. java map 修改键值对_MAP键值对
  3. iOS:练习题中如何用技术去实现一个连线题
  4. zend studio php 错误提示,Zend Studio错误总结,zendstudio总结_PHP教程
  5. R语言报错:Error in scan
  6. 基于深度学习的SLAM综述:迈向空间机器智能时代
  7. oppor829t如何刷机_OPPO R829T卡刷刷机图文教程
  8. Redis 菜鸟教程学习笔记- Redis 配置
  9. MTk2503,使用移动物联网卡上线慢问题
  10. 11.收货地址模块-新增收货地址①