python 批量下载bilibili视频的gui程序


Posted in Python onNovember 20, 2020

运行效果:

python 批量下载bilibili视频的gui程序

完整代码:

# !/usr/bin/python
# -*- coding:utf-8 -*-
# time: 2019/07/02--08:12
__author__ = 'Henry'


'''
项目: B站视频下载 - GUI版本
版本1: 加密API版,不需要加入cookie,直接即可下载1080p视频
20190422 - 增加多P视频单独下载其中一集的功能
20190702 - 增加视频多线程下载 速度大幅提升
20190711 - 增加GUI版本,可视化界面,操作更加友好
'''

import requests, time, hashlib, urllib.request, re, json
import imageio
imageio.plugins.ffmpeg.download()
from moviepy.editor import *
import os, sys, threading



from tkinter import *
from tkinter import ttk
from tkinter import StringVar
root=Tk()
start_time = time.time()

# 将输出重定向到表格
def print(theText):
  msgbox.insert(END,theText+'\n')


# 访问API地址
def get_play_list(start_url, cid, quality):
  entropy = 'rbMCKn@KuamXWlPMoJGsKcbiJKUfkPF_8dABscJntvqhRSETg'
  appkey, sec = ''.join([chr(ord(i) + 2) for i in entropy[::-1]]).split(':')
  params = 'appkey=%s&cid=%s&otype=json&qn=%s&quality=%s&type=' % (appkey, cid, quality, quality)
  chksum = hashlib.md5(bytes(params + sec, 'utf8')).hexdigest()
  url_api = 'https://interface.bilibili.com/v2/playurl?%s&sign=%s' % (params, chksum)
  headers = {
    'Referer': start_url, # 注意加上referer
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
  }
  # print(url_api)
  html = requests.get(url_api, headers=headers).json()
  # print(json.dumps(html))
  video_list = []
  for i in html['durl']:
    video_list.append(i['url'])
  # print(video_list)
  return video_list


# 下载视频
'''
 urllib.urlretrieve 的回调函数:
def callbackfunc(blocknum, blocksize, totalsize):
  @blocknum: 已经下载的数据块
  @blocksize: 数据块的大小
  @totalsize: 远程文件的大小
'''


def Schedule_cmd(blocknum, blocksize, totalsize):
  speed = (blocknum * blocksize) / (time.time() - start_time)
  # speed_str = " Speed: %.2f" % speed
  speed_str = " Speed: %s" % format_size(speed)
  recv_size = blocknum * blocksize

  # 设置下载进度条
  pervent = recv_size / totalsize
  percent_str = "%.2f%%" % (pervent * 100)
  download.coords(fill_line1,(0,0,pervent*465,23))
  root.update()
  pct.set(percent_str)



def Schedule(blocknum, blocksize, totalsize):
  speed = (blocknum * blocksize) / (time.time() - start_time)
  # speed_str = " Speed: %.2f" % speed
  speed_str = " Speed: %s" % format_size(speed)
  recv_size = blocknum * blocksize

  # 设置下载进度条
  f = sys.stdout
  pervent = recv_size / totalsize
  percent_str = "%.2f%%" % (pervent * 100)
  n = round(pervent * 50)
  s = ('#' * n).ljust(50, '-')
  print(percent_str.ljust(6, ' ') + '-' + speed_str)
  f.flush()
  time.sleep(2)
  # print('\r')


# 字节bytes转化K\M\G
def format_size(bytes):
  try:
    bytes = float(bytes)
    kb = bytes / 1024
  except:
    print("传入的字节格式不对")
    return "Error"
  if kb >= 1024:
    M = kb / 1024
    if M >= 1024:
      G = M / 1024
      return "%.3fG" % (G)
    else:
      return "%.3fM" % (M)
  else:
    return "%.3fK" % (kb)


# 下载视频
def down_video(video_list, title, start_url, page):
  num = 1
  print('[正在下载P{}段视频,请稍等...]:'.format(page) + title)
  currentVideoPath = os.path.join(sys.path[0], 'bilibili_video', title) # 当前目录作为下载目录
  for i in video_list:
    opener = urllib.request.build_opener()
    # 请求头
    opener.addheaders = [
      # ('Host', 'upos-hz-mirrorks3.acgvideo.com'), #注意修改host,不用也行
      ('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:56.0) Gecko/20100101 Firefox/56.0'),
      ('Accept', '*/*'),
      ('Accept-Language', 'en-US,en;q=0.5'),
      ('Accept-Encoding', 'gzip, deflate, br'),
      ('Range', 'bytes=0-'), # Range 的值要为 bytes=0- 才能下载完整视频
      ('Referer', start_url), # 注意修改referer,必须要加的!
      ('Origin', 'https://www.bilibili.com'),
      ('Connection', 'keep-alive'),
    ]
    urllib.request.install_opener(opener)
    # 创建文件夹存放下载的视频
    if not os.path.exists(currentVideoPath):
      os.makedirs(currentVideoPath)
    # 开始下载
    if len(video_list) > 1:
      urllib.request.urlretrieve(url=i, filename=os.path.join(currentVideoPath, r'{}-{}.flv'.format(title, num)),reporthook=Schedule_cmd) # 写成mp4也行 title + '-' + num + '.flv'
    else:
      urllib.request.urlretrieve(url=i, filename=os.path.join(currentVideoPath, r'{}.flv'.format(title)),reporthook=Schedule_cmd) # 写成mp4也行 title + '-' + num + '.flv'
    num += 1

# 合并视频(20190802新版)
def combine_video(title_list):
  video_path = os.path.join(sys.path[0], 'bilibili_video') # 下载目录
  for title in title_list:
    current_video_path = os.path.join(video_path ,title)
    if len(os.listdir(current_video_path)) >= 2:
      # 视频大于一段才要合并
      print('[下载完成,正在合并视频...]:' + title)
      # 定义一个数组
      L = []
      # 遍历所有文件
      for file in sorted(os.listdir(current_video_path), key=lambda x: int(x[x.rindex("-") + 1:x.rindex(".")])):
        # 如果后缀名为 .mp4/.flv
        if os.path.splitext(file)[1] == '.flv':
          # 拼接成完整路径
          filePath = os.path.join(current_video_path, file)
          # 载入视频
          video = VideoFileClip(filePath)
          # 添加到数组
          L.append(video)
      # 拼接视频
      final_clip = concatenate_videoclips(L)
      # 生成目标视频文件
      final_clip.to_videofile(os.path.join(current_video_path, r'{}.mp4'.format(title)), fps=24, remove_temp=False)
      print('[视频合并完成]' + title)
    else:
      # 视频只有一段则直接打印下载完成
      print('[视频合并完成]:' + title)

def do_prepare(inputStart,inputQuality):
  # 清空进度条
  download.coords(fill_line1,(0,0,0,23))
  pct.set('0.00%')
  root.update()
  # 清空文本栏
  msgbox.delete('1.0','end')
  start_time = time.time()
  # 用户输入av号或者视频链接地址
  print('*' * 30 + 'B站视频下载小助手' + '*' * 30)
  start = inputStart
  if start.isdigit() == True: # 如果输入的是av号
    # 获取cid的api, 传入aid即可
    start_url = 'https://api.bilibili.com/x/web-interface/view?aid=' + start
  else:
    # https://www.bilibili.com/video/av46958874/?spm_id_from=333.334.b_63686965665f7265636f6d6d656e64.16
    start_url = 'https://api.bilibili.com/x/web-interface/view?aid=' + re.search(r'/av(\d+)/*', start).group(1)

  # 视频质量
  # <accept_format><![CDATA[flv,flv720,flv480,flv360]]></accept_format>
  # <accept_description><![CDATA[高清 1080P,高清 720P,清晰 480P,流畅 360P]]></accept_description>
  # <accept_quality><![CDATA[80,64,32,16]]></accept_quality>
  quality = inputQuality
  # 获取视频的cid,title
  headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36'
  }
  html = requests.get(start_url, headers=headers).json()
  data = html['data']
  cid_list = []
  if '?p=' in start:
    # 单独下载分P视频中的一集
    p = re.search(r'\?p=(\d+)',start).group(1)
    cid_list.append(data['pages'][int(p) - 1])
  else:
    # 如果p不存在就是全集下载
    cid_list = data['pages']
  # print(cid_list)
  # 创建线程池
  threadpool = []
  title_list = []
  for item in cid_list:
    cid = str(item['cid'])
    title = item['part']
    title = re.sub(r'[\/\\:*?"<>|]', '', title) # 替换为空的
    print('[下载视频的cid]:' + cid)
    print('[下载视频的标题]:' + title)
    title_list.append(title)
    page = str(item['page'])
    start_url = start_url + "/?p=" + page
    video_list = get_play_list(start_url, cid, quality)
    start_time = time.time()
    # down_video(video_list, title, start_url, page)
    # 定义线程
    th = threading.Thread(target=down_video, args=(video_list, title, start_url, page))
    # 将线程加入线程池
    threadpool.append(th)

  # 开始线程
  for th in threadpool:
    th.start()
  # 等待所有线程运行完毕
  for th in threadpool:
    th.join()
  
  # 最后合并视频
  combine_video(title_list)

  end_time = time.time() # 结束时间
  print('下载总耗时%.2f秒,约%.2f分钟' % (end_time - start_time, int(end_time - start_time) / 60))

  # 如果是windows系统,下载完成后打开下载目录
  currentVideoPath = os.path.join(sys.path[0], 'bilibili_video') # 当前目录作为下载目录
  if (sys.platform.startswith('win')):
    os.startfile(currentVideoPath)



def thread_it(func, *args):
  '''将函数打包进线程'''
  # 创建
  t = threading.Thread(target=func, args=args) 
  # 守护 !!!
  t.setDaemon(True) 
  # 启动
  t.start()


if __name__ == "__main__":
  # 设置标题
  root.title('B站视频下载小助手-GUI')
  # 设置ico
  root.iconbitmap('./Pic/favicon.ico')
  # 设置Logo
  photo = PhotoImage(file='./Pic/logo.png')
  logo = Label(root,image=photo)
  logo.pack()
  # 各项输入栏和选择框
  inputStart = Entry(root,bd=4,width=600)
  labelStart=Label(root,text="请输入您要下载的B站av号或者视频链接地址:") # 地址输入
  labelStart.pack(anchor="w")
  inputStart.pack()
  labelQual = Label(root,text="请选择您要下载视频的清晰度") # 清晰度选择
  labelQual.pack(anchor="w")
  inputQual = ttk.Combobox(root,state="readonly")
  # 可供选择的表
  inputQual['value']=('1080P','720p','480p','360p')
  # 对应的转换字典
  keyTrans=dict()
  keyTrans['1080P']='80'
  keyTrans['720p']='64'
  keyTrans['480p']='32'
  keyTrans['360p']='16'
  # 初始值为720p
  inputQual.current(1)
  inputQual.pack()
  confirm = Button(root,text="开始下载",command=lambda:thread_it(do_prepare,inputStart.get(), keyTrans[inputQual.get()] ))
  msgbox = Text(root)
  msgbox.insert('1.0',"对于单P视频:直接传入B站av号或者视频链接地址\n(eg: 49842011或者https://www.bilibili.com/video/av49842011)\n对于多P视频:\n1.下载全集:直接传入B站av号或者视频链接地址\n(eg: 49842011或者https://www.bilibili.com/video/av49842011)\n2.下载其中一集:传入那一集的视频链接地址\n(eg: https://www.bilibili.com/video/av19516333/?p=2)")
  msgbox.pack()
  download=Canvas(root,width=465,height=23,bg="white")
  # 进度条的设置
  labelDownload=Label(root,text="下载进度")
  labelDownload.pack(anchor="w")
  download.pack()
  fill_line1 = download.create_rectangle(0, 0, 0, 23, width=0, fill="green")
  pct=StringVar()
  pct.set('0.0%')
  pctLabel = Label(root,textvariable=pct)
  pctLabel.pack()
  root.geometry("600x800")
  confirm.pack()
  # GUI主循环
  root.mainloop()

以上就是python 批量下载bilibili视频的gui程序的详细内容,更多关于python 批量下载bilibili视频的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python实现一个简单的MySQL类
Jan 07 Python
Python的collections模块中的OrderedDict有序字典
Jul 07 Python
Django实现组合搜索的方法示例
Jan 23 Python
python生成带有表格的图片实例
Feb 03 Python
python获取当前文件路径以及父文件路径的方法
Jul 10 Python
Python人工智能之路 之PyAudio 实现录音 自动化交互实现问答
Aug 13 Python
pytorch 使用单个GPU与多个GPU进行训练与测试的方法
Aug 19 Python
Python paramiko模块使用解析(实现ssh)
Aug 30 Python
解决TensorFlow GPU版出现OOM错误的问题
Feb 03 Python
Python实现屏幕录制功能的代码
Mar 02 Python
Python绘图之二维图与三维图详解
Aug 04 Python
python 实现的截屏工具
May 08 Python
Python ellipsis 的用法详解
Nov 20 #Python
python 动态渲染 mysql 配置文件的示例
Nov 20 #Python
Python paramiko使用方法代码汇总
Nov 20 #Python
分享一枚pycharm激活码适用所有pycharm版本我的pycharm2020.2.3激活成功
Nov 20 #Python
python使用requests库爬取拉勾网招聘信息的实现
Nov 20 #Python
python爬取天气数据的实例详解
Nov 20 #Python
python爬取招聘要求等信息实例
Nov 20 #Python
You might like
PHP中实现图片的锐化
2006/10/09 PHP
php数组函数序列之array_keys() - 获取数组键名
2011/10/30 PHP
WordPress中is_singular()函数简介
2015/02/05 PHP
php简单处理XML数据的方法示例
2017/05/19 PHP
javascript 在网页中的运用(asp.net)
2009/11/23 Javascript
无缝滚动改进版支持上下左右滚动(封装成函数)
2012/12/04 Javascript
JS判断不同分辨率调用不同的CSS样式文件实现思路及测试代码
2013/01/23 Javascript
jquery实现下拉菜单的二级联动利用json对象从DB取值显示联动
2014/03/27 Javascript
JavaScript中的变量作用域介绍
2014/12/31 Javascript
JavaScript常用判断写法大全(推荐)
2016/05/30 Javascript
JavaScript中的Reflect对象详解(ES6新特性)
2016/07/22 Javascript
AngularJS基础 ng-mouseenter 指令示例代码
2016/08/02 Javascript
ES6概念 Symbol.keyFor()方法
2016/12/25 Javascript
微信小程序 picker-view 组件详解及简单实例
2017/01/10 Javascript
jquery实现自定义图片裁剪功能【推荐】
2017/03/08 Javascript
JS创建Tag标签的方法详解
2017/06/09 Javascript
vue 解决在微信内置浏览器中调用支付宝支付的情况
2020/11/09 Javascript
详解Java中String JSONObject JSONArray List转换
2020/11/13 Javascript
Nest.js环境变量配置与序列化详解
2021/02/21 Javascript
调试Python程序代码的几种方法总结
2015/04/28 Python
Python守护进程用法实例分析
2015/06/04 Python
简介Python设计模式中的代理模式与模板方法模式编程
2016/02/02 Python
Python正则简单实例分析
2017/03/21 Python
Django3.0 异步通信初体验(小结)
2019/12/04 Python
Python Print实现在输出中插入变量的例子
2019/12/25 Python
如何解决安装python3.6.1失败
2020/07/01 Python
css3实例教程 一款纯css3实现的环形导航菜单
2014/10/20 HTML / CSS
css3 伪类选择器快速复习小结
2019/09/10 HTML / CSS
美国最大的团购网站:Groupon
2016/07/23 全球购物
韩国美国时尚服装和美容在线全球市场:KOODING
2018/11/07 全球购物
获取邓白氏信用报告:Dun & Bradstreet
2019/01/22 全球购物
大专自我鉴定范文
2013/10/23 职场文书
中专生求职自荐信范文
2013/12/22 职场文书
2014党员批评和自我批评思想汇报
2014/09/21 职场文书
Android开发实现极为简单的QQ登录页面
2022/04/24 Java/Android
Typescript类型系统FLOW静态检查基本规范
2022/05/25 Javascript