家里长辈平时爱用西瓜视频,看到喜欢的广场舞就会收藏,甚至想下载到视频机,晚上去广场跟着跳。
这时计算机专业出身的我,就派上用场了。不废话,直接上手:
人工操作多了就容易烦躁,于是用puppeteer做了个小工具,直接在命令行中传入视频连接进行下载
import chalk from "chalk"
import puppeteer from "puppeteer"
import dayjs from "dayjs"
import { get } from 'https'
import { createWriteStream } from "fs"
const iphonUserAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1"
const urls = process.argv.splice(2).filter(v=>!!v)
if(urls.length == 0){
console.error(chalk.red(`ERROR:请输入 URL`))
process.exit(-1)
}
const log = (...ps)=> console.debug(`${chalk.magenta(dayjs(new Date).format("HH:mm:ss"))} `, ...ps)
/**
* 下载远程资源到本地
* @param {String} remoteUrl
* @param {String} filename - 本地文件名
* @returns {Promise}
*/
const downloadToFile = (remoteUrl, filename)=>new Promise((ok)=>{
get(remoteUrl, res=>{
const stream = createWriteStream(filename)
res.pipe(stream)
stream.on('finish', ()=>{
stream.close()
ok()
})
})
})
/**
* 线程等待
* @param {Number} seconds - 秒,默认 3
* @returns {Promise}
*/
const sleep = (seconds=3)=> new Promise(ok=> setTimeout(ok, seconds*1000))
/**
* 根据分享地址获取视频页面的真实地址
* @param {String} url - 格式为 https://v.ixigua.com/xxxx/
* @returns {Promise}
*/
const getRealUrl = url=> new Promise(ok=> get(url,{headers:{"user-agent": iphonUserAgent }}, res=> ok(res.headers.location.split("/?").shift())))
const browser = await puppeteer.launch({headless: true})
const page = await browser.newPage()
await page.setExtraHTTPHeaders({
'User-Agent': iphonUserAgent,
'Referer': "https://m.ixigua.com/"
})
console.info(`本次下载地址 ${chalk.magenta(urls.length)} 个`)
/**
* 三类视频地址:
* 1、https://m.ixigua.com/video/7289329592371053071 手机版地址
* 2、https://www.ixigua.com7289329592371053071 PC版地址
* 3、https://v.ixigua.com/idPeuNAa/ 分享出来的短链接
*/
for (let url of urls) {
console.group(url)
if(url.startsWith("https://v.ixigua.com") || url.startsWith("https://www.ixigua.com/")){
url = await getRealUrl(url)
if(url.startsWith("//m.")) url = `https:${url}`
log(`解析真实地址 ${url}`)
}
log(`开始加载地址 ${url}`)
await page.goto(url)
await sleep(1)
let title = await page.title()
log(`读取标题:${title}`)
let video = await page.waitForSelector("video")
if(video){
let src = await video.getProperty('src')
src = await src.jsonValue()
log(`视频地址 ${src}`)
// 文件名删除空格、emoji、-西瓜视频这几类
let filename = `${title.replace(" ", "").replace(/\uD83C[\uDF00-\uDFFF]|\uD83D[\uDC00-\uDE4F]/g, "").replace("-西瓜视频", "").trim()}.mp4`
log(`即将保存资源到 ${filename}`)
let started = Date.now()
await downloadToFile(src, filename)
log(`原视频下载完成(耗时 ${((Date.now()-started)/1000).toFixed(4)} 秒) ^.^\n`)
}
else{
log(`找不到视频元素,跳过`)
}
console.groupEnd()
}
await page.close()
await browser.close()
console.debug(chalk.magenta(`\nBye!`))
process.exit(0)
使用方式:
index.js
npm i puppeteer chalk dayjs
node . {视频地址,多个用空格隔开}