源程序可直接到博主github上下載:https://github.com/HuerFu/bilibiliVideoMerge
最近想學習后端,又不想花錢,怎么辦呢?於是在手機端B站(嗶哩嗶哩)上面找到了滿意的免費視頻教程,但是手機端看起來很不方便啊。於是,我通過在手機端緩存下來后,導入到了電腦端,但是我后面了發現兩個問題:
1.本來一集視頻按理說一段吧,但是B站下載下來的視頻並不是完整的一段,而是被分成了多段blv格式的視頻,所以需要想辦法把它們拼接成完整的一段視頻!
2.視頻數量太大,不可能一個一個去修改,得想辦法批量處理!(我這里有120個視頻文件夾,下圖所示,沒顯示完)
基於上述兩個問題,必須用一種方法快速解決!網上百度查了一些資料,覺得可行性很大,於是准備自己動手寫代碼來實現這一功能。人生苦短,我學Python!哈哈,就是Python沒錯了,話不多說,直接上自己用python寫的批量合並並轉換B站視頻格式的源代碼:
#批量合並特定文件夾下的視頻文件,然后輸出到指定文件夾下 # 主要是需要moviepy這個庫 from moviepy.editor import * import os from natsort import natsorted import json # psutil是一個跨平台庫能夠輕松實現獲取系統運行的進程和系統利用率(包括CPU、內存、磁盤、網絡等)信息。它主要用來做系統監控,性能分析,進程管理。它實現了同等命令行工具提供的功能,如ps、top、lsof、netstat、ifconfig、who、df、kill、free、nice、ionice、iostat、iotop、uptime、pidof、tty、taskset、pmap等。目前支持32位和64位的Linux、Windows、OS X、FreeBSD和Sun Solaris等操作系統. import psutil # 殺死moviepy產生的特定進程 def killProcess(): # 處理python程序在運行中出現的異常和錯誤 try: # pids方法查看系統全部進程 pids = psutil.pids() for pid in pids: # Process方法查看單個進程 p = psutil.Process(pid) # print('pid-%s,pname-%s' % (pid, p.name())) # 進程名 if p.name() == 'ffmpeg-win64-v4.1.exe': # 關閉任務 /f是強制執行,/im對應程序名 cmd = 'taskkill /f /im ffmpeg-win64-v4.1.exe 2>nul 1>null' # python調用Shell腳本執行cmd命令 os.system(cmd) except: pass if __name__ == '__main__': #循環體 for i in range(120): #提取對應視頻標題的json文件路徑 myjsondirs = './video/{}/entry.json'.format(i + 1) #定義拼接完成后視頻的標題 vdtitle = '' with open(myjsondirs, 'r', encoding='UTF-8') as load_f: # loads方法將json格式數據轉換為字典(讀取文本用此法) load_dict = json.load(load_f) vdtitle = load_dict['page_data']['part'] #視頻文件夾路徑 mydirs = './video/{}/lua.flv.bili2api.80'.format(i+1) # 定義拼接視頻的數組 L = [] # 訪問 video 文件夾 # root指的是當前正在遍歷的這個文件夾的本身的地址,dirs是一個 list,內容是該文件夾中所有的目錄的名字(不包括子目錄),files同樣是 list,內容是該文件夾中所有的文件(不包括子目錄) for root, dirs, files in os.walk(mydirs): # 按文件名排序 # files.sort() # 自然排序法 files = natsorted(files) # print(files) # 遍歷所有文件 for file in files: # os.path.splitext(“文件路徑”) 分離文件名與擴展名:默認返回(fname, fextension)元組,可做分片操作 # 如果后綴名為 .blv if os.path.splitext(file)[1] == '.blv': # .blv格式視頻的完整路徑 filePath = os.path.join(root, file) # 讀取視頻到內存 myvideo = VideoFileClip(filePath) # 添加到數組 L.append(myvideo) # 對多個視頻在時長上進行拼接 final_clip = concatenate_videoclips(L) targetdir = './target/{}.mp4'.format(vdtitle) # 法一:生成目標視頻文件方法 # final_clip.to_videofile(targetdir, fps=24) #法二:最常規的生成目標視頻文件方法 final_clip.write_videofile(targetdir,fps=24, remove_temp=True) #remove_temp=True表示生成的音頻文件是臨時存放的,視頻生成后,音頻文件會自動處理掉!若為False表示,音頻文件會同時生成! print("{}---{}---拼接成功!".format(i + 1, vdtitle)) killProcess()
注意:因為moviepy拼接視頻特別慢,自己電腦配置也不行,要把120個文件夾下的視頻拼接完成需要很多時間!
經過一晚的運行,順利合並了成了120個視頻,結果如下:
總的來說合並后的視頻,畫質還是不錯的!
過程中遇到的一些問題:
其實我個人覺得寫代碼,遇到問題才是最有意思的,當把這一個一個的問題都解決掉,這種感覺才是真的nice!顯然我自己在處理合並B站視頻時遇到了一些問題,這里記錄下來,方便自己也方便他人查閱學習!
問題1:真正拼接視頻時,發現會報錯 OSError: [WinError 6] 句柄無效!
解決辦法:調用自定義函數killProcess()殺死moviepy產生的特定進程!
原因:這是因為調用了moviepy的concatenate_videoclips函數,沒有及時殺死進程!如果不加殺死進程的程序,循環一次,在任務管理器就會新產生一個ffmpeg-win64-v4.1.exe的進程,這個進程數量過多,windows任務管理器並不會自動殺死這些進程,而運行的python程序就會報錯!所以我在程序里添加了殺死ffmpeg-win64-v4.1.exe進程的程序,這樣拼接完成一個視頻,就調用函數強行殺死ffmpeg-win64-v4.1.exe進程,保證pycharm里面的ffmpeg-win64-v4.1.exe進程不會無限增長下去導致程序報錯!
問題2:報錯 psutil._exceptions.NoSuchProcess: psutil.NoSuchProcess no process found with pid 5764
解決辦法:添加try...except 處理,使python程序能夠處理在運行中出現的異常和錯誤。
原因:沒有發現指定的進程引起的程序報錯!
注意:命令 cmd = 'taskkill /f /im ffmpeg-win64-v4.1.exe 2>nul 1>null',這里的2>nul:表示不輸出錯誤信息 1>nul:表示不輸出成功的信息
--------------------喜歡就點個贊唄,嘻嘻嘻----------------------