提取B站音频的一些尝试

目录
  1. 提取B站的音频和视频
  2. 提取B站封面图片地址并进行保存
  3. 设置音频信息
TOC

在尝试构建自己的音乐库的时候,我不幸地发现现行的主流流媒体音乐库几乎都有一些音乐我找不到。于是在选择了Apple Music作为音乐库之后,我迫切地希望能够导入一些本地音乐。

鉴于B站有着很全的音乐资料,只是下载需要一些手段,我又对音乐品质没有太高的要求,研究从B站获取音频资料便成了优先选择。

  • 相关配置:Win11,Python,VScode

相关代码会发布到我的GitHub上(大概?)


提取B站的音频和视频

利用request的get获取B站该视频的信息,url为这个视频的网址,截取到BV好就行了。然后把它们保存下来留待下一步处理。

注意,request似乎不是pip自带的库,记得利用pip进行下载:

1
pip install request

这一部分我是随便找了一个网上的代码,由于B站上面标题和名字的关联有些微妙——有些是对的,有时候标题不是歌名,所以在细节方面进行了一些改动。

另外,为了能够加入到iTunes,我得将音频格式改为M4A(很奇怪,MP3总是无法导入)。

这方面的代码如下:(顺便写入了所有的头文件)

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
import pprint
import requests
import re
import json
import os
from mutagen.mp4 import MP4, MP4Cover, MP4Tags

session = requests.session()
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.37',
"Referer": "https://www.bilibili.com",
}
resp = session.get(url,headers=headers)
#print(resp.text)
if (title == ""):
title = re.findall(r'<title data-vue-meta="true">(.*?)_哔哩哔哩_bilibili',resp.text)[0]
play_info = re.findall(r'<script>window.__playinfo__=(.*?)</script>',resp.text)[0]
title = title.replace("《","")
title = title.replace("》","")

print("Title:"+title)
#print(play_info,type(play_info))
json_data = json.loads(play_info)
#pprint.pprint(json_data) #格式化输出,便于观看
#print(type(json_data))
audio_url = json_data['data']['dash']['audio'][0]['backupUrl'][0] #音频地址 [0]清晰度最高
#video_url = json_data['data']['dash']['video'][0]['backupUrl'][0] #视频地址

audio_content = session.get(audio_url,headers=headers).content #音频二进制内容
#video_content = session.get(video_url,headers=headers).content #视频二进制内容
with open(r'D:\\music\\'+title+'.m4a','wb') as f:
f.write(audio_content)

提取B站封面图片地址并进行保存

由于不想试错去慢慢研究相关库的信息携带的模式,我采用了一种委婉的方式来获取B站封面并添加到歌曲里。

  • 大致过程:首先获取B站封面的url地址,然后根据这个地址下载图片,然后将本地图片添加到歌曲信息里,就制成封面了。

而本部分就论述如何获取图片地址及保存上。

首先,我们通过查看网站源码可以发现(好吧其实是百度之后再用源码确认的),其实B站的封面地址在 pic: 之后,而且需要换一下编码方式才能使用。

注意,有的文章上会提到另一种封面地址获取方式,这种方式虽然不需要转换编码,但获取的图片基本都是小图,而且很模糊。

这个代码还处于比较简陋的状态,只是检测有”pic”:”.jpg”,即最前面前面和后面的描述来定位信息,有时候其实会多截取一大坨信息,导致解析失败,建议把网址打印出来看看防止搞错。

获取B站封面网址的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
def getcover():
#avnum=input("please input the av number of your video:")
#url="https://www.bilibili.com/video/av"+avnum
headers={
'Host':'www.bilibili.com',
'User-Agent': 'Chrome/73.0.3683.103'
}
text=requests.get(url,headers=headers).text#解决反爬虫问题
a = text.find('"pic":"')
b = text.find('.jpg"')
need_image = text[a+7:b+4].encode("utf-8").decode("unicode_escape")
cover = str(need_image)
print("Cover:"+cover)

随后,轻松获取一个根据网址下载图片的“轮子”——这种到处都是的东西就没兴趣自己造了,最好能匹配上现添加的库就行。

我选取的代码如下:

1
2
3
4
photo = requests.get(cover)
if photo.status_code == 200:
with open('D:\\music\\' + title + ".jpg", 'wb') as f:
f.write(photo.content)

把所有的路径设置为D盘下的绝对地址(我的相对地址老是出错),跑一次发现获取成功。


设置音频信息

我们现在可以尝试把音频和作者,封面结合在一起了。毕竟有时候我们习惯获取同一个歌手的好多歌曲,在代码里尝试加入作者信息,总比一个个手动加入信息要快。同时,我们也希望直接能够搞定封面,毕竟把本地图片加入音频是一个机械动作,让Python来完成当然是更好的。

我惊喜地发现,我不必使用FFmpeg来解析音频,只需要使用M就好。这真是一个好消息,毕竟FFmpeg的安装实在是太麻烦了,为了这盘小醋包这盘饺子大可不必。

同时需要注意的是,我们需要使用的是M.mp4,而非m4a,这一点让人很迷惑。

另外,文件标签分别为:”covr”(封面), “\xm9nam”(标题), “\xm9ART”(歌手,多个歌手用英文分号隔开), “\xm9alb”(专辑),注意,是covr而非cover,ART是大写,\xm9意义不明···

Anyway,大概就是这样,具体的代码直接进行一个参考就行,没有什么奇怪的地方。

最后记得用os.remove()删掉本地的封面图片就行。

相关代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
#add video info
video = MP4('D:\\music\\'+title+'.m4a')
with open('D:\\music\\' + title + ".jpg", "rb") as f:
video["covr"] = [
MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG)
]
video["\xa9nam"] = title
video["\xa9ART"] = artist
video["\xa9alb"] = album
video.save()

os.remove('D:\\music\\' + title + ".jpg")

至于如何上传到iCloud云音乐库,做到像其他流媒体一样在其他设备上边下边听···这个问题还在折磨我,苹果好像不允许不能匹配到他自家的音乐上传到云。

不过话说回来,似乎就算上传到云,也得下下来才能听——那我还不如本地同步到手机呢,悲。

总之,至少能本地愉快听歌了对吧。

附完整代码:(会输出到D盘的music文件夹中)

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
import pprint
import requests
import re
import json
import os
from mutagen.mp4 import MP4, MP4Cover, MP4Tags

artist = "三轮学"
album = ""
title = "Old Memory"
url = 'https://www.bilibili.com/video/BV1cq4y1Y7Qn'

print("------------- Linking -------------")
session = requests.session()
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36 Edg/106.0.1370.37',
"Referer": "https://www.bilibili.com",
}
resp = session.get(url,headers=headers)
#print(resp.text)
if (title == ""):
title = re.findall(r'<title data-vue-meta="true">(.*?)_哔哩哔哩_bilibili',resp.text)[0]
play_info = re.findall(r'<script>window.__playinfo__=(.*?)</script>',resp.text)[0]
title = title.replace("《","")
title = title.replace("》","")

print("Title:"+title)
#print(play_info,type(play_info))
json_data = json.loads(play_info)
#pprint.pprint(json_data) #格式化输出,便于观看
#print(type(json_data))
audio_url = json_data['data']['dash']['audio'][0]['backupUrl'][0] #音频地址 [0]清晰度最高
#video_url = json_data['data']['dash']['video'][0]['backupUrl'][0] #视频地址

audio_content = session.get(audio_url,headers=headers).content #音频二进制内容
#video_content = session.get(video_url,headers=headers).content #视频二进制内容
with open(r'D:\\music\\'+title+'.m4a','wb') as f:
f.write(audio_content)

#get cover from Bilibili
def getcover():
#avnum=input("please input the av number of your video:")
#url="https://www.bilibili.com/video/av"+avnum
headers={
'Host':'www.bilibili.com',
'User-Agent': 'Chrome/73.0.3683.103'
}
text=requests.get(url,headers=headers).text#解决反爬虫问题
a = text.find('"pic":"')
b = text.find('.jpg"')
need_image = text[a+7:b+4].encode("utf-8").decode("unicode_escape")
cover = str(need_image)
print("Cover:"+cover)
photo = requests.get(cover)
if photo.status_code == 200:
with open('D:\\music\\' + title + ".jpg", 'wb') as f:
f.write(photo.content)
getcover()

#add video info
video = MP4('D:\\music\\'+title+'.m4a')
with open('D:\\music\\' + title + ".jpg", "rb") as f:
video["covr"] = [
MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG)
]
video["\xa9nam"] = title
video["\xa9ART"] = artist
video["\xa9alb"] = album
video.save()

os.remove('D:\\music\\' + title + ".jpg")
print("------------- Done -------------")
DAR
SON