大家好, 今天我們分享一個小工具,主要用于B站視頻的下載,只需要輸入對應視頻的網頁地址就可以進行下載到本地了。
目錄:
1. 原理簡介
2. 網頁分析
3. 視頻爬取
4. 存入本地
5. GUI工具制作
6. 完整代碼
原理很簡單,就是獲取視頻資源的源地址,然后爬取視頻的二進制內容,再寫入到本地即可。
案例視頻地址:https://www.bilibili.com/video/BV1BU4y1H7E3
打開該網頁,然后F12
進入開發(fā)者模式 ,接著點開網絡 —>全部 ,因為視頻資源一般比較大,我這里根據大小 進行了從大到小的排序
,找到了第一條這些可能和視頻源地址有關。
然后,我們復制找到的這條里的url
部分不變的部分,回到元素中ctrl+F
搜索,找到了可能和視頻源地址有關的節(jié)點。
果然,我們復制這部分內容,用json在線解析工具發(fā)現真的有我們需要的看似視頻文件所在的地址。
然后,我復制這個地址用瀏覽器打開發(fā)現提示403
了。。
不過,沒關系。。我們看接下來的操作!
在網頁分析部分,我們可以在視頻的B站 地址網頁源代碼里通過各種數據解析的方式來獲取視頻文件的源地址,這里我采用的是正則表達式 。
import requestsimport reimport json url = 'https://www.bilibili.com/video/BV1BU4y1H7E3' headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36' , 'referer' : 'https://www.bilibili.com' } resp = requests.get(url, headers=headers) palyinfo = re.findall(r'<script>window.__playinfo__=(.*?)</script>' , resp.text)[0 ] palyinfo_data = json.loads(palyinfo)
由于正在表達式獲取的結果是字符串,而實際上它是json
(字典),所以這里需要再引入json
庫來進行轉化。
我們再分析數據,可以發(fā)現最終視頻文件的信息,直接key-value
操作就行了。比較有意思的是視頻和音頻文件是分開的,我們需要分別爬取后再合并即可。
# 視頻與音頻文件地址 video_url = json_data['data' ]['dash' ]['video' ][0 ]['base_url' ] audio_url = json_data['data' ]['dash' ]['audio' ][0 ]['base_url' ]
有朋友可能會發(fā)現,base_url
貌似有好多個。是的,因為視頻清晰度有很多種嘛。這里我選取的是第一種超清 4K ,大家可以根據自己需求進行選擇!
當然了,我們把視頻存入本地的時候還需要起個名字,這里隨便找個節(jié)點解析出文件名就行了。
# 視頻標題 title = re.findall(r'<h1 title='(.*?)' class='video-title'>' , resp.text)[0 ]
既然我們已經解析獲得了視頻的文件地址、音頻地址和文件名,那么直接就安排下載吧!
不過,我們在網頁分析的時候發(fā)現直接打開視頻和音頻文件地址會提示403
,那么因為跳過去的來源不明確導致的,只需調整請求頭為如下即可:
headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36' , # 加上referer即可 'referer' : 'https://www.bilibili.com' }
搞定這些玩意后,我們就開始寫文件寫入本地的函數吧!
# 一般視頻是mp4,音頻是mp3 def down_file (file_url, file_type) : headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36' , 'referer' : 'https://www.bilibili.com' } resp = requests.get(url = file_url, headers=headers) print(resp.status_code) print(f'文件名稱:{title} ' ) # 設置單次寫入數據的塊大小 chunk_size = 1024 # 獲取文件大小 file_size = int(resp.headers['content-length' ]) # 用于記錄已經下載的文件大小 done_size = 0 # 將文件大小轉化為MB file_size_MB = file_size / 1024 / 1024 print(f'文件大小:{file_size_MB:0.2 f} MB' ) start_time = time.time() with open(title + '.' + file_type, mode='wb' ) as f: for chunk in resp.iter_content(chunk_size=chunk_size): f.write(chunk) done_size += len(chunk) print(f'\r下載進度:{done_size/file_size*100 :0.2 f} %' ,end='' ) end_time = time.time() cost_time = end_time-start_time print(f'\n累計耗時:{cost_time:0.2 f} 秒' ) print(f'下載速度:{file_size_MB/cost_time:0.2 f} M/s' )
運行結果:
# 視頻下載 >>>down_file(video_url, 'mp4') 200 文件名稱:【咒術回戰(zhàn)】第20集五條悟帥的有些過分了 文件大?。?2.10 MB 下載進度:100.00% 累計耗時:5.72 秒 下載速度:7.36M/s # 音頻下載 >>>down_file(audio_url, 'mp3') 200 文件名稱:【咒術回戰(zhàn)】第20集五條悟帥的有些過分了 文件大小:5.13 MB 下載進度:100.00% 累計耗時:0.80 秒 下載速度:6.42M/s
我們在本地可以看到下載成功的視頻文件:
由于視頻和音頻是分開的,所以單獨打開這個視頻是沒有聲音的,我們需要進行合并操作。
合并操作需要用到moviepy
庫,關于這個庫我們后續(xù)也會介紹它的更多應用,敬請期待~
from moviepy import *from moviepy.editor import * video_path = title + '.mp4' audio_path = title + '.mp3' # 讀入視頻 video = VideoFileClip(video_path)# 提取音軌 audio = AudioFileClip(audio_path)# 將音軌合并到視頻中 video = video.set_audio(audio)# 輸出 video.write_videofile(f'{title} (含音頻).mp4' )
就這樣搞定了:
Moviepy - Building video 【咒術回戰(zhàn)】第20集五條悟帥的有些過分了(含音頻).mp4. MoviePy - Writing audio in 【咒術回戰(zhàn)】第20集五條悟帥的有些過分了(含音頻)TEMP_MPY_wvf_snd.mp3 MoviePy - Done. Moviepy - Writing video 【咒術回戰(zhàn)】第20集五條悟帥的有些過分了(含音頻).mp4 Moviepy - Done ! Moviepy - video ready 【咒術回戰(zhàn)】第20集五條悟帥的有些過分了(含音頻).mp4
這個吧,就是用我常用的pysimplegui
來操作了,比較簡單。