b站视频爬取(python学习笔记)
亲爱的b站自从在2020年3月23日将av号改为BV号后,之前的很多爬虫已经不能用了,暂时没看懂这操作的意义何在,反而一大串大小写英文字母加数字的BV号让人很难记,反爬嘛?(滑稽),那就爬一下玩一玩。
在网上简单搜一下资料就可以了解到b站视频下载需要两个参数aid和cid
这里参考了这个案例
以这个视频为例:https://www.bilibili.com/video/BV1J7411R7nV?p=1&share_medium=iphone&share_plat=ios&share_source=QQ&share_tag=s_i×tamp=1597063419&unique_k=NRuL4t
开始爬取操作
首先可以看到视频的BV号为BV1J7411R7nV,要将其转化为av号,有以下两种方法:
方法1
(1)来到播放页面,按下F12打开浏览器控制台,一般控制台的名字为"控制台"或者"Console"
(2)在控制台输入aid,下面出现的数字就是av号(纯数字),如果没有出现的话可能需要按下回车.
方法2
借用知乎某位大佬的回答https://www.zhihu.com/question/381784377/answer/1099438784?utm_source=qq&utm_medium=social&utm_oi=916644358151897088&utm_content=sec
table='fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'
tr={}
for i in range(58):
tr[table[i]]=i
s=[11,10,3,8,4,6]
xor=177451812
add=8728348608
def dec(x):
r=0
for i in range(6):
r+=tr[x[s[i]]]*58**i
return (r-add)^xor
def enc(x):
x=(x^xor)+add
r=list('BV1 4 1 7 ')
for i in range(6):
r[s[i]]=table[x//58**i%58]
return ''.join(r)
作者:mcfx
链接:https://www.zhihu.com/question/381784377/answer/1099438784
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
av号和BV号的相互转化规则(牛牛牛),也可得出av号,这时候我们将BV号替换为av号:https://www.bilibili.com/video/av97325890
发现所打开的地址是一致的
接下来我们去找该视频对应的cid
F12打开开发者工具,点击network,刷新界面,耐心点找,得到以下数据,最后找到对应cid
得到aid和cid后我们要去寻找视频真正所在的url,B站的视频格式一般为flv
这时访问: https://api.bilibili.com/x/player/playurl?avid=97325890&cid=166148070&qn=32&type=&otype=json
继续F12打开可以看到
此时发现找到了视频真正的url
这里quality是视频质量
80: 高清 1080P
64: 高清 720P
32: 清晰 480P
16: 流畅 360P
下面只需要写代码将视频爬取下来即可
用到以下3个库
import requests
from lxml import html
import re
得到cid,可以从这个接口获得:https://api.bilibili.com/x/player/pagelist?aid=97325890&jsonp=jsonp
def get_cid(aid):
"""得到cid"""
header = {
'host': 'api.bilibili.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0) Gecko/20100101 Firefox/6.0'
}
url = "https://api.bilibili.com/x/player/pagelist?aid={aid}&jsonp=jsonp".format(aid=aid)
response = requests.get(url,headers=header).json()
return response["data"][0]["cid"] ,response["data"][0]["part"]
接着获取视频的flv文件
def get_flvurl(url):
"""获得视频真实flv地址"""
header = {'host': 'api.bilibili.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'}
response = requests.get(url,headers=header).json()
return response["data"]["durl"][0]["url"],response["data"]["durl"][0]["size"]
视频的存储
def save_movie(res,name):
"""保存视频"""
chunk_size = 1024
with open("{name}.flv".format(name = name),"wb") as f:
for data in res.iter_content(1024):
f.write(data)
结合python的tkinter库写了一个Gui界面,运行时发现按钮所绑定的方法处理起来需要点时间,然后导致界面会卡死,最后发现是单线程的原因,尝试引入多线程来解决问题,最后问题成功解决。
首先封装线程函数
def thread_it(func,*args):
"""将函数打包进线程内执行"""
# 创建
t = threading.Thread(target=func, args=args)
# 守护 !!!
t.setDaemon(True)
# 启动
t.start()
按钮绑定函数的写法
button1=Button(init_window,text="开始下载", bg="Cornsilk", width=8,height=1,command=lambda :thread_it(star))
完整代码如下(结合BV号转av号的规则)
from tkinter import *
from tkinter import messagebox
import tkinter.font as tkFont
import requests
from lxml import html
import re
import threading
table='fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'
tr={}
for i in range(58):
tr[table[i]]=i
s=[11,10,3,8,4,6]
xor=177451812
add=8728348608
def dec(x):
"""将BV号转化为av号"""
r=0
for i in range(6):
r+=tr[x[s[i]]]*58**i
return (r-add)^xor
def star():
url=text1.get(1.0,END)
url2 = "https://api.bilibili.com/x/player/playurl?avid={avid}&cid={cid}&qn=64&type=&otype=json"
headers2 = {
"host": "",
"Referer": "https://www.bilibili.com",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36"
}
BVid=re.findall("video/(.+)\?",url)
avid=dec(BVid[0])
text2.insert(1.0,'av号:'+str(avid)+'\n')
cid ,name = get_cid(avid)
text2.insert(3.0,'视频名称:'+name+'\n')
flv_url , size = get_flvurl(url2.format(avid=avid,cid=cid))
bulk = size / 1024 / 1024
text2.insert(5.0,"本视频大小为:%.2fM" % bulk+'\n')
h = re.findall("http://(.+)com",flv_url)
host = h[0]+"com"
headers2["host"] = host
res = requests.get(flv_url,headers=headers2,stream=True, verify=False)
if res.status_code==200:
text2.insert(7.0,'下载成功')
save_movie(res,name)
def get_cid(aid):
"""得到cid"""
header = {
'host': 'api.bilibili.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'
}
url = "https://api.bilibili.com/x/player/pagelist?aid={aid}&jsonp=jsonp".format(aid=aid)
response = requests.get(url,headers=header).json()
return response["data"][0]["cid"] ,response["data"][0]["part"]
def get_flvurl(url):
"""获得视频真实flv地址"""
header = {'host': 'api.bilibili.com',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36'}
response = requests.get(url,headers=header).json()
return response["data"]["durl"][0]["url"],response["data"]["durl"][0]["size"]
def save_movie(res,name):
"""保存视频"""
chunk_size = 1024
with open("{name}.flv".format(name = name),"wb") as f:
for data in res.iter_content(1024):
f.write(data)
def thread_it(func,*args):
"""将函数打包进线程内执行"""
# 创建
t = threading.Thread(target=func, args=args)
# 守护 !!!
t.setDaemon(True)
# 启动
t.start()
#实例化一个父界面
init_window = Tk()
#界面参数
init_window.title('B站视频下载器') #窗口名称
init_window.geometry('600x500+400+150') #窗口大小
init_window["bg"] = "mistyrose" #窗口颜色
init_window.attributes("-alpha",0.9) #窗口透明度
init_window.iconbitmap("billbill.ico") #窗口图标
#标签
label1=Label(init_window,text="请输入视频链接",bg='mistyrose',font=('隶书',13))
label1.place(relx=0.38,rely=0.05)
label2=Label(init_window,text="消息",bg='mistyrose',font=('隶书',13))
label2.place(relx=0.46,rely=0.37)
#文本框
text1=Text(init_window,width=60, height=4)
text1.place(relx=0.15,rely=0.12)
text2=Text(init_window,width=60, height=15)
text2.place(relx=0.15,rely=0.42)
#按钮
button1=Button(init_window,text="开始下载", bg="Cornsilk", width=8,height=1,command=lambda :thread_it(star))
button1.place(relx=0.44,rely=0.25)
#窗口进入循环
init_window.mainloop()
运行结果
然后打开程序所在的目录下
B站的视频格式都是flv格式,可以用格式工厂(实测有用)转化为MP4文件
其实tkinter库写一写这些功能较为简单的小程序还是挺方便的。
转载:https://blog.csdn.net/weixin_43875683/article/details/107922300