批量将B站学习视频以MP4格式下载到本地
一、背景描述
有些爱学习的小伙伴可能在外出或者回老家过节前会有提前将学习视频缓存的习惯,但是缓存的视频只能用Bilibili
App来看,屏幕属实有一点点小,因此更想将其下载到电脑上,然后用自己喜欢的播放器去观看(如potPlayer
)。
本篇博客将分享个JS脚本,利用免费解析网站,来实现批量的视频下载。
后续如果有bug将会在gitee上进行更新: https://gitee.com/he_fu_ren/bilibili-video-download
注:本脚本仅供学习使用,切勿用于商业用途
二、效果图
以下面的这个集数较少的视频为例,可以看到此教程有13个Part
脚本运行截图
下载文件截图
三、代码
本脚本使用的编程语言为为JavaScript,可以直接在浏览器中运行
let inputElement = document.getElementsByTagName('input')[0];
let resolveBtn = document.getElementsByClassName('btn')[0];
let beginTime = 0;
let downloadCount = 0;
let resolveCount = 0;
// 视频的前缀,只需要修改 .../video/av?p= 中的 av 号即可
let prefix = "https://www.bilibili.com/video/BV1EN411X7jw/?p=";
// 从点击完解析按钮到解析完成的时间间隔,默认1s
let interval = 2000;
// 从第几集开始下载,默认是从第一集开始
let beginPart = 1;
// 要下载的视频的集数
let num = 13;
// 算出最后一个Part
let endPart = beginPart + num - 1;
// 标记每一Part是否需要重置,1表示需要,0表示不需要
let retryCandidate = new Array(num);
retryCandidate.fill(1);
let retryTime = (interval + 1000) * num;
// 下载视频,url:视频链接,name:下载后的文件名
function downloadVideo(url,name) {
//创建XMLHttpRequest对象
let httpRequest = new XMLHttpRequest();
//打开连接,将请求参数拼在url后面
httpRequest.open('GET', url, true);
//设置期望的返回值类型
httpRequest.responseType = "blob";
//请求成功回调函数
httpRequest.onload = function (oEvent) {
if (httpRequest.status === 200) {
let fileName = decodeURI(name+'.mp4');
let response = httpRequest.response;
//数据转换为文件下载
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
let blob = new Blob([response]);
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
document.body.removeChild(elink);
console.log('视频' + fileName + '下载完成');
downloadCount = downloadCount + 1;
console.log('下载进度' + downloadCount + '/' + num);
}else {
console.log('下载失败,请手动下载,视频url ' + url);
}
}
httpRequest.send();
};
// 解析Part的url与名称
function resolve(i) {
if (document.getElementsByClassName('caption').length > 0) {
// 获取解析后的url
let url = document.getElementsByClassName('caption')[0].getElementsByTagName('p')[1].getElementsByTagName('a')[0].getAttribute('href');
// 获取文件名
let name = document.getElementsByClassName('caption')[0].getElementsByTagName('p')[0].innerText;
// 为了保证下载后的视频顺序,统一加上'Part-'前缀
name = 'Part-' + i + ' ' + name;
resolveCount = resolveCount + 1;
// 这里-1的目的是为了将逻辑下标变成物理下标
retryCandidate[i-1] = 0;
downloadVideo(url, name);
} else {
console.log('第' + i + '集视频解析失败,稍后将会重试');
}
console.log('解析进度:' + resolveCount + '/' + num);
};
// 调用网页的解析功能
function sendResolvRequest(i) {
console.log('当前正在解析第' + i + '集视频');
// url
let bilibiliUrl = prefix + i;
inputElement.value = bilibiliUrl;
let evt = document.createEvent('HTMLEvents');
evt.initEvent('input',true,true);
inputElement.dispatchEvent(evt);
// 触发按钮点击事件
resolveBtn.click();
// 解析结果
setTimeout(resolve(i),interval);
};
function doRetry() {
console.log('开始重试');
// 待重试的数量
let len = num - resolveCount;
// 重置开始解析的时间,初始化为0,表示第一次解析是在0s后执行,也就是立刻执行
beginTime = 0;
console.log('剩余解析失败的视频数量:',len);
// 遍历retryCandidate数组,找出需要重试的part
for(let i = 0; i < num; i++) {
if (retryCandidate[i] == 1) {
setTimeout(function(){
// 物理下标转逻辑下标
sendResolvRequest(i+1);
},beginTime);
console.log('第' + (i + 1) + '个视频将在' + beginTime/1000 + '秒后重试');
beginTime = beginTime + interval + 1000;
}
}
// 计算下一次重试的开始时间
let retryTime = (interval + 1000) * len ;
console.log('将在' + retryTime/1000 + 's后进行下一轮重试');
setTimeout(function() {
retry();
},retryTime);
}
// 重试失败的Part
function retry() {
// 成功解析的Part的数量 < 预期解析的Part的数量
if (resolveCount < num) {
console .log('开始重试');
doRetry()
}
}
// 正常的去解析下载每一个Part
for(let i = beginPart; i <= endPart; i++) {
setTimeout(function(){
sendResolvRequest(i);
},beginTime);
beginTime = beginTime + interval + 1000;
}
// 在上面循环结束完之后会解析重试失败的Part
setTimeout(function() {
retry();
},retryTime);
四、使用教程
第一步
打开视频解析网址:https://bilibili.iiilab.com/
第二步
按F12
打开控制台 或者 右键
网页然后再点检查
,打开控制台后点击`Consoles,切换到控制台界面
第三步
将上述代码全部复制到文本编辑器中,根据自身情况去做修改
可修改部分在代码最上面:
// 视频的前缀,只需要修改 .../video/【av】?p= 中括号内的部分即可,av号在对应B站视频的url中可以看到
let prefix = "https://www.bilibili.com/video/BV1q4411y7Zh/?p=";
// 从点击完解析按钮到解析完成的时间间隔,默认2s(2000ms),网速如果比较慢就改大一些
let interval = 2000;
// 从第几集开始下载,默认是从第一集开始
let beginPart = 1;
// 要下载的视频的集数,配合上面的参数,就是下载1-13集视频
let num = 13;
修改完之后就可以将修改后的代码粘贴到第一步打开的网页中去了。
说明
本脚本仅供学习使用,目前是初代版本,可能会有一些小bug,比如可能漏下载个别视频等,之后有时间会进行修复,并提交到git上