起源
- 最近突然想嘗試剪視訊,所以就想先從動漫開始,二次元搞起來,剪視訊就必須需要原視訊,怎麼找到這些資源呢,知乎一搜一大把
- 我經常會上六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
完整程式碼
免責宣告
- 首先 非常感謝這個網站讓我這個老二次元能夠找到喜歡的片源 ???
- 本專案只是學習使用,無意對此網站進行爬蟲等操作
- 希望想用的同學自己玩玩就行,樹大招風
- 侵權請聯絡,立馬刪除
- 就這些吧