前言:我是一个爬虫萌新,所以这里面有一些错误的欢迎指正.
本教程面向有一定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