飞道的博客

python爬虫之b站视频下载(python学习笔记)

521人阅读  评论(0)

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&timestamp=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
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场