前言

MATS在bilibili当UP主(MATSD10S)发布了不少视频,他告诉我第二天要删掉一些早期视频,吓得我想立刻把他的视频都下载下来好好收藏,于是这个程序就出来了

想法

我知道有这么一款命令行式的工具特别方便,叫做you-get(使用python编写),下载视频相当方便,如果直接使用它,能够省下不少功夫

使用程序调用you-get,只需要一个个自动填入视频ID下载就行了

为保证you-get能使用,请使用pip或其它方法安装you-get

1
pip3 install you-get

为了迅速完成,我选择了Python3进行程序编写

运行截图

我已经把他的视频都下载下来了,如果你想收藏你喜欢的UP主的视频,这个程序或许对你有些帮助

运行中

完成任务

开始介绍

有兴趣的话建议先跳到本文的“相关知识”处再返回此处,无兴趣的话可以直接跳到“完整程序”复制程序使用

什么!你都不需要?那就随便看看吧,关掉这个页面也行

第一步

确定思路:

1、you-get只能下载一个页面中的视频,下载一个UP主的所有视频,就一次次调用you-get

2、调用you-get需要提供页面网址(可简化为视频ID),那么就要获取所有视频ID

3、猜想有输入UP主ID 的接口可以调用,直接获取所有视频ID

验证:

完全可行,相关信息可见本文的“相关知识”处

第二步

选择程序语言

为保证较短时间完成,使用Python3作为编写语言

第三步

开始编写程序

获取获取所有视频ID

注意页面数需试出来,返回的JSON信息没有提到页面总数(**#遍历所有页面** 处)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#伪造请求头
json_headers_setting = {"user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"}
#获取所有视频的VID
def get_json_use_id(inf_setup):
req_s = requests.Session()
json_all_data = req_s.get("https://api.bilibili.com/x/space/arc/search?mid={}".format(inf_setup), headers=json_headers_setting).text
del req_s
return json_all_data

dict_json_d = []
#遍历所有页面
for i in range(1, 1000):#页面上限1000
data_id_all = json.loads(get_json_use_id("{}&pn={}".format(up_id, i)))
if data_id_all["data"]["list"]["vlist"] == []:
break
dict_json_d.append(data_id_all)

获取视频清晰度、格式信息

1
2
3
4
5
6
7
8
9
process = subprocess.Popen("you-get --json " + url_set, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
r_json = str(process.stdout.read().decode('utf-8'))

json_d_inf = json.loads(r_json)

down_command = ""
#遍历JSON获取视频信息
for i in json_d_inf["streams"]:
data_j_get = json_d_inf["streams"][str(i)]

选择最高清晰度、MP4格式

1
2
3
4
5
6
7
8
9
10
11
12
process = subprocess.Popen("you-get --json " + url_set, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
r_json = str(process.stdout.read().decode('utf-8'))

json_d_inf = json.loads(r_json)

down_command = ""
#遍历JSON获取视频信息
for i in json_d_inf["streams"]:
data_j_get = json_d_inf["streams"][str(i)]
down_command = str(i)
if data_j_get["container"] == "mp4":#选择最高清晰度且为MP4格式
break

注意判断是否为多P视频,多P视频会有提示信息在开头或者结尾出现导致JSON无法被正确加载

变量d_ctl就是为了指示是否不是多P视频

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
d_ctl = True
#判断是否为多P视频
if r_json[0:1] != "{":
count_a = 0
for a in r_json:
if a == "{" or a == "[":
break
else:
count_a += 1
ppp = "-p"
d_ctl = False
r_json = r_json[count_a:]
if r_json[-3:-2] != "}":
ppp = "-p"
d_ctl = False
r_json = r_json[:r_json.rindex("}")+1]

第四步

前面信息已经准备完了,直接开始下载

1
2
3
4
if d_ctl:
os.system("you-get --format={} {}".format(down_command, url_set)#多P视频下载
else:
os.system("you-get --playlist {}".format(url_set))#单视频下载

所有步骤就到这里了

完整程序

程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#coding=utf-8
import json
import subprocess
import os
import requests
from datetime import datetime

up_id = "102067913" #设置UP主的ID
down_work_dir = "./video" #下载的存放位置,没有此文件夹会自动新建
#伪造请求头
json_headers_setting = {"user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"}

if os.path.isdir(down_work_dir) == False:
os.mkdir(down_work_dir)

def down_when_vid_set(url_set):
#获取视频大小信息
process = subprocess.Popen("you-get --json " + url_set, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
r_json = str(process.stdout.read().decode('utf-8'))
#print(r_json)
ppp = ""
d_ctl = True
#判断是否为多P视频
if r_json[0:1] != "{":
count_a = 0
for a in r_json:
if a == "{" or a == "[":
break
else:
count_a += 1
ppp = "-p"
d_ctl = False
r_json = r_json[count_a:]
if r_json[-3:-2] != "}":
ppp = "-p"
d_ctl = False
r_json = r_json[:r_json.rindex("}")+1]

#print(r_json)
json_d_inf = json.loads(r_json)

title_get = json_d_inf["title"]
down_command = ""
#遍历JSON获取视频信息
for i in json_d_inf["streams"]:
data_j_get = json_d_inf["streams"][str(i)]
down_command = str(i)
if data_j_get["container"] == "mp4":#选择最高清晰度且为MP4格式
break

#开始运行下载命令
now_ttime = datetime.now().strftime('%a,%b%d,%H-%M-%S')
dir_set = "{}/{}{}".format(down_work_dir, now_ttime, ppp)#以当前时间作为目录名创建目录
os.mkdir(dir_set)
if d_ctl:
os.system("you-get --format={} {} -o {}".format(down_command, url_set, dir_set))#多P视频下载
else:
os.system("you-get --playlist {} -o {}".format(url_set, dir_set))#单视频下载

#down_when_vid_set("https://www.bilibili.com/video/BV1z54y1a7Kj")
#获取所有视频的VID
def get_json_use_id(inf_setup):
req_s = requests.Session()
json_all_data = req_s.get("https://api.bilibili.com/x/space/arc/search?mid={}".format(inf_setup), headers=json_headers_setting).text
del req_s
return json_all_data

dict_json_d = []
#遍历所有页面
for i in range(1, 1000):#页面上限1000
data_id_all = json.loads(get_json_use_id("{}&pn={}".format(up_id, i)))
if data_id_all["data"]["list"]["vlist"] == []:
break
dict_json_d.append(data_id_all)


#down_when_vid_set("https://www.bilibili.com/video/{}".format("BV197411a7tD"))
#exit()

count_all = 0
count_q = 0
#统计总视频数,用于计算进度
for i in dict_json_d:
for ii in i["data"]["list"]["vlist"]:
count_all += 1
#开始下载
for i in dict_json_d:
for ii in i["data"]["list"]["vlist"]:
#print(ii["bvid"])
count_q += 1
if count_q > 0:
print("Process at {:.1f}%".format((count_q/count_all)*100))#显示下载进度百分比
try:
print("At ", ii["bvid"])
down_when_vid_set("https://www.bilibili.com/video/{}".format(ii["bvid"]))
except:
print("Error at ", ii["bvid"])
os.mkdir(ii["bvid"])#出现错误就在程序运行目录新建一个名称为VID的文件夹以提示

使用方法

修改此处,改成需要的UP主

1
up_id = "102067913" #设置UP主的ID

然后运行程序

相关知识

一些知识

了解you-get简单使用

简单用法

使用如下命令可以获取一个视频的可下载列表

1
you-get -i 视频网址

下载单个视频

按照上图它提示的**you-get –format= [URL]**就可以开始选择需要的清晰度开始下载啦

按JSON返回信息

根据you-get的使用提示,还可以使用

1
you-get --json 视频地址

来获取可下载列表,这样可以方便程序处理

下载多P视频

考虑到B站存在视频分Part

当遇到多P视频时使用这个命令下载省事很多,需要注意的是,下载的是FLV格式视频

1
you-get --playlist 视频地址

设置下载存储文件夹

1
-o 文件夹

如上就是本文所涉及的所有you-get使用方法

在Python3中

requests库的部分用法

发起GET请求

只需要GET就能获取指定JSON,所以这里不提POST用法

1
2
3
4
import requests
req_s = requests.Session()
json_all_data = req_s.get("https://api.bilibili.com/")
del req_s
设置请求头(伪造请求头)

为了防止网站认为是脚本进行抓取而拒绝访问,伪造请求头来假装自己是浏览器

设置header

1
json_headers_setting = {"user-agent" : "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36"}

headers=

1
2
3
4
import requests
req_s = requests.Session()
json_all_data = req_s.get("https://api.bilibili.com/", headers=json_headers_setting)
del req_s
获取回复信息

.text

1
2
3
4
import requests
json_all_data = req_s.get("https://api.bilibili.com/", headers=json_headers_setting).text
print(json_all_data)
del req_s
安装requests库

如果提示requests无法调用,你可能还没有安装,可以使用pip进行安装

1
pip3 install requests

目录

操作需要os库

1
import os
目录创建

有时可能会操作失败,最好使用try方法执行此语句

1
os.mkdir()
目录存在判断

此语句 目录存在就不新建目录 时会使用

1
os.path.isdir()

运行命令行(CMD或shell)

用于调用you-get,当然也可以import you-get而不使用2这个方法

无输出(但可以将运行结果赋值给字符串)

演示运行ls命令

1
2
3
import subprocess
process = subprocess.Popen("ls", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
r_json = process.stdout.read().decode('utf-8')

直接运行

1
2
import os
os.system("ls")

当前时间获取

使用python3自带的datetime库

1
2
3
from datetime import datetime
now_ttime = datetime.now().strftime('%a,%b%d,%H-%M-%S')
print(now_ttime)

运行上述程序可以获得类似输出

1
Thu,Apr15,19-34-19

JSON的使用

加载JSON
1
2
import json
json.loads()
读取JSON
1
2
3
4
import json
j = '{"a" : "123abc"}'
aaa = json.loads(j)
print(aaa["a"])

一些必须提前获取的东西

获取一个UP主所有的视频ID

根据请求追踪,我找到了这个网址

1
https://api.bilibili.com/x/space/arc/search?mid=

把UP主的ID填上去就能获取一串JSON,不过只记录了第一页的视频ID

想获取所有视频ID就得传入页数了

1
&pn=

获取ID的示例链接

示例

示例如下,可以获取该UP主第二页的视频

1
https://api.bilibili.com/x/space/arc/search?mid=102067913&pn=2
注意

但是如果超过了UP主发布的视频页数,返回的JSON里的视频ID就会为空,所以需要注意

获取允许下载的视频参数

Bilibili上的视频是有清晰度的,而且还分FLV和MP4两种格式,所以使用you-get下载前最好先获取清晰度,再按此清晰度进行操作,否则会出错而无法下载

前面提过you-get可以返回json信息方便处理