当前位置: 首页 > news >正文

【JavaScript爬虫记录】记录一下使用JavaScript爬取m4s流视频过程(内含ffmpeg合并)

前言

前段时间发现了一个很喜欢的视频,可惜网站不让下载,简单看了一下视频是被切片成m4s格式的流文件,初步想法是将所有的流文件下载下来然后使用ffmpeg合并成一个完整的mp4,于是写了一段脚本来实现一下,电脑没有配python环境,所以使用JavaScript实现,合并功能需要安装ffmpeg,没有的小伙伴自行安装哦

前置知识
  1. m4s文件(复制百度)

    M4S 文件是使用 MPEG-DASH 流技术通过 Internet 流式传输的一小段视频。它包含二进制数据形式的视频片段。接收应用程序(通常是网络浏览器或媒体播放器)按接收顺序播放这些片段。第一个 M4S 段由它包含的初始化数据标识。在 summary 中,m4s文件是完整文件的单个小媒体片段。 M4S 文件基于 ISO 基础媒体文件 (ISOBMFF) 格式。大文件的这些小片段可以通过 HTTP 独立下载。因此,如果您有一个大的 MP4 电影文件,则可以使用 MPEG-DASHHTTP 上的动态自适应流式传输)技术将其分段为 M4S 分段文件,从而对其进行流式传输。如果将此大型电影文件作为 M4S 下载到光盘,则会下载多个 M4S 文件。如果将所有这些 .m4s 段连接起来,就会生成一个完整的可播放文件。除非文件的第一个初始化段也可用,否则媒体播放器无法播放文件。

思路整理
  1. 找到目标m4s文件的接口,观察接口规律,拼接URL批量下载
  2. 然后将文件写入本地,再遍历目录生成ffmpeg合并用的文化列表目录
  3. 然后调用ffmpeg终端命令合并
  4. 最后清理临时文件

开始实现

首先观察到目标m4s文件的url格式都是https://xxxxxx/1080.mp4/seg-1-v1-a1.m4s / https://xxxxxx/1080.mp4/seg-2-v1-a1.m4s等等,猜测只是通过目标的序号来管理分片,那考虑使用循环来批量下载,先写几个函数来处理基本的功能,例如下载文件 / 生成临时目录 / 本地写入 / 清理临时文件等

请求函数

const fetchData = async (url) => {try {let response = await fetch(url, {method: 'GET',headers: {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36 Edg/117.0.2045.60"}});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}let m4sData = await response.blob();if (m4sData instanceof Blob) {console.log(m4sData.size); // 打印 Blob 对象的大小} else {console.log("m4sData.data 不是一个 Blob 对象");}return m4sData;} catch (error) {console.log("get_m4sData下载失败");console.log(error);}
};

本地写入的函数

const writeFile = async (fileName, file) => {fs.writeFile(fileName, file, (err) => {if (err) {console.log("写入失败:", err);return}console.log(`${fileName}写入成功`);})
}

生成临时目录的函数

const generateFileList = () => {// 获取 assets 目录下所有目标文件const files = fs.readdirSync(folderPath).filter(file => file.endsWith('.ts')).sort((a, b) => {// 提取文件名中的数字部分进行比较const numA = parseInt(a.match(/seg-(\d+)-v1-a1\.ts/)[1], 10);const numB = parseInt(b.match(/seg-(\d+)-v1-a1\.ts/)[1], 10);return numA - numB;});// 生成文件列表内容(使用 Unix 路径分隔符)const listContent = files.map(file => `file '${path.join(file).replace(/\\/g, '/')}'`).join('\n');// 写入文件列表const listPath = path.join(folderPath, 'list.txt');fs.writeFileSync(listPath, listContent);console.log('文件列表已生成:', listPath);return listPath;
};

合并视频的函数

const mergeSegments = () => {const listPath = path.join(folderPath, 'list.txt').replace(/\\/g, '/');const outputFile = './mergeVideo/merged_video.mp4';console.log(listPath);// 检查文件列表是否存在if (!fs.existsSync(listPath)) {console.error('错误:文件列表未生成');process.exit(1);}execSync(`ffmpeg -f concat -safe 0 -i "${listPath}" -c copy "${outputFile}"`,{ stdio: 'inherit' });console.log('合并完成:', outputFile);};

移除临时文件和善后优化的函数

// 删除 assets 目录下的所有文件
const deleteAllFilesInAssets = () => {const folderPath = path.join('./assets');const files = fs.readdirSync(folderPath);files.forEach(file => {const filePath = path.join(folderPath, file);fs.unlinkSync(filePath);});console.log('assets 目录下的所有文件已删除');
};// 随机改名 
const renameMergedVideo = () => {const oldPath = path.join('./mergeVideo', 'merged_video.mp4');const videoFileName = generateRandomString()const newPath = path.join('./mergeVideo', `video_${videoFileName}.mp4`);if (fs.existsSync(oldPath)) {fs.renameSync(oldPath, newPath);console.log(`文件已重命名为video_${videoFileName}.mp4 `);} else {console.log('文件 merged_video.mp4 不存在');}
};
// 生成一个随机的8位数字加大小写字母的字符串
const generateRandomString = () => {const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';let results = '';const length = 8;for (let i = 0; i < length; i++) {const randomIndex = Math.floor(Math.random() * characters.length);results += characters.charAt(randomIndex);}return results;
};

接下来就可以编写我们的main函数了,只需要挨个调用上面的辅助函数即可

export const main = async (Number_of_data_segments,BASE_URL,rootPath = folderPath) => {for (let i = 1; i <= Number_of_data_segments; i++) {let url = `seg-${i}-v1-a1.ts`console.log(`正在下载第${i}个数据段,标识为${url}`);let m4s = await fetchData(BASE_URL + url)console.log(`${i}个数据段下载完成`);await writeFile(`${rootPath}/${url}`, Buffer.from(await m4s.arrayBuffer()))}console.log(`下载完成`);console.log(`生成目录映射`);generateFileList();console.log(`合并数据段`);mergeSegments();deleteAllFilesInAssets()renameMergedVideo()
}

总结

最后试了一下,效果还是蛮不错的,这些都是最终合成的视频
在这里插入图片描述
这只是个简单的脚本,很多地方都可以优化,例如可以通过网络状态来判断分片数量,就不再需要手动去查看分片数量了,这些地方有兴趣的小伙伴可以自行尝试

相关文章:

【JavaScript爬虫记录】记录一下使用JavaScript爬取m4s流视频过程(内含ffmpeg合并)

前言 前段时间发现了一个很喜欢的视频,可惜网站不让下载,简单看了一下视频是被切片成m4s格式的流文件,初步想法是将所有的流文件下载下来然后使用ffmpeg合并成一个完整的mp4,于是写了一段脚本来实现一下,电脑没有配python环境,所以使用JavaScript实现,合并功能需要安装ffmpeg,…...

CSDN2024年度总结|乾坤未定你我皆是黑马|2025一起为了梦想奋斗加油少年!!!

CSDN2024年我的创作纪念日1024天|不忘初心|努力上进|积极向前 一、前言&#xff1a;二、2024个人成长经历&#xff1a;HarmonyOS鸿蒙应用生态构建与扩展——杭州站AGI创新工坊&神经网络大模型——杭州站 三、2024年度创作总结&#xff1a;2024创作数据总结&#xff1a;博客…...

【前端】 react项目使用bootstrap、useRef和useState之间的区别和应用

一、场景描述 我想写一个轮播图的程序&#xff0c;只是把bootstrap里面的轮播图拉过来就用上感觉不是很合适&#xff0c;然后我就想自己写自动轮播&#xff0c;因此&#xff0c;这篇文章里面只是自动轮播的部分&#xff0c;没有按键跟自动轮播的衔接部分。 Ps: 本文用的是函数…...

联想电脑如何进入BIOS?

打开设置 下滑找到更新与安全 点击恢复和立即重新启动 选择疑难解答 选择UEFI固件设置 然后如果有重启点击重启 重启开机时一直点击FNF10进入BIOS界面...

蓝桥杯单片机大模板(西风)

#include <REGX52.H> #include "Key.h" #include "Seg.h" //变量声明区 unsigned char Key_Val,Key_Down,Key_Old;//按键扫描专用变量 unsigned char Key_Slow_Down;//按键减速专用变量 10ms unsigned int Seg_Slow_Down;//按键扫描专用变量 500ms …...

20250213刷机飞凌的OK3588-C_Linux5.10.209+Qt5.15.10_用户资料_R1

20250213刷机飞凌的OK3588-C_Linux5.10.209Qt5.15.10_用户资料_R1 2025/2/13 15:10 缘起&#xff1a;OK3588-C_Linux5.10.66Qt5.15.2的R5都出来了。但是公司一直在R4上面开发的&#xff0c;不想动了。 不过我的原则&#xff0c;只要是有新的系统SDK/BSP放出来&#xff0c;都先在…...

2.13学习记录

web ezSSTI 根据题意&#xff0c;这题考察ssti漏洞&#xff0c;查询有关信息得知这是一种模版攻击漏洞。这种题目可以利用工具进行解决&#xff0c;用焚靖&#xff0c;这是一个针对CTF比赛中Jinja SSTI绕过WAF的全自动脚本 根据教程安装工具和对应的依赖就可以了这个脚本会自…...

【DeepSeek】Deepseek辅组编程-通过卫星轨道计算终端距离、相对速度和多普勒频移

引言 笔者在前面的文章中&#xff0c;介绍了基于卫星轨道参数如何计算终端和卫星的距离&#xff0c;相对速度和多普勒频移。 【一文读懂】卫星轨道的轨道参数&#xff08;六根数&#xff09;和位置速度矢量转换及其在终端距离、相对速度和多普勒频移计算中的应用 Matlab程序 …...

JavaEE架构

一.架构选型 1.VM架构 VM架构通常指的是虚拟机&#xff08;Virtual Machine&#xff09;的架构。虚拟机是一种软件实现的计算机系统&#xff0c;它模拟了物理计算机的功能&#xff0c;允许在单一物理硬件上运行多个操作系统实例。虚拟机架构主要包括以下几个关键组件&#xff…...

Docker 网络的几种常见类型

目录 Docker 网络类型 桥接网络&#xff08;Bridge&#xff09; 通俗解释 特点 使用场景 示例 主机网络&#xff08;Host&#xff09; 通俗解释 特点 使用场景 示例 None 网络 通俗解释 特点 使用场景 示例 Overlay 网络 通俗解释 特点 使用场景 示例 Ma…...

C++ 常用的设计模式

1&#xff1a;单例模式&#xff1a;首先能想到的&#xff0c;最为重要的一个设计模式。确保一个类仅有一个实例&#xff0c;提供一个 全局访问点&#xff0c;惯用做法是屏蔽构造数访问&#xff08;设为private&#xff09;&#xff0c;通过static 权限达到间接访问调用的目的…...

【设计模式】01- 一文理解常用设计模式-“创建型模式”篇

一、前言 最近在复习设计模式&#xff0c;撰写、整理了内容和代码片段&#xff0c;和大家一起交流学习。 设计模式是软件设计中常见问题的典型解决方案。 修改记录 更新内容更新时间第一版 250212 更新了对文章中的模式代码示范的解释250214 二、模式分类 模式可以根据其意图…...

在ArcGIS JS API中使用WebGL实现波纹扩散特效

在现代WebGIS开发中&#xff0c;ArcGIS JS API 是一个非常强大的工具&#xff0c;它允许开发者创建丰富的地理信息应用。结合WebGL技术&#xff0c;我们可以实现更加复杂和炫酷的可视化效果。本文将介绍如何使用ArcGIS JS API结合WebGL实现一个波纹扩散特效。 波纹扩散效果 1 概…...

我用AI做数据分析之四种堆叠聚合模型的比较

我用AI做数据分析之四种堆叠聚合模型的比较 这里AI数据分析不仅仅是指AI生成代码的能力&#xff0c;我想是测试AI数据分析方面的四个能力&#xff0c;理解人类指令的能力、撰写代码的能力、执行代码的能力和解释结果的能力。如果这四个能力都达到了相当的水准&#xff0c;才可…...

《LSTM与HMM:序列建模领域的双雄对决》

在序列建模的广阔领域中&#xff0c;长短期记忆网络&#xff08;LSTM&#xff09;和隐马尔可夫模型&#xff08;HMM&#xff09;都是极为重要的工具&#xff0c;它们各自有着独特的优势和应用场景。下面将对两者在序列建模上的异同进行深入探讨。 相同点 序列数据处理能力&…...

Flutter 的 Widget Key 提议大调整?深入聊一聊 Key 的作用

Flutter 的 Widget Key 提议大调整&#xff1f;深入聊一聊 Key 的作用 在 Flutter 里&#xff0c;Key 对象存在的目的主要是区分和维持 Widget 的状态&#xff0c;它是控件在渲染树里的「复用」标识之一&#xff0c;这一点在之前的《深入 Flutter 和 Compose 在 UI 渲染刷新时…...

只需三步!5分钟本地部署deep seek——MAC环境

MAC本地部署deep seek 第一步:下载Ollama第二步:下载deepseek-r1模型第三步&#xff1a;安装谷歌浏览器插件 第一步:下载Ollama 打开此网址&#xff1a;https://ollama.com/&#xff0c;点击下载即可&#xff0c;如果网络比较慢可使用文末百度网盘链接 注&#xff1a;Ollama是…...

网络工程师 (31)VLAN

前言 VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。 一、定义与特点 定义&#xff1a;VLAN是对连接到的第二层交换机端口的网络用户的逻辑分段&#xff0c;不受网络用户的物理位置…...

浏览器网络请求全流程深度解析

一、核心流程概述 现代浏览器的网络请求过程是一个分层协作的精密系统&#xff0c;涉及应用层协议、传输层协议、操作系统内核及网络基础设施的协同工作。整个过程可抽象为以下关键阶段&#xff1a; 请求构建与初始化DNS解析与寻址TCP连接建立HTTP协议交互响应处理与资源解析…...

React历代主要更新

一、React 16之前更新 React Fiber是16版本之后的一种更新机制&#xff0c;使用链表取代了树&#xff0c;是一种fiber数据结构&#xff0c;其有三个指针&#xff0c;分别指向了父节点、子节点、兄弟节点&#xff0c;当中断的时候会记录下当前的节点&#xff0c;然后继续更新&a…...

多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度​

一、引言&#xff1a;多云环境的技术复杂性本质​​ 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时&#xff0c;​​基础设施的技术债呈现指数级积累​​。网络连接、身份认证、成本管理这三大核心挑战相互嵌套&#xff1a;跨云网络构建数据…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

通过Wrangler CLI在worker中创建数据库和表

官方使用文档&#xff1a;Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后&#xff0c;会在本地和远程创建数据库&#xff1a; npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库&#xff1a; 现在&#xff0c;您的Cloudfla…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

2021-03-15 iview一些问题

1.iview 在使用tree组件时&#xff0c;发现没有set类的方法&#xff0c;只有get&#xff0c;那么要改变tree值&#xff0c;只能遍历treeData&#xff0c;递归修改treeData的checked&#xff0c;发现无法更改&#xff0c;原因在于check模式下&#xff0c;子元素的勾选状态跟父节…...

ElasticSearch搜索引擎之倒排索引及其底层算法

文章目录 一、搜索引擎1、什么是搜索引擎?2、搜索引擎的分类3、常用的搜索引擎4、搜索引擎的特点二、倒排索引1、简介2、为什么倒排索引不用B+树1.创建时间长,文件大。2.其次,树深,IO次数可怕。3.索引可能会失效。4.精准度差。三. 倒排索引四、算法1、Term Index的算法2、 …...

全志A40i android7.1 调试信息打印串口由uart0改为uart3

一&#xff0c;概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本&#xff1a;2014.07&#xff1b; Kernel版本&#xff1a;Linux-3.10&#xff1b; 二&#xff0c;Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01)&#xff0c;并让boo…...