How to download video from station B with one click
In order to easily download the video of station B, I wrote a small tool with Nodejs, and I used ibili
this library to download at the beginning, but because of the problem of not being able to customize the download directory, it was inconvenient to modify, so I built a wheel.
Warehouse address , welcome star.
Features
- Download individual works based on URL
- Download all works according to the homepage URL of the UP master
- Option to download video or audio
Environmental requirements
Node and npm are required to use this tool. If you don't have it, you can go to the official website to download and install it.
use
Install:
npm i bilibili-save-nodejs
复制代码
Using command line tools
bili-download
复制代码
Choose what and how to download based on the command line menu
Entrainment of private goods: one-click star chase, download all videos of Jiu San :
bili-download -d
复制代码
Using the Node.js API
Function name | effect |
---|---|
download |
download |
downloadByVedioPath |
Download a single work based on the video URL |
downloadByHomePath |
Download all works according to the UP homepage |
API parameters
Note: The parameters of the three functions are in the form of objects.
download
parameter name | Is it necessary | Ranges | meaning |
---|---|---|---|
downloadRange | Yes | ['byAuthor','byVedio'] |
According to author homepage URL or work URL |
downloadType | Yes | ['mp4','mp3'] |
Download video or audio |
downloadPath | Yes | none | Legal work URL or UP home page URL |
downloadFolder | no | none | The full path to the storage directory , the default value is used by default |
Directory defaults:
/video
Video: In a folder under the root directory/audio
Audio: in a folder under the root directory
demo:
const { download } = require("bilibili-download-nodejs");
download({
downloadRange: "byAuthor",
downloadType: "mp4",
downloadPath: "https://space.bilibili.com/313580179",
})
.then(() => console.log("下载成功"))
.catch((e) => console.log("下载出错"));
复制代码
downloadByVedioPath & downloadByHomePath
参数名 | 是否必须 | 取值范围 | 含义 |
---|---|---|---|
type | 是 | ['mp4','mp3'] |
下载视频或音频 |
url | 是 | 无 | 合法的作品 URL |
folder | 是 | 无 | 存储目录的完整路径 |
demo:
const { downloadByVedioPath, downloadByHomePath } = require("./download.js");
const path = require("path");
// 下载单个作品的视频
downloadByVedioPath({
url: "https://www.bilibili.com/video/BV1AL4y1L7cg",
type: "mp4",
folder: path.join(__dirname, "/foo"),
})
.then(() => console.log("下载成功"))
.catch((e) => console.log("下载出错"));
// 下载UP主所有作品的音频
downloadByHomePath({
url: "https://space.bilibili.com/313580179",
type: "mp3",
folder: path.join(__dirname, "/bar"),
})
.then(() => console.log("下载成功"))
.catch((e) => console.log("下载出错"));
复制代码
原理介绍
介绍完用法,简单介绍本项目的原理。
根据视频 URL 下载视频
根据 URL 获取 bvid
bvid 是现在 b 站视频的唯一标识,也是后续操作的必备参数。但为了方便使用,直接复制作品的 URL 更好理解和操作。
其实 bvid 就存在于 URL 之中,只需要做简单的字符串操作:
const urlList = url.split("/");
const bvid = urlList[urlList.length - 1].split("?")[0];
复制代码
根据 bvid 获取 cid 数组与作品标题
对于多 p 视频,仅通过 bvid 无法确定请求的是哪一 p 视频,此时 cid 起到了唯一标识视频的作用。通过浏览器抓包分析接口,可知获取 cid 的方法为:
const getCidByBvid = async (bvid) => {
const res = await axios.get("https://api.bilibili.com/x/web-interface/view", {
params: {
bvid,
},
});
return res.data.data.pages.map((item) => item.cid);
};
复制代码
该接口用于查询作品信息,因此获取作品标题的方法为:
const getTitleByBvid = async (bvid) => {
const res = await axios.get("https://api.bilibili.com/x/web-interface/view", {
params: {
bvid,
},
});
return res.data.data.title;
};
复制代码
根据 bvid 获取视频下载地址数组
- 首先,根据 bvid 获取 cid 数组
- 对于每个 cid,与 bvid 一起唯一标识了视频,进行请求
- 根据下载类型的不同(视频或音频),传入参数会不同。
参数含义:
fnval
设为 16 时,音视频将会分离,此时可以达到只下载音频的目的qn
参数标识清晰度,对照如下
清晰度 | 字段取值 |
---|---|
4K | 120 |
1080p+ | 112 |
1080p | 80 |
720p | 64 |
480p | 32 |
360p | 16 |
完整实现代码为:
const getDownloadPathById = async (bvid, type) => {
const cidList = await getCidByBvid(bvid);
const result = [];
for (const cid of cidList) {
const params = {
bvid,
cid,
qn: 112,
};
if (type === "mp3") {
params.fnval = 16;
}
const res = await axios.get("https://api.bilibili.com/x/player/playurl", {
params,
});
result.push(
type === "mp3"
? res.data.data.dash.audio[0].baseUrl
: res.data.data.durl[0].url
);
}
return result;
};
复制代码
根据下载地址下载资源
getDownloadPathById
返回的是下载地址,但直接在浏览器打开会报错,这是因为 request header 必须带有 referer
字段。
通过抓包可知, referer
字段取值就是视频地址,因此:
const getRefererByBvid = (bvid) => `https://www.bilibili.com/video/${bvid}`;
复制代码
下载资源的思路是:
- 检查目标文件是否存在,若存在不重复下载
- 根据 bvid 获取
referer
字段 - 请求资源
- 写入文件
完整实现:
const downloadResource = async ({ url, referer, folder, title, type }) => {
const target = path.join(folder, `${title}.${type}`);
if (fs.existsSync(target)) {
console.log(`视频 ${title} 已存在`);
return Promise.resolve();
}
const res = await axios.get(url, {
headers: {
referer,
},
responseType: "stream",
});
console.log(`开始下载:${title}.${type}`);
const writer = fs.createWriteStream(target);
res.data.pipe(writer);
return new Promise((resolve, reject) => {
writer.on("finish", resolve);
writer.on("error", reject);
});
};
复制代码
Download video according to UP homepage URL
Get mid from UP homepage URL
mid is the unique identifier of each B station user. Also for the convenience of understanding and use, the exposed interface uses the UP home page URL, and performs string processing to obtain the mid:
const getMidByUrl = (url) => {
const reg = /space.bilibili.com\/(?<mid>\d+)/;
return url.match(reg).groups?.mid;
};
复制代码
Get the video homepage address according to the up main mid
According to the packet capture, three parameters are required to obtain homepage information:
- mid: account ID
- ps: number of videos per page
- pn: current page number
Using the default number of 30 per page, the operation to get the home page can be written as:
const getHomeUrl = (mid, currentPage) =>
`https://api.bilibili.com/x/space/arc/search?mid=${mid}&ps=30&pn=${currentPage}`;
复制代码
By capturing and analyzing the returned data, the returned result is data.list.vlist
the video information, in which bvid can be obtained, and the subsequent download process is the same as above.