使用node爬取视频网站里《龙珠》m3u8视频

1. 找到视频播放网站

百度一下 龙珠视频播放  精挑细选一个可以播放的网站。
 如:我在网上随便找了一个播放网站,可以直接在线播放   https://www.xxx.com/play/39999-1-7.html

这里不具体写视频地址了,大家可以自行搜索 

2.分析网页DOM结构 找出视频资源地址

可以看到 整块播放内容在 td#playleft 下的 iframe 引入。

验证一下:把 https://xxx/yun/?url=https://XXX/20221016/npV6fcC2/index.m3u8 地址在浏览器内直接访问 发现可以正常播放视频

那这串地址就是我们所需要的视频文件资源路径。那我们接下来就需要想办法根据这个路径把视频保存到本地。

3.批量获取视频播放地址

        虽然通过第二步的操作 我们可以拿到了第一话的视频资源地址,但是是手动完成的。需要想办法能批量的拿到第一部153话的所有资源地址。

        想拿到所有视频的视频资源地址的前提是拿到所有视频的播放地址。所以我们要先想办法拿到每一集的播放地址。

        点击播放第1话 第2话 第3话 ,可以看到 浏览器URL 分别是

        第1话  /play/39999-1-1.html 

        第2话 /play/39999-1-2.html  

        第3话/play/39999-1-3.html 


分析视频网站的地址不难看出 规律, 递增n就可以获取到每一话的在线播放地址

let n = 1
let urlArr = []
while(n < 154){urlArr.push('/play/39999-1-' +n+'.html' ) n++
}
console.log(urlArr )

4.批量获取视频资源地址

        通过第三步我们已经拿到了 每一话的播放地址,那就要想办法拿到 每一个播放地址下的td#playleft 下的 iframe 的 src。

1.第一次尝试

        直接获取 /play/39999-1-1.html 的页面结构,尝试从返回的dom中找到 td#playleft 下的 iframe。但是并没有找到相关的DOM,推测应该是动态添加的 节点,第一次尝试失败

var request = require('request');request(`https://www.xxx.com/play/39999-1-1.html`, function (err, res, body) {console.log(err, res, body);
});

2.第二次尝试

        既然直接拿不到那就等页面加载完成再去拿,所以第二种方案就是 在本地项目中 通过 iframe引入 

https://www.xxx.com/play/39999-1-1.html     等 iframe onload之后再去获取iframe.contentDocument 下的 

<body><iframe id="iframe" src="https://www.xxx.com/dragon/39999-1-1.html" onload="loadPage()" frameborder="0"></iframe>
</body><script>
function loadPage(e){let iframe = document.getElementsByTagName('iframe')[0]var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;console.log(iframeDocument )
}
</script>

但是呢 并没有拿到 ,

虽然拿到了ifram的dom,但是呢 拿不到 contentDocument。

这是为什么呢?

新机呲挖一呲冒黑套呲  真相只有一个

iframe src 的跨域问题,  

方案二失败 

3.第三次尝试

        第三次的尝试是和第二次思路一样的,所以主要任务是解决 iframe的跨域问题,

<iframe id="iframe" src="/dragon/39999-1-1.html" onload="loadPage()" frameborder="0"></iframe>

    代理一下吧

    # 龙珠server {listen       9001;location / {root   E:/dragonBall;index  index.html index.htm;try_files $uri $uri/ @router;}location /dragon {proxy_pass https://www.xxx.com/play;}location /_guard {proxy_pass https://www.xxx.com;}location /template {proxy_pass https://www.xxx.com;}location /static {proxy_pass https://www.xxx.com;}}

至此 终于拿到了 在线播放页面的全部DOM数据

那么简单的处理下数据 就可以拿到每一话的 视频资源地址了

(这里直接循环了,也可以直接使用第3步获取的视频播放地址,逻辑是一致的)

<script>let num = 1let arr = []function loadPage(e){arr = localStorage.getItem('streamUrl')if(arr){arr = JSON.parse(arr)}else{arr = []}if(num > 154) return let iframe = document.getElementsByTagName('iframe')[0]var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;var iframeElement = iframeDocument.getElementById('playleft').getElementsByTagName('iframe')[0];let streamUrl = iframeElement.attributes.src.value.split('?url=')[1]console.log('这是第'+ num +"话:"+streamUrl)arr.push({index:num,url:streamUrl})num ++localStorage.setItem('streamUrl',JSON.stringify(arr))iframe.src = "/dragon/39999-1-"+num+".html"}
</script>

5.根据m3u8的资源地址下载视频

首先封装一个下载视频的函数

function downloadMedia (opt, callback) {// 测试视频,如果链接失效的话就自己找一个let url = opt.url ;let output = opt.output || 'video';let filename = opt.index + '.mp4';let title = opt.title || '测试视频';if (!fs.existsSync(output)) {fs.mkdirSync(output, {recursive: true,});}(async function() {try {console.log("准备下载...");await converter.setInputFile(url).setOutputFile(path.join(output, filename)).start();console.log("下载完成!");if ( typeof callback === 'function' ) callback(opt.index);} catch (error) {console.log(error)throw new Error("哎呀,出错啦! 检查一下参数传对了没喔。", error);}})(); }

然后 再遍历一下我们拿到的视频资源地址 ,轮询调用一下 下载方法 就可以了


let arr = [{"index": 1,"url": "https://xxx/20221016/npV6fcC2/index.m3u8"},...{"index": 153,"url": "https://xxx/20221016/6AaX2hCl/index.m3u8"}
]let callback = function(index){let indexName = arr[index - 1].indexif(indexName.length === 1){indexName = '00' + indexName} if(indexName.length === 2){indexName = '0' + indexName}downloadMedia({url:arr[indexName].url,index:arr[indexName].index},callback)
}downloadMedia({url:arr[0].url,index:'001'},callback)

我现在设置的是一次下载1个文件,也可以修改下同时下载多个,注意别把 视频网站搞崩了。

总结:

        主要问题还是获取到资源地址。处理好资源地址的问题,就可以轮询下载了。

附:

gitee源码

仓库 - wangbanglei (wangbangleilei) - Gitee.com

注:仅供学习使用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/781055.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【面试经典150 | 动态规划】三角形最小路径和

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;动态规划 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容进行…

使用pytorch构建带梯度惩罚的Wasserstein GAN(WGAN-GP)网络模型

本文为此系列的第三篇WGAN-GP&#xff0c;上一篇为DCGAN。文中仍然不会过多详细的讲解之前写过的&#xff0c;只会写WGAN-GP相对于之前版本的改进点&#xff0c;若有不懂的可以重点看第一篇比较详细。 原理 具有梯度惩罚的 Wasserstein GAN (WGAN-GP)可以解决 GAN 的一些稳定性…

十四、Spring源码学习之createBean方法

AbstractAutowireCapableBeanFactory#createBean()方法 protected Object createBean(String beanName, RootBeanDefinition mbd, Nullable Object[] args)throws BeanCreationException {if (logger.isTraceEnabled()) {logger.trace("Creating instance of bean "…

【unity】认识unity Hub的主要功能

这里我们主要讲解unity Hub中的【项目】和【安装】功能&#xff0c;其他对应的功能栏相信大家根据文字就可以知道相应的作用。 首先是介绍【项目】功能&#xff0c;在这里我们可以创建本地项目和云端项目&#xff0c;作为初学者我们创建本地项目皆可&#xff0c;当然如果你是多…

UE4_碰撞_使用蓝图控制物体移动时如何让被阻挡

当我们这样设置蓝图时&#xff1a; 运行效果&#xff1a; 利用蓝图更改一个物体的位置&#xff0c;发现本来两个应该相互阻挡的物体被穿过去了。为了不让相互阻挡的物体被穿过去&#xff0c;我们需要设置好蓝图节点的参数Sweep。 勾选之后 墙的蓝图我们这样设置&#xff1a; 运…

C#-ConcurrentDictionary用于多线程并发字典

ConcurrentDictionary 是 .NET Framework 中用于多线程并发操作的一种线程安全的字典集合类。它提供了一种在多个线程同时访问和修改字典时保持数据一致性的机制。 以下是 ConcurrentDictionary 类的一些重要特性和用法&#xff1a; 线程安全性&#xff1a;ConcurrentDictiona…

【软件工程】需求分析

1. 导言 1.1. 需求文档的目的 该文档是关于用户对于“学生成绩管理系统”的功能和性能的要求&#xff0c;重点描述了“学生成绩管理系统”的设计需求&#xff0c;将作为对该工具在概要设计阶段的设计输入。编写本文档的目的在于说明软件工程管理系统的业务需求内容&#xff0…

30-3 越权漏洞 - 水平越权(横向越权)

环境准备:构建完善的安全渗透测试环境:推荐工具、资源和下载链接_渗透测试靶机下载-CSDN博客 一、定义 攻击者可以访问和操作与其拥有同级权限的用户资源。 示例: 学生A在教务系统上正常只能修改自己的作业内容,但由于不合理的权限校验规则等原因,学生A可以修改学生B的内…

点云从入门到精通技术详解100篇-基于3D点云的盘类元件识别与定位

目录 前言 2 3D视觉机器人抓取系统方案设计 2.1 系统硬件方案选型 2.1.1 相机选取方案

记录C++中,vector的迭代器在push_back以后扩容导致迭代器失效的问题

前言 vector是我们用到最多的数据结构&#xff0c;其底层数据结构是单端动态数组&#xff0c;由于数组的特点&#xff0c;vector也具有以下特性&#xff1a; ①O(1)时间的快速访问&#xff1b; ②顺序存储&#xff0c;所以插入到非尾结点位置所需时间复杂度为O(n)&#xff0c;删…

动态规划--(递推2(最长上升子序列,格子染色,斐波那切数列,奇数塔问题,最长子段和))

1281&#xff1a;最长上升子序列 【题目描述】 一个数的序列bi &#xff0c;当b1<b2<…<bS 的时候&#xff0c;我们称这个序列是上升的。对于给定的一个序列(a1,a2,…,aN) &#xff0c;我们可以得到一些上升的子序列(ai1,ai2,…,aiK) &#xff0c;这里1≤i1<i2<…

uniapp开发微信小程序设置分包,简单易学

文章目录 前言一、在 manifest.json文件中的源码试图中配置二、配置pages.json 前言 我们使用uniapp开发微信小程序的时候&#xff0c;当我们的包体积过大的时候&#xff0c;无法真机模拟。 因为小程序单个包只支持2MB&#xff08;现已支持预览4MB&#xff09;&#xff0c;所以…

Docker 学习总结(81)—— 冷门而又实用的 Docker 使用技巧总结

1、docker top 这个命令是用来查看一个容器里面的进程信息的,比如你想查看一个 nginx 容器里面有几个 nginx 进程的时候,就可以这么做。 ➜ ~ docker top 3b307a09d20d UID PID PPID C STIME …

JAVA面试大全之开发基础篇

目录 1、常用类库 1.1、平时常用的开发工具库有哪些? 1.2、Java常用的JSON库有哪些?有啥注意点? 1.3、Lombok工具库用来解决什么问题?

AI:155-基于深度学习的股票价格预测模型

本文收录于专栏:精通AI实战千例专栏合集 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 每一个案例都附带关键代码,详细讲解供大家学习,希望可以帮到大家。正在不断更新中~ 一.基于深度学习的股票价格预测模型 …

基于k8s的web服务器构建

文章目录 k8s综合项目1、项目规划图2、项目描述3、项目环境4、前期准备4.1、环境准备4.2、ip划分4.3、静态配置ip地址4.4、修改主机名4.5、部署k8s集群4.5.1、关闭防火墙和selinux4.5.2、升级系统4.5.3、每台主机都配置hosts文件&#xff0c;相互之间通过主机名互相访问4.5.4、…

深入解析大数据Scala面试题及参考答案(持续更新)

Scala,作为一种多范式编程语言,因其强大的功能性和与Java的互操作性,在大数据和并发编程领域备受青睐。本文将深入探讨10个常见的Scala面试题,并提供详尽的参考答案,以期帮助读者在面试中展现其Scala编程的深厚功底。 目录 1. Scala的基本特性是什么? 2. 什么是函数式…

总结IP协议各类知识点

前言 本篇博客博主将详解IP协议中的各类知识点&#xff0c;坐好板凳发车啦~ 一.IP协议格式 1.1 4位版本号&#xff08;version&#xff09; 指定IP协议的版本&#xff0c;对于IPv4来说&#xff0c;就是4。 1.2 4位头部长度&#xff08;header length&#xff09; IP头部的…

HarmonyOS像素转换-如何使用像素单位设置组件的尺寸。

1 卡片介绍 基于像素单位&#xff0c;展示了像素单位的基本知识与像素转换API的使用。 2 标题 像素转换&#xff08;ArkTS&#xff09; 3 介绍 本篇Codelab介绍像素单位的基本知识与像素单位转换API的使用。通过像素转换案例&#xff0c;向开发者讲解了如何使用像素单位设…

【LeetCode热题100】【多维动态规划】最长公共子序列

我昨天面了天美L1的游戏客户端开发&#xff0c;面了我100分钟&#xff0c;问完实习、项目、计算机图形学和C后给了我两道算法题做&#xff0c;一道是最长公共子序列&#xff0c;一道是LRU缓存&#xff0c;我知道是经典的题目&#xff0c;但是我都没敲过&#xff0c;最长公共子序…