共计 3155 个字符,预计需要花费 8 分钟才能阅读完成。
起源
- 最近忽然想尝试剪视频,所以就想先从动漫开始,二次元搞起来,剪视频就必须须要原视频,怎么找到这些资源呢,知乎一搜一大把
- 我常常会上六 DM 外面去看动漫, 外面的动漫清晰度也还能够,所以就想怎么写个爬虫间接把喜爱的动漫下载下来,毕竟是干前端的,手动下载有点丢人把 😄😄😄
最终成果
下载后的文件名不是.mp4 怎么解决
比如说我下载的这个龙猫就是啥 yum 格局的,我间接后缀名改成.mp4 搞定,如果还不行,就上个格局工厂 应该就好了
作者在写这篇文章的技能和环境
- 前端略懂一二
- nodejs 无所不通
- window 零碎
- puppeteer 版本: 14.3.0
- node 版本: 16.1.0
开始剖析网站, 轻易搜一个喜爱的动漫
介绍页
轻易点击一个播放地址,F12 搞起来,剖析页面
能够,这个网站还是会玩的,调试工具开起来就给我始终 debugger
如何跳过 debugger 死循环
这种形式能够不便跳过死循环,并且咱们还能够持续调试
找到页面播放地址
这个网站还是很简略的,间接把资源地址丢到 iframe 外面而已,难度升高了,怪不得不给他人调试,😄😄
剖析播放地址由来
思路 1:通过接口申请剖析,是否存在共通点
通过抓包,发现第一集和第二集的播放资源门路没有共同性可言,放弃 o(╥﹏╥)o
思路 2:间接看播放器的源码逻辑,找到 url 的拼接逻辑
找出播放器源码,间接通过调试工具找到所有的 js 文件,而后轻易看看
剖析播放器源码
读取播放器源码发现,这个网站会在页面外面存入一个全局变量
全局变量 player_aaaa
会做赋值存储,而后会引入一个 js 文件,文件名:/static/player/parse.js
关上调试工具,找到 /static/player/parse.js 文件,
/static/player/parse.js 文件内容
-
浏览器控制台输出:MacPlayer.Parse + MacPlayer.PlayUrl
因为播放器的源码是一个自执行函数,而后咱们又看到这个 parse.js 文件外面的资源拼接形式,所以咱们能够在浏览器的控制台外面间接把这个资源给拼出来
右键保留?
把下面的地址放到浏览器外面拜访,发现就是咱们想下载的资源了,到了这一步骤,咱们就能够右键保留了,当然作为一名合格的前端,咱们怎么可能会去右键保留呢,接下来咱们就筹备上大杀器,puppeteer 配合 nodejs 来帮咱们实现主动下载
资源 demo
如何做自动化?
- 通过下面的连贯,会进入到一个解析页面,因为咱们要做成主动下载的,必须要找到视频源连贯,否则不行,o(╥﹏╥)o
- 查找元素页面,发现了最终的资源地址
- 最终的资源地址
应用 puppeteer 解析页面,获取到视频资源地址,而后应用 nodejs 主动下载视频
思路 1:遍历出播放列表,而后开始一个工作,顺次关上页面,找到资源地址,而后收集到所有的播放资源地址,应用 nodejs 下载到本地,
为啥不必下面那个思路,因为那个思路我把代码写完,测试了一下,发现他的服务器扛不住哈,所以还是保险点,一次一个操作
思路 2:应用 puppetee 主动触发右键下载,并保留到咱们想要下载的中央(目前没有尝试这个办法)
思路 3:遍历出播放列表,而后开始一个工作,从第一个开始,关上页面,找到资源地址,应用 nodejs 下载到本地,下载实现,开始下一个就这样
思路 3 难点剖析
pupeteer 如何获取元素的属性,别问我,反正我不懂,堆栈溢出大佬通知我的
-
堆栈溢出的答案
// 获取单个
await page.evaluate('document.querySelector("span.styleNumber").getAttribute("data-Color")')
// 获取多个
const attr = await page.$$eval("span.styleNumber", el => el.map(x => x.getAttribute("data-Color")));
nodejs 下载远端视频,并显示进度
const fs = require('fs');
const https = require('https')
// 我的 demo 应用的是 axios 来下载
const axiosRequest = require('./utils/request');
// 这是一个 axios 实例
axiosRequest.get('https://media.w3.org/2010/05/sintel/trailer.mp4', {responseType: 'stream'}).then(response => {
// 返回头外面的 content-length 字段,会通知咱们这个视频有多大
// 获取视频总长度 byte 为单位
const totalLength = response.headers['content-length']
// 以后数据的总长度
let totalChunkLength = 0
// 以后读取的流
const readSteam = response.data
// 读取流会触发的事件
readSteam.on('data', (chunk) => {
totalChunkLength += chunk.length
console.log('数据传输中,以后进度 ==>', ((totalChunkLength / totalLength) * 100).toFixed(2) + '%')
});
// 读取实现的工夫
readSteam.on('end', (chunk) => {console.log('获取远端数据结束')
});
// 读取谬误会触发的事件
readSteam.on('error', (err) => {console.log('获取远端数据结束,产生了谬误, 错误信息 ==>', err)
});
// 写入本地的文件名
const fileName = 67.mp4
// 调用 nodejs 写入文件办法
const writeFile = readSteam.pipe(fs.createWriteStream(fileName))
// 写入实现事件
writeFile.on("finish", () => {writeFile.close();
console.log("祝贺大哥,本地数据写入实现");
});
// 写入谬误触发的事件
writeFile.on("error", (err) => {console.log("不好意思,写入本地文件产生异样,错误信息 ==>", err);
});
});
//axios 代码如下
const axios = require('axios')
// 创立 axios 实例
const service = axios.create({
baseURL: '', // api 的 base_url
// 永不凋零,真男人 就是这么长久 😄😄
timeout: 90000000 // 申请超时工夫
})
// request 拦截器
service.interceptors.request.use(
config => {return config},
error => {
// Do something with request error
console.log(error) // for debug
Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {return response},
error => {return Promise.reject(error)
}
)
module.exports = service
残缺代码
残缺代码
免责申明
- 首先 非常感谢这个网站让我这个老二次元可能找到喜爱的片源 😄😄😄
- 本我的项目只是学习应用,无心对此网站进行爬虫等操作
- 心愿想用的同学本人玩玩就行,树大招风
- 侵权请分割,立马删除
- 就这些吧
参考资料
破解视频的另一个思路
puppeterr 获取元素属性
puppeterr 下载文件
正文完