NipGeihou's blog NipGeihou's blog
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档

NipGeihou

我见青山多妩媚,料青山见我应如是
  • Java

    • 开发规范
    • 进阶笔记
    • 微服务
    • 快速开始
    • 设计模式
  • 其他

    • Golang
    • Python
    • Drat
  • Redis
  • MongoDB
  • 数据结构与算法
  • 计算机网络
  • 应用

    • Grafana
    • Prometheus
  • 容器与编排

    • KubeSphere
    • Kubernetes
    • Docker Compose
    • Docker
  • 组网

    • TailScale
    • WireGuard
  • 密码生成器
  • 英文单词生成器
🍳烹饪
🧑‍💻关于
  • 分类
  • 标签
  • 归档
  • nodejs

    • 分享

      • 「NodeJS脚本」Bilibili视频进度统计脚本
        • 🛠️环境
        • 🔎寻找合适API
        • 📝接口分析
        • ⌨️编码
          • 封装getPageList(bvid)方法
          • 统计时长
          • 优化:接收用户输入
        • 📄源码
  • css

  • 最佳实践

  • TypeScript

  • ajax

  • JavaScript

  • 前端工程化

  • React

  • nextjs

  • Flutter

  • 笔记

  • 前端
  • nodejs
  • 分享
NipGeihou
2022-01-11
目录

「NodeJS脚本」Bilibili视频进度统计脚本

众所周知,B 站是个学习网站,每天都有人沉迷在 B 站中学习,有时候想看看现在看的这个课程 / 教程还有多久看完,而通过 已看选集数/总选集数 并不是那么准确,因为选集的时长有长又短,最好的方法是 已看时长/总时长 ,于是就有这个脚本。

# 🛠️环境

  • NodeJS
  • axios
  • prompt-sync

# 🔎寻找合适 API

  • 打开网页版 B 站的视频页面
  • 通过浏览器的抓包工具,从页面加载开始抓取到页面的视频选集出现
  • 通过搜索关键字,比如我这里搜索了第 1p 标题中的 概念 二字,就找到里对应的请求 API

# 📝接口分析

请求接口

https://api.bilibili.com/x/player/pagelist?bvid=BV19E411D78Q&jsonp=jsonp

响应头(response_heads)

access-control-allow-credentials: true
access-control-allow-headers: Origin,No-Cache,X-Requested-With,If-Modified-Since,Pragma,Last-Modified,Cache-Control,Expires,Content-Type,Access-Control-Allow-Credentials,DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Cache-Webcdn,x-bilibili-key-real-ip,x-backend-bili-real-ip,x-risk-header
access-control-allow-methods: GET,POST,PUT,DELETE
access-control-allow-origin: https://www.bilibili.com # 跨域只允许https://www.bilibili.com发来的请求

响应体(response_body)

{
    "code": 0,
    "message": "0",
    "ttl": 1,
    "data": [
        {
            "cid": 121579556,
            "page": 1,
            "from": "vupload",
            "part": "1.1.1 概念、组成、功能和分类",
            "duration": 1291,
            "vid": "",
            "weblink": "",
            "dimension": {
                "width": 1366,
                "height": 768,
                "rotate": 0
            }
        },
        {
            "cid": 121581144,
            "page": 2,
            "from": "vupload",
            "part": "1.1.2 标准化工作及相关组织",
            "duration": 481,
            "vid": "",
            "weblink": "",
            "dimension": {
                "width": 1366,
                "height": 768,
                "rotate": 0
            }
        }
    ]
}

经过对此接口的简单分析(也就是直接在浏览器打开),发现无需令牌即可打开,而请求参数中也只有一个 bvid 参数,而在响应的数据中 data[].duration 属性便是每个选集的时长,通过与页面时长的对比,可以肯定是以秒为单位存储,如 "duration": 1291 对应的就是 21分钟31秒 。

那么接下来的事情就很简单了,只需要获取接口中的数据,然后遍历统计即可。

# ⌨️编码

# 封装 getPageList(bvid) 方法

用于获取选集列表,因为对于方法调用者来讲,它关心的是 我传入一个bvid,你能返回一个对应的选集列表 ,它不关系方法内部是通过来实现的。

const axios = require('axios').default;

/**
 * 获取选集列表
 * @param bvid
 * @returns {Promise<any>}
 */
const getPageList = async (bvid) => {
    let res = await axios.get(`https://api.bilibili.com/x/player/pagelist?bvid=${bvid}&jsonp=jsonp`);
    return res.data.data;
};

# 统计时长

(async () => {
    // bv号(bvid)
    let bvid = "BV19E411D78Q"
    // 已观看的选集
    let watchedPage = 10;

    // 选集列表
    let pageList = await getPageList(bvid);

    // 初始化总时长、已观看时长
    let totalDuration = 0
    let watchedDuration = 0;

    // 遍历每一个选集
    for (let page of pageList) {
        // 如果选集时已观看的选集范围内的,记录到已观看时长中
        if (page.page <= watchedPage) {
            watchedDuration += page.duration;
        }
        // 统计每一个选集时长
        totalDuration += page.duration;
    }

    // 格式化总时长
    let totalDurationMin = Math.round(totalDuration / 3600);
    let totalDurationSecond = Math.round(totalDuration % 3600 / 60);

    // 格式化已观看时长
    let watchedDurationMin = Math.round(watchedDuration / 3600);
    let watchedDurationSecond = Math.round(watchedDuration % 3600 / 60);

    // 进度率
    let schedulePercent = Math.round(watchedDuration / totalDuration * 100);

    console.log(`视频总时长为:${totalDurationMin}小时${totalDurationSecond}分钟`);
    console.log(`已观看时长为:${watchedDurationMin}小时${watchedDurationSecond}分钟`);
    console.log(`观看进度为:${schedulePercent}%`)
})();

至此就可以在项目目录下使用命令行统计视频时长了:

PS C:\Users\NipGeihou\WebstormProjects\bilibili-stats> node index # idenx是我文件的名字
视频总时长为:21小时59分钟
已观看时长为:3小时38分钟
观看进度为:13%

# 优化:接收用户输入

刚刚的写法,是把 bv 号和已观看的选集数写死在代码里的,那么每次都要去改代码,这体验并不会,那么可以用一个 prompt-sync 库来接收用户输入的参数,动态的统计不同视频的观看时长情况。

// // bv号(bvid)
// let bvid = "BV19E411D78Q"
// // 已观看的选集
// let watchedPage = 10;

let bvid = prompt("请输入BV号:");
let watchedPage = prompt("请输入已观看的选集数:");
PS C:\Users\NipGeihou\WebstormProjects\bilibili-stats> node index
请输入BV号:BV19E411D78Q
请输入已观看的选集数:10
视频总时长为:21小时59分钟
已观看时长为:3小时38分钟
观看进度为:13%

# 📄源码

NipGeihou/bilibili-stats: Bilibili 视频进度统计脚本 (opens new window)

上次更新: 2024/03/11, 22:37:05
「CSS」Flex伸缩布局

「CSS」Flex伸缩布局→

最近更新
01
Docker Swarm
04-18
02
安全隧道 - gost
04-17
03
Solana最佳实践
04-16
更多文章>
Theme by Vdoing | Copyright © 2018-2025 NipGeihou | 友情链接
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式