前言

本文目标功能基于B站API实现

使用到的API

URL: https://api.bilibili.com/x/player/pagelist
method: GET
remarks: 获取 视频cid 用以下载

url 参数:

参数名 必选 备注
aid 视频的av号(avid)
bvid 视频的bv号
jsonp 默认值"jsonp"无需变动

: aidbvid必选一个

headers 参数:
目前没有必要的请求头参数

返回值:
以json格式返回, 包含视频cid part (标题) 等等…(包括所有子视频)

例:

URL: https://api.bilibili.com/x/player/playurl
method: GET
remarks: 获取视频资源

url 参数:

参数名 必选 备注
qn 指定返回下载链接的视频清晰度
otype 默认值 “json”
cid avid bvid同一页面只需变更 cid 来下载子视频
avid av号
bvid bv号

bvid avid必选一个, avid还需要转换

headers 参数:

参数名 备注
Cookie 只需要 SESSDATA 字段
User-Agent 使用postman测试结果表示可以不用设置(建议添加)

例:

内容限制对这些接口描述比较片面, 如有不明白的地方可以自行研究,也可以加我(俺)一起讨论讨论。

基本思路

  1. 通过API 获取 视频cid

    • 可能含有多个视频
  2. 使用 cid avid (或 bvid)请求接口返回视频下载链接
  3. 下载
    • 这个 playurl 接口返回的视频链接下载速度巨慢…可能需要多线程下载(至少俺就是这么淦的)

具体实现

环境: Python3.9
requests==2.25.1

import requests
import threading
import os, timefrom requests.api import headclass CodeChange:def __init__(self) -> None:self.table='fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'self.tr={}for i in range(58):self.tr[self.table[i]]=iself.s=[11,10,3,8,4,6]self.xor=177451812self.add=8728348608def decode(self, x):r=0for i in range(6):r+=self.tr[x[self.s[i]]]*58**ireturn (r-self.add)^self.xordef encode(self, x):x=(x^self.xor)+self.addr=list('BV1  4 1 7  ')for i in range(6):r[self.s[i]]=self.table[x//58**i%58]return ''.join(r)class BiliBiliVideoDownloader:USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36"SESSDATA = "你的SESSDATA"# 用于 bv av 互转change = CodeChange()accept_description = []accept_quality = []def __init__(self, bvid) -> None:# bv号self.bvid = bvid# av号self.avid = self.change.decode(bvid)# 获取视频信息列表self.pagelist = self.__API_pagelist(bvid)# 发送 apidef __SEND_api(self, url, api_name):headers = {"user-agent": self.USER_AGENT,"cookie": f"SESSDATA={self.SESSDATA}"}try:return requests.get(url, headers=headers)except:print(f"API Error with \"{api_name}\"")def __API_pagelist(self, bvid):url = f"https://api.bilibili.com/x/player/pagelist?bvid={bvid}&jsonp=jsonp"response = self.__SEND_api(url, "__API_pagelist")data = response.json()["data"]# 列表生成器 (方便查看)return [{"cid": item["cid"],"page": item["page"],"title": item["part"]}for item in data]def __API_playurl(self, qn, cid, bvid, otype="json"):url = f"https://api.bilibili.com/x/player/playurl?qn={qn}&otype={otype}&cid={cid}&bvid={bvid}"# print(url)response = self.__SEND_api(url, "__API_playurl")return response.json()["data"]def Show_Accept_Quality(self):if not (self.accept_description and self.accept_quality):# use api get the video accept qualitydata = self.__API_playurl("16", self.pagelist[0]["cid"], self.bvid)self.accept_description = data["accept_description"]self.accept_quality = data["accept_quality"]# output for i in range(len(self.accept_description)):print(f"{i}  {self.accept_description[i]}")def Show_All_Page_Title(self):for i in range(len(self.pagelist)):print(f"{i}. {self.pagelist[i]['title']}")def __Get_Download_Link(self, index : int, quality : int) -> dict:""":data["durl"] 为一个列表:只有一个元素时视频既是一整段视频:大于一个元素每个元素都是一个视频切片 下载完成后还需要进行合并"""cid = self.pagelist[index]["cid"]return self.__API_playurl(quality, cid, self.bvid)def Start_Download(self, part, qualityIndex):data = self.__Get_Download_Link(part, self.accept_quality[qualityIndex])title = self.pagelist[part]["title"]filename = f"{part}.{title}.{data['format'][:3]}"    # 提取前三个字符作为后缀 [mp3, flv]if len(data["durl"]) <= 1:filesize = data["durl"][0]["size"]url = data["durl"][0]["url"]workerThread = DownloadWorkerThread(url, filename, filesize=filesize)# 阻塞主线程workerThread.setDaemon(True)workerThread.start()workerThread.join()class DownloadWorkerThread(threading.Thread):fileLock = threading.Lock()threadCount = 30maxBlockSize = 1024*1024*5curRangNum = 0curRangNumLock = threading.Lock()headers = {"user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.190 Safari/537.36","referer": "https://www.bilibili.com/"}EMPTY_STR = ""ERROR_STATUS_VALUE = -1def __init__(self, url, filename, **kwargs) -> None:threading.Thread.__init__(self)self.url = urlself.filename = filenameself.curSize = 0self.filesize = kwargs.get("filesize") or self.Auto_Get_Filesize()self.maxBlockSize = kwargs.get("maxBlockSize") or self.maxBlockSizeself.headers = kwargs.get("headers") or self.headersself.threadCount = kwargs.get("threadCount") or self.threadCount# check filename if not os.path.exists(filename):# Create file with open(filename, "w+")as f:f.close()def run(self):tLst = []print(f"""
*******************************************************************
文件名: {self.filename}
文件大小:{self.filesize//(1024**2)} MB
启用线程: {self.threadCount}
*******************************************************************""")for i in range(self.threadCount):t = threading.Thread(target=self.Range_Worker, args=(self, ))t.setDaemon(True)# print("Start thread: " + t.getName())t.start()tLst.append(t)print("Thread strat success")t = threading.Thread(target=self.Display_Progress, args=())t.setDaemon(True)t.start()tLst.append(t)for t in tLst:t.join()def Display_Progress(self):num = 0size = self.filesize // (1024**2)preSize = 0while num < 50:num = int(50*(self.curSize/self.filesize))# \033[32m  将后面文字设为绿色   \033[0m 设为默认print(f"\r [\033[32m{'■'*num}{' '*(50-num)}\033[0m]{self.curSize//(1024**2)}/{size} MB    {(self.curSize-preSize)//(1024**2)} MB/S", end="")preSize = self.curSizetime.sleep(1)def Range_Worker(self, downloader):reqHeaders = self.headers.copy()while True:if self.curRangNum != 0 and self.curRangNum*self.maxBlockSize > self.filesize:breakself.curRangNumLock.acquire()rag = f"{self.curRangNum * self.maxBlockSize}-{(lambda x : x if x < self.filesize else self.EMPTY_STR)((self.curRangNum+1) * self.maxBlockSize-1)}"self.curRangNum += 1self.curRangNumLock.release()reqHeaders["Range"] = "bytes=" + ragresponse = requests.get(url=self.url, headers=reqHeaders, stream=True)if response.ok:# print(rag)dataLst = []for chunk in response.iter_content(chunk_size=4096):self.fileLock.acquire()self.curSize += len(chunk)self.fileLock.release()dataLst.append(chunk)downloader.Write_Content(b''.join(dataLst), rag)# print(response.headers.get("Content-Range") + "\n", reqHeaders, "\n" + rag)else:print(rag + f": Error     status:{response.status_code}" )response.raise_for_status()def Write_Content(self, content, content_range):seekIndex = int(content_range.split("-")[0])self.fileLock.acquire()with open(self.filename, "rb+")as f:f.seek(seekIndex)f.write(content)self.fileLock.release()# print(f"{content_range}")def Auto_Get_Filesize(self) -> int:headers = self.headers.copy()headers["range"] = "bytes=0-0"response = requests.get(self,url, headers=headers, stream=True)if response.ok:return (lambda x : int(x.split("-")[-1]) if x != None else self.ERROR_STATUS_VALUE)(response.headers.get("Content-Range"))else:response.raise_for_status()def Auto_Get_Blocksize(self) -> int:passif __name__ == "__main__":    # 下载测试video = BiliBiliVideoDownloader("BV1w54y1y7Di")video.Show_All_Page_Title()# pageIndex = int(input("\nSelect page:"))video.Show_Accept_Quality()qualityIndex = int(input("\nSelect download quality:"))for pageIndex in range(len(video.pagelist)):print(f"\n{pageIndex}.{video.pagelist[pageIndex]['title']}")video.Start_Download(pageIndex, qualityIndex)

Bilibili 视频下载 Python 实现相关推荐

  1. python批量下载bilibili视频_关于bilibili视频下载的一些小思路

    关于bilibili视频下载的一些小思路 (基于b站官方接口实现) 由于B站近期某些蛋(chou)疼(feng)表现,某十八线女团不知名野站(我无聊作死的地方)为了稳健,需要备份一份所有视频.然而市面 ...

  2. Javascript 创建书签小工具 (bilibili视频下载为例)

    书签小工具(Bookmarklets)是一个非常棒的javascript代码小片断伪装成的小应用,它驻留在你的浏览器里并为网页提供额外的功能.这里我们将研究如何从头开始创建一个书签小工具及一些最佳实践 ...

  3. Downkyi(哔哩下载姬)绿色便携版V1.4.0 | bilibili视频下载工具 \ B站解析器 -- b站视频怎么下载到电脑?

    downkyi(哔哩下载姬)是一款优秀实用搭载有强力B站解析器的bilibili视频下载工具,基于多线程Aria下载技术开发,具有简洁的界面和流畅高速的下载体验以及别具一格的代码开发逻辑,哔哩下载姬d ...

  4. bilibili视频下载神器[无广告]

    一.前言 ​ 最近群里有小伙伴问我bilibili视频如何下载,当然目前下载的方法网上有很多,但是习惯使然,我一般只介绍我信得过的软件以及广告很少的软件,也就是追求最简洁的页面以及最强大的功能,因此这 ...

  5. python爬虫之b站视频下载(python学习笔记)

    b站视频爬取(python学习笔记) 亲爱的b站自从在2020年3月23日将av号改为BV号后,之前的很多爬虫已经不能用了,暂时没看懂这操作的意义何在,反而一大串大小写英文字母加数字的BV号让人很难记 ...

  6. python视频下载-python动态视频下载器的实现方法

    这里向大家分享一下python爬虫的一些应用,主要是用爬虫配合简单的GUI界面实现视频,音乐和小说的下载器.今天就先介绍如何实现一个动态视频下载器. 爬取电影天堂视频 首先介绍的是python爬取电影 ...

  7. python视频下载-Python实现视频下载功能

    最近一两年短视频业务风生水起,各个视频网站都有各自特色的短视频内容.如果有这样一个程序,可以把各大视频网站的热门用户最新发布的视频都下载下来,不仅方便自己观看,还可以将没有版权的视频发布在个人社交网站 ...

  8. 哔哩哔哩(bilibili)视频下载-支持4K

    前言 小破站现在支持了4K清晰度了,作为没有4K显示器的我然而并体验不出区别,但我就是喜欢 屯 欢迎访问个人博客:https://ccsubia.github.io/ 准备阶段–安装著名的下载器IDM ...

  9. bilibili视频下载信息获取

    一.开发者工具 1. 开启方式 进浏览器后F12(这里以window为例,其他需要自行查询) 三个点的图标->更多工具->开发者工具(以Chrome版本 85.0.4183.121(正式版 ...

最新文章

  1. 键盘码 ascii码
  2. poj 3254 Corn Fields 状态压缩dp
  3. 《职场一点诀 帆风顺,一定快乐?》读后感
  4. 扩展插件_Adobe扩展工具插件系列
  5. 穿越疫情,阿里云3000万补贴助力中小企业寒冬突围
  6. java切片_ java中一个极其强悍的新特性Stream详解(非常实用)
  7. Oracle dmp文件导入
  8. wamp下安装php的xdebug调试的方法
  9. 次坐标从0开始_干货 | 全站仪测量使用方法及坐标计算,一步一步讲解!
  10. VS2017使用C#编写程序员告白小软件
  11. 大数据CAP原则(CAP定理)、BASE理论
  12. 网络新文化现象:newbilitynbsp;(不代表…
  13. 有益视力的食疗方法(转载)
  14. 红黑树插入修正和删除修正详解
  15. 打印机服务无法启动(如何解决打印机后台服务没有启动)
  16. MATLAB矩阵行列
  17. conda安装packages时报错File “C:\ProgramData\anaconda3\lib\site-packages\conda\core\subdir_data.py“
  18. html鼠标手状态,css鼠标样式
  19. 为了实现自动控制处理,需要计算机具有的基础条件是( ),计算机应用基础考试试卷(电大本科)...
  20. JAVA洛谷B2117 整理药名

热门文章

  1. linux 使用cp卡死,细说CP使用
  2. 抖音自动生成文字_抖音视频怎么加文字
  3. WAF检测率及误报测试工具Gotestwaf
  4. QMC5883L说明文档
  5. linux服务器硬盘修复,Linux服务器磁盘坏道怎么修复
  6. LeetCode 10. Regular Expression Matching / 44. Wildcard Matching
  7. 2007网吧经营攻略之技术完全篇
  8. 北京大学可视化发展前沿研究生暑期学校Day4
  9. 通过注册表方式启动和关闭手写输入法(方法适合各种应用)
  10. 9X、2000、XP、2003所有注册表设置