工欲善其事必先利其器,我们经常需要在B站上保存自己想要的“学习资料”。对于单一的视频,我们可以用“B站下载助手”即时下载,安装地址:https://chrome.google.com/webstore/detail/bilibili哔哩哔哩下载助手/bfcbfobhcjbkilcbehlnlchiinokiijp?hl=zh-CN
打开你要播放的视频页面,在页面底部会弹出下载助手的主界面
操作比较傻瓜化,但是遇到多P视频操作起来就不是很有好了,需要逐个点击确认。课件什么的一个个下很麻烦,有这工夫点开,视频也都顺便看完了,这时候就需要批量下载了。
解放双手,首选Python,我们来看看Python中有什么骚操作可以满足我们的“学习需求”
安装you-get
you-get是一个基于python的多媒体内容下载开源工具,在控制台输入pip3 install you-get ,我这里已经安装好了,
安装FFmpeg
you-get是基于ffmpeg进行视频音频处理的,如果不装ffmpeg,通常下载的视频是声音画面分开的两个文件,后续需要合并及转码就不能完成了。下载地址:https://www.ffmpeg.org/
下载测试
- 安装完成后就可以进行下载了,先下载一个B站普通视频,在命令行中输入
you-get https://www.bilibili.com/video/BV1xK411773N
可以看到下载的是1080P的MP4文件(按默认最高画质,有时候下载的是flv)。
可以使用--info/-i以查看所有可用画质与格式,使用--format以指定下载的格式
如果输入的是一个多P视频的地址,you-get会提示你使用--playlist参数
you-get: This is a multipart video. (use --playlist to download all parts.)
哦豁,这不就是我们寻找的批量下载吗,赶紧试试
you-get --playlist https://www.bilibili.com/video/BV1B5411W7TW
然而这样下载经常会遇到网络延迟中断,这时又得重新执行上面的命令。而且是逐个视频下载的,经常会卡在某一个视频上下不动(貌似服务器端有速率限制),网速并没有很好地利用,此方法能用,但不好用,而且python还没出手,还不够风骚。
多线程下载
那怎样才能发挥我们Python的功效,让操作过程骚起来呢?既然一个命令行只能同时下载一个文件,那么我们就采用多线程模式同时下载多个文件。下面这就走起来
import you_get
import threading
import os
import time
def download1080(count,url):
print("thread "+ str(count) +" is running")
os.system("you-get --format=dash-flv "+url)
def download720(count,url):
print("thread "+ str(count) +" is running")
os.system("you-get --format=dash-flv720 "+url)
def downloaddefault(count,url):
print("thread "+ str(count) +" is running")
os.system("you-get "+url)
def showinfo(count,url):
print("thread "+ str(count) +" is running")
os.system("you-get -i "+url)
def test(arg):
print("thread "+ str(arg) +" is running")
os.system("ping www.baidu.com")
print("thread "+ str(arg) +" finish")
if __name__ == '__main__':
url_seed = 'https://www.bilibili.com/video/BV1MJ411u7Bc?p='
thread_list = []
url_num = 33
thread_num = 8
for i in range(1,url_num+1):
#为每个新URL创建下载线程
url = url_seed + str(i)
t = threading.Thread(target=download720, args=(i,url))
#加入线程池并启动
thread_list.append(t)
t.start()
#print(thread_list[0])
#当线程池满时,等待线程结束
while len(thread_list)>thread_num:
#移除已结束线程
thread_list = [x for x in thread_list if x.is_alive()]
time.sleep(3)
# print("running threads_________" + str(thread_list))
pass
这里采用os.system()函数提交shell命令。首先定义几个函数分别下载不同清晰度格式,"you-get --format=dash-flv "、"you-get --format=dash-flv720 "、"you-get --format=dash-flv480 "、"you-get --format=dash-flv360 "。
确定下载视频的URL链接公共部分,去掉数字后缀
url_seed = 'https://www.bilibili.com/video/BV1MJ411u7Bc?p='
B站中多P视频的URL链接有两种形式,第一种是刚点开标题后出现的合集链接,第二种是点开右侧播放列表任意p视频时出现,这里需要传入的是第二种URL链接,下载对应的p=n的视频
#点开总标题时出现
https://www.bilibili.com/video/BV1MJ411u7Bc
#点开各Part
https://www.bilibili.com/video/BV1MJ411u7Bc?p=1
https://www.bilibili.com/video/BV1MJ411u7Bc?p=2
https://www.bilibili.com/video/BV1MJ411u7Bc?p=3
......
thread_list列表充当一个简单线程池,存放活动的下载线程。
url_num = 33 代表分P视频的总数,在右侧选集列表查看,这里是33。
thread_num =8 定义了线程池的线程数量上限,根据自己电脑的核心数和网络带宽确定,通常4~8就够用了。
下面就为每一个URL单独创建下载线程,启动下载,注意查看网页上最高支持的清晰度,选取不同的目标函数,这里最高清晰度为720P,选择target=download720。当线程池满了就等待先前的下载结束,每隔3秒查询一次
for i in range(1,url_num+1):
#为每个新URL创建下载线程
url = url_seed + str(i)
t = threading.Thread(target=download720, args=(i,url))
#加入线程池并启动
thread_list.append(t)
t.start()
#print(thread_list[0])
#当线程池满时,等待线程结束
while len(thread_list)>thread_num:
#移除已结束线程
thread_list = [x for x in thread_list if x.is_alive()]
time.sleep(3)
将上述代码保存成download_with_you-get.py文件,下载新的合集时需要修改对应的url_seed,url_num和下载清晰度,然后在shell命令行中输入
python download_with_you-get.py
或者将上述命令保存成bat文件点击运行,这样就开始下载啦
我们看下效果,速度简直飞起,500Mbps带宽都跑满了,而且同时把弹幕文件也下载下来了,保存成xml文件,本地也可以看弹幕了,美滋滋。不过对于大多数课程视频,我们并不需要弹幕,可以在上面的bat文件加入一行 del *.xml 删除之。
如果网络负载过大,有时候会中断,或者请求时间过长导致程序退出,没关系,重新运行上面的bat就好了,you-get会自动断点续传,已下载的会跳过该文件。如果程序正常退出,那么所有的视频都下载好了。
文件重命名
不过等等,我们下载的视频文件都带了很长的父标题前缀,有时候文件列表里很难完全显示,需要把它简化一下,更清楚地看到是第几集,如上图,只需要保留小括号里边的内容作为文件名即可。
新建rename.py文件,内容如下
coding: utf-8
import os
path = os.getcwd() +'\\'
for f in os.listdir(path):
if f.endswith(u'.mp4'):
oldname = path + f
#print(f)
f1 = f.split("(")
try:
f2 = f1[1].split(")")
except:
f2 = f1
if f2[0].endswith(u'.mp4'):
newname = path + f2[0]
else:
newname=path + f2[0] + '.mp4'
os.rename(oldname,newname)
print(oldname,'======>',newname)
遍历当前文件夹下的所有文件,找出我们下载的MP4文件名,分割出小括号中间的内容作为新文件名,采用os.rename函数重命名文件。如果文件名中没有小括号(已经处理过),则沿用旧文件名,这样文件名就格式化好了
大会员视频
如何下载那些需要登录才能下载的视频呢,这时候就要用到cookies信息了。首先需要使用 firefox 火狐浏览器事先登录视频网站平台账号,在开始运行中输入%appdata%/Mozilla/firefox/profiles确定,会显示类似下面这样的文件夹
进入这个文件夹,找到 cookies.sqlite 文件,获得完整文件路径
// 每个人的文件夹名称可能不一样
86mdqb8b.default-release
//完整文件路径
C:\Users\pizh12thu\AppData\Roaming\Mozilla\Firefox\Profiles\86mdqb8b.default-release\cookies.sqlite
在shell中输入以下内容,就可以下载大会员视频了
set cookie=C:\Users\pizh12thu\AppData\Roaming\Mozilla\Firefox\Profiles\86mdqb8b.default-release\cookies.sqlite
you-get -c %cookie% https://www.bilibili.com/bangumi/play/ep75275
指定--format=dash-hdflv2就可以下载大会员HD画质视频,我们修改一下py文件,提交命令时加上“-c %cookie%”参数,记得字符串前面加上r强制不转义,url_seed和url_num都做相应修改,target=downloadhd使用最高下载格式
def download1080(count,url):
print("thread "+ str(count) +" is running")
os.system(r"you-get -c %cookie% --format=dash-flv "+url)
def download720(count,url):
print("thread "+ str(count) +" is running")
os.system(r"you-get -c %cookie% --format=dash-flv720 "+url)
def downloadhd(count,url):
print("thread "+ str(count) +" is running")
os.system(r"you-get -c %cookie% --format=dash-hdflv2 "+url)
if __name__ == '__main__':
url_seed = 'https://www.bilibili.com/bangumi/play/ep752' #后缀 + 74~85
thread_list = []
url_num = 85
thread_num = 8
for i in range(74,url_num+1):
#为每个新URL创建下载线程
url = url_seed + str(i)
t = threading.Thread(target=downloadhd, args=(i,url))
#加入线程池并启动
thread_list.append(t)
t.start()