用 Python 处理 B 站下载视频

技术

picture.image

bilibili(哔哩哔哩,又称B站)是2009年6月推出的一个AGC相关的弹幕视频分享网站,是年轻人潮流文化的娱乐社区,可能对于听过但是不经常上b站的童鞋来说,对于b站最大的影响的就是二次元、动漫、弹幕等等。但是作为国内知名的弹幕视频网站,b站已经不仅仅局限于动漫,还有着丰富的学习资源。

picture.image

B站图标

作者本人常在b站搜索一些关于人工智能、机器学习类的视频资源,常常都是使用手机下载后离线观看,为了电脑观看方便也会使用“视频合并助手”一类的APP对视频进行转换处理后导入电脑观看,适逢春节假期再次下载视频想导入到电脑上观看,发现以前的视频转换APP已经失效,无法搜索到下载到手机里的b站视频资源,随后开始了下文描述的视频合成工作。

基本思路

  • 目的: 合成哔哩哔哩APP缓存到手机的文件,并转换为MP4格式
  • 基本思路:

            
1.分析下载文件目录结构和缓存文件    
2.使用库来合成文件  

        
  • 开发环境:

            
1.手机:华为Mate20x EMUI10   哔哩哔哩APP 版本:5.53.1  
2.开发环境:MacBook Pro 2015,PYTHON3.7.6 64-bit, Visual Studio Code  1.41.1  

        

0x00 哔哩哔哩APP缓存文件目录结构及文件分析

打开手机文件管理器,找到Android/data/tv.danmaku.bili/down文件夹,结构如下图所示:

picture.image

app目录

其中以8位数字命名的文件夹用于单个视频专辑的存储,其一级子目录是从数字1开始递增命名,每个目录内存储的是缓存的分节文件(可以理解为每一集)。其二级子目录均是以数字16命名,这是很规律的。

在每一个视频专辑下有以下几个文件:

1.在一级子目录下有danmaku.xml和entry.json,其中danmaku.xml为弹幕文件


        
<?xml version="1.0" encoding="UTF-8"?>  
<i>  
    <chatserver>chat.bilibili.com</chatserver>  
    <chatid>132379211</chatid>  
    <mission>0</mission>  
    <maxlimit>3000</maxlimit>  
    <state>0</state>  
    <real\_name>0</real\_name>  
    <source>k-v</source>  
    <d p="22.23400,1,25,16777215,1575199941,0,aaaeeaeb,25196110486700034">地气儿</d>  
    <d p="1318.88600,1,25,16777215,1578391805,0,48b91c28,26869566679810052">指定了版本的那个装不上,只能装最新的</d>  
    <d p="582.62400,1,25,16777215,1578964914,0,15eedcf5,27170040630476802">nice</d>  
    <d p="26.29000,1,25,16777215,1579009720,0,c1d89d8e,27193531775320068">哈哈哈确实</d>  
</i>  

    

entry.json文件则是关于缓存视频的描述文件:


        
{  
    "media\_type": 2,  
    "has\_dash\_audio": true,  
    "is\_completed": true,  
    "total\_bytes": 21176174,  
    "downloaded\_bytes": 21176174,  
    "title": "(全)基于python的Opencv项目实战",  
    "type\_tag": "16",  
    "cover": "http:\/\/i2.hdslb.com\/bfs\/archive\/afae181e4bb00d7ca2e97f192e6f11dc2c3d8142.jpg",  
    "prefered\_video\_quality": 16,  
    "guessed\_total\_bytes": 0,  
    "total\_time\_milli": 1152336,  
    "danmaku\_count": 0,  
    "time\_update\_stamp": 1580398289030,  
    "time\_create\_stamp": 1580348758458,  
    "avid": 77390697,  
    "spid": 0,  
    "seasion\_id": 0,  
    "bvid": "",  
    "page\_data": {  
        "cid": 132379572,  
        "page": 6,  
        "from": "vupload",  
        "part": "06、边缘检测",  
        "link": "",  
        "rich\_vid": "",  
        "vid": "",  
        "has\_alias": false,  
        "weblink": "",  
        "offsite": "",  
        "tid": 39,  
        "width": 960,  
        "height": 540,  
        "rotate": 0,  
        "download\_title": "视频已缓存完成",  
        "download\_subtitle": "(全)基于python的Opencv项目实战 06、边缘检测"  
    }  
}  

    

我们需要从这个json中提取“download_subtitle”字段作为文件的命名。
2.在以“16”命名的二级子目录下有3个文件,从文件的名称可以判断audio.m4s和vedio.m4s两个文件应该是缓存的音频和视频文件,我们可以尝试使用播放器播放这两个文件,发现能够成功的播放但是视频中没有声音,可以断定B站将一个视频的音频和视频分开存储了。

picture.image

视频

picture.image

音频

还剩下一个index.json文件


        
{  
    "video": [{  
        "id": 16,  
        "base\_url": "https:\/\/upos-sz-mirrorhw.bilivideo.com\/upgcxcode\/11\/92\/132379211\/132379211-1-30015.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J\_aug859r1qXg8xNEVE5XREto8GuFGv2U7SuxI72X6fTr859IB\_&uipk=5&nbs=1&deadline=1580403879&gen=playurl&os=hwbv&oi=611071466&trid=69dea77b6c6a4049a6f88615e82ada05u&platform=android&upsig=1e3e3eedc71aba8b50ce51e67f3ca508&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform∣=280178137",  
        "backup\_url": ["https:\/\/upos-sz-mirrorks3.bilivideo.com\/upgcxcode\/11\/92\/132379211\/132379211-1-30015.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J\_aug859r1qXg8xNEVE5XREto8GuFGv2U7SuxI72X6fTr859IB\_&uipk=5&nbs=1&deadline=1580403879&gen=playurl&os=ks3bv&oi=611071466&trid=69dea77b6c6a4049a6f88615e82ada05u&platform=android&upsig=6de0ffa1809d46318ca36387ca8d8634&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform∣=280178137"],  
        "bandwidth": 104293,  
        "codecid": 7,  
        "size": 19069578,  
        "md5": "eab8c79d8ab56a973626a20e1dee6c25"  
    }],  
    "audio": [{  
        "id": 30216,  
        "base\_url": "https:\/\/upos-sz-mirrorkodo.bilivideo.com\/upgcxcode\/11\/92\/132379211\/132379211-1-30216.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J\_aug859r1qXg8xNEVE5XREto8GuFGv2U7SuxI72X6fTr859IB\_&uipk=5&nbs=1&deadline=1580403879&gen=playurl&os=kodobv&oi=611071466&trid=69dea77b6c6a4049a6f88615e82ada05u&platform=android&upsig=59e394e791a6ccb7c32b7d2eb1f0957d&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform∣=280178137",  
        "backup\_url": ["https:\/\/upos-sz-mirrorks3.bilivideo.com\/upgcxcode\/11\/92\/132379211\/132379211-1-30216.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEuENvNC8aNEVEtEvE9IMvXBvE2ENvNCImNEVEIj0Y2J\_aug859r1qXg8xNEVE5XREto8GuFGv2U7SuxI72X6fTr859IB\_&uipk=5&nbs=1&deadline=1580403879&gen=playurl&os=ks3bv&oi=611071466&trid=69dea77b6c6a4049a6f88615e82ada05u&platform=android&upsig=856c6fed7b7f7c1a4967a8e40cf8fc59&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform∣=280178137"],  
        "bandwidth": 67113,  
        "codecid": 0,  
        "size": 12272062,  
        "md5": "5d7a6a8e6f4c2809ac61eeafa1d9eaae"  
    }]  
}  

    

这个json文件包含了音频文件和视频文件的相关信息。

0x01 合成音视频文件

通过上述分析,我们找到了单个专辑的缓存文件,下面需要做的就是将音轨合并到视频中去,为此我们需要使用Moviepy这个库。MoviePy是一个用于视频编辑的python模块,你可以用它实现一些基本的操作(比如视频剪辑,视频拼接,插入标题),还可以实现视频合成,还有视频处理,抑或用它加入一些自定义的高级的特效。此外,MoviePy可以读写绝大多数常见的视频格式,甚至包括GIF格式!详细的使用说明可以参考MoviePy - 中文文档和官方文档。
首先需要安装Moviepy库,使用pip直接安装即可,所需要的依赖库如numpy等会在安装时自动下载并配置:


        
pip install moviepy  

    

安装完毕后即可使用,我们先用单个文件尝试一下,代码如下:


        
from moviepy.editor import VideoFileClip,AudioFileClip #从moviepy中导入editor包  
  
audioFile  = r"/Users/airwolf/Desktop/81427329/1/16/audio.m4s"   #指定需要读取的音频文件  
videoFile  = r"/Users/airwolf/Desktop/81427329/1/16/video.m4s"   #指定需要读取的视频文件  
outputfile = r"/Users/airwolf/Desktop/81427329/output.mp4"       #指定输出文件  
video_in = VideoFileClip(videoFile) #读取视频文件  
audio_in =AudioFileClip(audioFile) #读取音频文件  
video_out = video_in.set_audio(audio)   #video\_out文件的输出是将音频文件合并到video\_in文件的音轨中  
video.write_videofile(outputfile) #输出video\_out文件  

    

picture.image

处理

此时我们用播放器打开输入文件output.mp4,发现音频文件已经合成到视频文件中了。

picture.image

合成信息

下一步我们就着手批量的文件合成,首先将音视频合成的方法封装成一个函数:


        
def set\_audio(proc\_file, output\_path):  
    (file_name, audio_file, vedio_file) = proc_file  
    file_name = file_name.replace('.', '-').replace('“', "").replace('”', "")#对文件名称中含有的影响命名的特殊字符进行处理  
    original_vedio = VideoFileClip(vedio_file)  
    audio = AudioFileClip(audio_file)  
    video = original_vedio.set_audio(audio)  
    outputfile = os.path.join(output_path, file_name)+".mp4"#形成输出文件名  
    video.write_videofile(outputfile)  

    

函数的输入有2个参数,参数proc_file表示要处理的文件信息,按照[文件名称,音频文件名,视频文件名]的列表形式输入,output_path为合成后的MP4文件输出路径。接着需要遍历视频专辑下所有的子目录,把待处理的视频放入一个proc_fileList列表中:


        
import os  
import json  
proc_fileLis=[]  
def get\_proList(init\_path):  
    folder = os.listdir(init_path)  
    for subfolder in folder:  
        name_path = os.path.join(init_path, subfolder)  
        json_file = os.path.join(name_path, "entry.json")  
        if os.path.exists(json_file):  
            file_info = []  #用于封装带处理的文件信息,格式为:[文件名,音频文件名,视频文件名]  
            with open(json_file, 'r') as f: #从json文件中提取文件名  
                data = json.load(f)  
                file_name = data["page\_data"]["part"]  
                file_info.append(file_name)  
            a_filename = os.path.join(name_path, "16/audio.m4s")  
            v_filename = os.path.join(name_path, "16/video.m4s")  
            file_info.append(a_filename)  
            file_info.append(v_filename)  
            proc_fileList.append(file_info)#将带处理文件加入到proc\_fileList中  

    

函数的输入参数init_path为待处理第一级目录,即上文所指的以8个数字命名的文件夹。
接着编写主函数:


        
import sys  
if __name__ == "\_\_main\_\_":  
    init_path = sys.argv[1]  
    get_proList(init_path)  
    for proc_file in proc_fileList:  
        print(proc_file)  
        set_audio(proc_file,init_path)  

    

最后保存为proc.py。
使用时打开终端,输入如下命令,即可完成视频的转换:


        
python proc.py 处理文件路径  

    

作者简介:

Airwolf,非IT行业码农,国家嵌入式系统设计师。自小学6年级自学BASIC开启编程生涯,酷爱编程,喜欢用实用简洁的程序解决工作生活中的问题。

picture.image

打赏码

Python中文社区作为一个去中心化的全球技术社区,以成为全球20万Python中文开发者的精神部落为愿景,目前覆盖各大主流媒体和协作平台,与阿里、腾讯、百度、微软、亚马逊、开源中国、CSDN等业界知名公司和技术社区建立了广泛的联系,拥有来自十多个国家和地区数万名登记会员,会员来自以工信部、清华大学、北京大学、北京邮电大学、中国人民银行、中科院、中金、华为、BAT、谷歌、微软等为代表的政府机关、科研单位、金融机构以及海内外知名公司,全平台近20万开发者关注。

picture.image ▼ 点击成为社区注册会员** 喜欢文章,点个** 在看 picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动客户端性能优化最佳实践
在用户日益增长、需求不断迭代的背景下,如何保证 APP 发布的稳定性和用户良好的使用体验?本次分享将结合字节跳动内部应用的实践案例,介绍应用性能优化的更多方向,以及 APM 团队对应用性能监控建设的探索和思考。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论