社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

用 Python 处理 B 站下载视频

Python中文社区 • 5 年前 • 676 次点击  

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

B站图标

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

基本思路

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

  • 基本思路:

    1.分析下载文件目录结构和缓存文件  
    2.使用库来合成文件
  • 开发环境:

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

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

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

app目录

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

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

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




    

<i>
    <chatserver>chat.bilibili.comchatserver>
    <chatid>132379211chatid>
    <mission>0mission>
    <maxlimit>3000maxlimit>
    <state>0state>
    <real_name>0real_name>
    <source>k-vsource>
    <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">niced>
    <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站将一个视频的音频和视频分开存储了。

视频

音频
还剩下一个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&mid=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&mid=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&mid=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&mid=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文件


处理


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

合成信息


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


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开启编程生涯,酷爱编程,喜欢用实用简洁的程序解决工作生活中的问题。


打赏码

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

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

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/54099
 
676 次点击