Py爬取b站視頻教程


​前言:我是一個爬蟲萌新,所以這里面有一些錯誤的歡迎指正.
本教程面向有一定Python基礎的人.
1.爬取普通的視頻.
首先,我們先來解析一下的網址,看看能不能直接獲取啥信息.
我們先打開視頻源代碼.如圖所示,我用的edge.

 

由於網址一般對應url,所以我們搜一下url.
當你搜到第4個的時候你應該會注意到什么------
沒錯,這是一串json.這個baseUrl屬於video的子項.也就是視頻的子項.音頻的json也是這種結構,只不過將video換成了audio.
順便提一句,b站的audio和video的url是分開的.所以我們需要分別獲取對應的url.
再繼續解析,我們可以獲取如下信息:
1.音頻和視頻都處於data子項下的dash子項,並且都是一個數組,且baseurl都存在數組的第1個元素.
2.可以看到,整串代碼是這樣的結構:

1 <script>
2 window.__playinfo__={
3     //json
4 }
5 </script>

那我們可以通過這串url獲得視頻所在的地址嗎?試一下

 

 

啊這......
是的,你沒有看錯,就是403.
還好不是404......
既然是403,那就意味着文件存在,只不過我們沒有權限訪問罷了.
這時候,我們得想辦法偽造權限.正好,我們有python.(假設你已經裝了requests庫)
那么,我們能不能通過偽造headers讓他請求成功而不顯示403呢?
我們可以嘗試一下---

1 headers={
2   "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
3   "referer": "https://message.bilibili.com/"#這段代碼的意思是你從哪兒獲得這個網址的(換一句話講,誰推薦你去訪問這個網址的).有了它就能夠正常訪問.
4 }

然后通過re來獲取json並且加載它,然后獲取音頻和視頻---

 1 import re
 2 import json
 3 
 4 #htmltxt為你之前通過requests.get()獲得的html代碼.
 5 r=re.findall(r'<script>window.__playinfo__=(.*?)</script>',htmltxt)[0]
 6 #之前我們講過這串json所在的代碼的結構,現在我們可以用正則分析了.(.*?):所有字符且匹配一次
 7 js=json.load(r)
 8 
 9 audiourl=js["data"]["dash"]["audio"][0]["baseUrl"]
10 videourl=js["data"]["dash"]["video"][0]["baseUrl"]

之后我們還要下載視頻---

 1 import requests
 2 import io
 3 
 4 headers={
 5   "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
 6    "referer": "https://message.bilibili.com/"#這段代碼的意思是你從哪兒獲得這個網址的(換一句話講,誰推薦你去訪問這個網址的).有了它就能夠正常訪問.
 7 }
 8 
 9 res=requests.get(url=audiourl,headers=headers)
10 with open("你的音頻名字.mp3","wb") as f:
11   f.write(res.content)
12 
13 res=requests.get(url=videourl,headers=headers)
14 with open("你的視頻名字.mp4","wb") as f:
15   f.write(res.content)

所以總合起來就是這樣的代碼---

 1 import requests
 2 import re
 3 import json
 4 import io
 5 
 6 headers={
 7   "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
 8    "referer": "https://message.bilibili.com/"#這段代碼的意思是你從哪兒獲得這個網址的(換一句話講,誰推薦你去訪問這個網址的).有了它就能夠正常訪問.
 9 }
10 
11 def get_url_html(url):
12   req=requests.get(url,headers=headers)
13   htmltext=req.text
14   get_json(htmltext)
15 
16 def get_json(htmltxt):
17   r=re.findall(r'<script>window.__playinfo__=(.*?)</script>',htmltxt)[0]
18   js=json.load(r)
19   audiourl=js["data"]["dash"]["audio"][0]["baseUrl"]
20   videourl=js["data"]["dash"]["video"][0]["baseUrl"]
21   download(audiourl,videourl)
22 
23 def download(audiourl,videourl):
24   res=requests.get(url=audiourl,headers=headers)
25   with open("你的音頻名字.mp3","wb") as f:
26     f.write(res.content)
27 
28   res=requests.get(url=videourl,headers=headers)
29   with open("你的視頻名字.mp4","wb") as f:
30     f.write(res.content)
31 
32 if __name__=="__main__":
33   get_url_html(url) #url對應網址

 

ok,現在你可以去嘗試一下,將url換成對應b站視頻網址,看看可不可以下載.

--END--

2.分頁視頻下載
有很多時候,視頻並非只有1集,而是有很多集.
那么遇見這種情況我們不可能運行n遍程序然后分別輸入網址進行下載吧
那么,遇到這種情況就沒辦法了嗎?
當然有.

我們隨意進入一個有很多集視頻的網站,然后隨意切換視頻的集數,觀察網址的變化.

 

通過多次實踐,我們可以得出結論:
視頻某一集對應的url:www.bilibili.com/video/bv號?p=集數
那么我們可不可以改動我們之前寫的程序讓他支持下載網址中的所有視頻呢?
當然可以.
我們可以通過獲得視頻的集數,然后通過for i in range(0,集數)來依次獲得視頻.

我們首先翻開視頻的源代碼.
然后我們輸入隨意一個視頻的標題搜索.

ok,搜索到了.存在part子項里面.
然后我們再來解析一下這個json,可以獲取以下信息:
1.pages是一個數組,里面存放着視頻的信息.
2.pages位於videoData中.也就是:

1 {
2   "videoData":{
3     "pages":[
4       {...}
5      ]
6    }
7  }

既然搜索到了,那我們還是沿用原來的方法,查找這個子項所對應的整個json及其代碼.

我們可以看到,這個script跟之前不一樣了,不是window.__playinfo__,而是window.__INITIAL_STATE__.
然后我們再去看這個json的末尾.

哦不!這串代碼的格式和之前也有點不一樣,因為這后面有一串小尾巴,而它不屬於json格式類型,故整個script代碼塊是無法通過json.loads()來加載的.
怎么辦呢?

在上面的教程中,我們用了正則表達式.那這兒我們也可以用正則表達式來篩選.
由於最后面的小尾巴不能包括,所以我們的正則表達式可以是這樣的:

1 r'<script>window.__INITIAL_STATE__=(.*?);\(function\(\)\{var s;\(s=document.currentScript\|\|document.scripts\[document.scripts.length-1\]\).parentNode.removeChild\(s\)\;}\(\)\);</script>'

ps:**注意轉義.例如"|" "()" "{}"等字符需要轉義.**
之后,我們可以考慮添加一個函數用來獲取json:

1 def getpagejson(txt): #txt:對應b站網頁源代碼
2     r=re.findall("<script>window.__INITIAL_STATE__=(.*?);\(function\(\)\{var s;\(s=document.currentScript\|\|document.scripts\[document.scripts.length-1\]\).parentNode.removeChild\(s\)\;}\(\)\);</script>",txt)[0]#獲得json
3     js=json.loads(r)#加載json
4     return js

ok,我們把解析json的函數也改一下---

def get_json(htmltxt):
    collections=[]
    r=re.findall(r'<script>window.__playinfo__=(.*?)</script>',htmltxt)[0]
    js=json.loads(r)
    jsobj=getpagejson(htmltxt)#獲得json
    page=jsobj["videodata"]["pages"]#獲取pages數組
    pagecount=len(page)#通過len獲取數組長度
    for i in range(0,pagecount):
        audiourl=js["data"]["dash"]["audio"][0]["baseUrl"]
        videourl=js["data"]["dash"]["video"][0]["baseUrl"]
        name=page[i]["part"]
        collections.append([audiourl,videourl,name])
    download(collections)

 

順便我們改一下下載的函數:

1 def download(collection):
2   for i in collection):#遍歷集合
3     res=requests.get(i[0],headers=headers)
4     with open(i[2]+" - audio.mp3","wb") as f:
5        f.write(res.content)
6   res=requests.get(i[1],headers=headers)
7     with open(i[2]+" - video.mp4","wb") as f:
8       f.write(res.content)

總代碼--

 1 import requests
 2 import re
 3 import io
 4 import json
 5 
 6 headers={
 7   "User-Agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
 8    "referer": "https://message.bilibili.com/"#這段代碼的意思是你從哪兒獲得這個網址的(換一句話講,誰推薦你去訪問這個網址的).有了它就能夠正常訪問.
 9 }
10 
11 def get_url_html(url):
12    req=requests.get(url,headers=headers)
13    htmltext=req.text
14    get_json(htmltext)
15 
16 def getpagejson(txt): #txt:對應b站網頁源代碼
17     r=re.findall("<script>window.__INITIAL_STATE__=(.*?);\(function\(\)\{var s;\(s=document.currentScript\|\|document.scripts\[document.scripts.length-1\]\).parentNode.removeChild\(s\)\;}\(\)\);</script>",txt)[0]#獲得json
18     js=json.loads(r)#加載json
19     return js
20 
21 def get_json(htmltxt):
22     collections=[]
23     r=re.findall(r'<script>window.__playinfo__=(.*?)</script>',htmltxt)[0]
24     js=json.loads(r)
25     jsobj=getpagejson(htmltxt)#獲得json
26     page=jsobj["videodata"]["pages"]#獲取pages數組
27     pagecount=len(page)#通過len獲取數組長度
28     for i in range(0,pagecount):
29         audiourl=js["data"]["dash"]["audio"][0]["baseUrl"]
30         videourl=js["data"]["dash"]["video"][0]["baseUrl"]
31         name=page[i]["part"]
32         collections.append([audiourl,videourl,name])
33     download(collections)
34 
35 def download(collection):
36    for i in collection:#遍歷集合
37       res=requests.get(i[0],headers=headers)
38       with open(i[2]+" - audio.mp3","wb") as f:
39          f.write(res.content)
40     res=requests.get(i[1],headers=headers)
41       with open(i[2]+" - video.mp4","wb") as f:
42         f.write(res.content)
43 
44 if __name__=="__main__":
45   get_url_html(url) #url對應網址

--END--

最后編輯於 10/24/2021 9:45

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM