python 获取json中最大值_详细解析 Python 爬取 bilibili 的视频、弹幕以及封面
本文使用 Zhihu On VSCode 創(chuàng)作并發(fā)布
環(huán)境
用到的 Python 庫:
- Python 3.7
- requests
- moviepy
- json
- re
- os
瀏覽器:Firefox/ 83.0
訪問測試
打開一個視頻網(wǎng)址,如(https://www.bilibili.com/video/BV1E4411e7ir),然后直接打開發(fā)者工具,轉(zhuǎn)到網(wǎng)絡(luò),選擇XHR文件,再點擊播放視頻??梢钥吹胶芸炀蛡鬏斄撕芏辔募?。
Image 可以看出有兩種不同的文件,一種是 30280,另一種是 30080。因為 B 站是把音頻和視頻分開傳輸?shù)?#xff0c;所以很明顯,一種是視頻,另一種就是音頻。按大小來分的話,30080 是視頻,30280 是音頻文件。
首先用試著獲取其中的一個文件,來測試一下。 先把請求視頻 url 復(fù)制下來,再把請求頭弄下來,接著發(fā)送個請求。
import requestsheaders = {'Accept': '*/*','Accept-Language': 'zh,en-US;q=0.7,en;q=0.3','Accept-Encoding': 'gzip, deflate, br','TE': 'Trailers','Range': 'bytes=1431-391742','Origin': 'https://www.bilibili.com','Connection': 'keep-alive','DNT': '1','Referer': 'https://www.bilibili.com/video/BV1E4411e7ir','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0', }url = 'https://upos-sz-mirrorks3.bilivideo.com/upgcxcode/40/06/91280640/91280640-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605719498&gen=playurl&os=ks3bv&oi=2028921166&trid=cfe12b1b9d1b4b58bf13bb1d08429d3au&platform=pc&upsig=00dbb5691fd73fdc2b8e3a834e50ad15&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=11418834&orderid=0,3&agrr=0&logo=80000000'res = requests.get(url, headers=headers)with open('test.flv', 'wb') as fp:fp.write(res.content)然而請求之后發(fā)現(xiàn) flv 文件是空的,再看一下發(fā)回的請求文本,顯示 403 禁止錯誤:
<html><head><title>403 Forbidden</title></head><body bgcolor="white"><center><h1>403 Forbidden</h1></center><hr /><center>QCMAS/V2</center></body> </html>再仔細觀察一下抓包情況,在發(fā)送請 get 請求之前,瀏覽器會發(fā)送兩個 options 請求,應(yīng)該是請求許可的意思。分別是請求音頻許可和請求視頻許可,因為請求 url 與請求音視頻的 url 相同。
Image 那么用 session 來發(fā)送請求,保存好信息,再去請求鏈接。修改一下代碼:import requests# 請求音視頻的請求頭 headers_1 = {'Accept': '*/*','Accept-Language': 'zh,en-US;q=0.7,en;q=0.3','Accept-Encoding': 'gzip, deflate, br','TE': 'Trailers','Range': 'bytes=1431-391742','Origin': 'https://www.bilibili.com','Connection': 'keep-alive','DNT': '1','Referer': 'https://www.bilibili.com/video/BV1E4411e7ir','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0', }# 請求的OPTIONS的請求頭 headers_2 = {'Host': 'cn-gdgz4-cmcc-v-10.bilivideo.com','User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:82.0) Gecko/20100101 Firefox/82.0','Accept': '*/*','Accept-Language': 'zh,en-US;q=0.7,en;q=0.3','Accept-Encoding': 'gzip, deflate, br','Access-Control-Request-Method': 'GET','Access-Control-Request-Headers': 'range','Referer': 'https://www.bilibili.com/video/BV1E4411e7ir','Origin': 'https://www.bilibili.com','DNT': '1','Connection': 'keep-alive','Cache-Control': 'max-age=0', }url = 'https://upos-sz-mirrorcos.bilivideo.com/upgcxcode/40/06/91280640/91280640-1-30080.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1605752758&gen=playurl&os=cosbv&oi=2028921166&trid=18e55ae8ed4d41018ee9374a63501860u&platform=pc&upsig=346e8bea6d225d1ea592bb5a0c470c48&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=11418834&orderid=0,3&agrr=1&logo=80000000'session = requests.session()session.get(url, headers=headers_2) res = session.get(url, headers=headers_1)with open('test.flv', 'wb') as fp:fp.write(res.content)可以看到,test.flv 有文件大小了。
Image但打開時顯示解析錯誤:
Image再看一下多個不同請求的請求頭,只有 range 發(fā)生明顯的改變,而且 range 的值里的 bytes 參數(shù),說明這很有可能是一個下載的文件大小范圍。那么找到最后一個發(fā)送視頻請求的的包,把最大值復(fù)制下來,然后再設(shè)置請求頭里的 range 值為 0-最大值,即'Range':'bytes=0-29271958',然后再次運行 py 文件。很明顯這次請求回來的文件比之前的大了許多,再點擊播放,解析成功,有畫面,但是沒有聲音。
Image找到 url 地址
要找到請求 url 肯定不能在在抓包里找到,可以嘗試看下網(wǎng)頁的源代碼。 復(fù)制一點 url 的信息,在網(wǎng)頁中查找,果然找到了信息。
Image 這些信息存在 window.__playinfo__ 里,然后把這個 json 提取出來,放到一個 json 文件里,再用 Firefox 打開。ImageImage 可以看到,視頻的 url 信息就在 'video' 這個鍵里面,id 指的就是請求的質(zhì)量,對應(yīng)著上面的 accept_quality,'id': 116指就是高清 1080p60。 視頻的在里面,那么音頻的 url 也在 audio這個鍵里面。提取也很容易,先把 window._playinfo 用正則表達式獲取到,再將其轉(zhuǎn)為 python 的 json 對象,然后就可以取出來了。
請求的 range 參數(shù)怎么設(shè)置呢?可以把其刪去,或者設(shè)為'range': 'bytes=0-',這樣就會請求一個全文件了。
## 請求視頻頁面,注意此時的請求頭不是同一個 res = session.get(url, headers=headers) text = res.text text = re.findall(r'<script>window.__playinfo__=(.*?)</script>', text)[0] json_data = json.loads(text) # 一般有多種格式可選,優(yōu)先選1080p,沒有就選720p。什么?沒有720p!這樣的視頻還有下載的必要? v_url = json_data['data']['dash']['video'][0]['baseUrl'] a_url = json_data['data']['dash']['audio'][0]['baseUrl']# 獲取準許 session.options(v_url, headers=headers_2) session.options(a_url, headers=headers_2)# 獲取數(shù)據(jù) video_content = session.get(v_url, headers=headers_1).content audio_content = session.get(a_url, headers=headers_1).content# 保存 with open('test.mp3', 'wb') as fp:fp.write(audio_content)with open('test.flv, 'wb') as fp:fp.write(video_content)彈幕
要獲取 B 站的彈幕,首先得知道 B 站的彈幕文件是從哪加載的。B 站的彈幕文件放在http://comment.bilibili.com/{cid}.xml,所以要獲取彈幕,就要獲取 B 站的視頻的 cid。 視頻的 cid 號在頁面也可以找到,所以用正則表達式提取。
Imagetext = res.text # 獲取cid cid = re.findall(r'cid=([d]+)', text)[0]# 獲取彈幕文件 xml = 'http://comment.bilibili.com/{}.xml'.format(cid) danmaku = session.get(xml, headers=headers) # 不設(shè)置的話,提取會亂碼 danmaku.encoding = 'utf-8'# 保存 with open(cid+'.xml', 'w', encoding='utf-8') as fp:fp.write(danmaku.text)封面
封面也可以在頁面找到。
text = res.text cover_url = re.findall(r'<meta data-vue-meta="true" itemprop="image" content="(.*?)">', text)[0]合并音視頻
這里使用 python 的 moviepy 庫,安裝命令pip install moviepy。
導(dǎo)入 from moviepy.editor import *,然后合并導(dǎo)出。
video = VideoFileClip(video_name) audio = AudioFileClip(audio_name) new_video = video.set_audio(audio) new_video.to_videofile("test.mp4", fps=24, remove_temp=False) os.remove(video_name) os.remove(audio_name)代碼
完全代碼查看我的 Github 地址
總結(jié)
以上是生活随笔為你收集整理的python 获取json中最大值_详细解析 Python 爬取 bilibili 的视频、弹幕以及封面的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linuxoracle静默安装应答文件修
- 下一篇: ffmpeg 将拆分的数据合成一帧_FF