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实现面向对像的ASP程序实例
Nov 10 Python
Python 3.7新功能之dataclass装饰器详解
Apr 21 Python
python实现求两个字符串的最长公共子串方法
Jul 20 Python
python中tkinter的应用:修改字体的实例讲解
Jul 17 Python
django之自定义软删除Model的方法
Aug 14 Python
Python编程快速上手——PDF文件操作案例分析
Feb 28 Python
Python MySQLdb 执行sql语句时的参数传递方式
Mar 04 Python
基于Python实现全自动下载抖音视频
Nov 06 Python
Jupyter Notebook添加代码自动补全功能的实现
Jan 07 Python
python调试工具Birdseye的使用教程
May 25 Python
用Python进行栅格数据的分区统计和批量提取
May 27 Python
python内置进制转换函数的操作
Jun 02 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的一个完整SMTP类(解决邮件服务器需要验证时的问题)
2006/10/09 PHP
ThinkPHP中数据操作案例分析
2015/09/27 PHP
PHP通过get方法获得form表单数据方法总结
2018/09/12 PHP
PHP getName()函数讲解
2019/02/03 PHP
PHP日期和时间函数的使用示例详解
2020/08/06 PHP
escape编码与unescape解码汉字出现乱码的解决方法
2014/07/02 Javascript
浅析Node.js查找字符串功能
2014/09/03 Javascript
Nodejs实现多人同时在线移动鼠标的小游戏分享
2014/12/06 NodeJs
解决jquery无法找到其他父级子集问题的方法
2016/05/10 Javascript
深入剖析javascript中的exec与match方法
2016/05/18 Javascript
用JavaScript实现让浏览器停止载入页面的方法
2017/01/19 Javascript
js实现登录框鼠标拖拽效果
2017/03/09 Javascript
用Vue-cli搭建的项目中引入css报错的原因分析
2017/07/20 Javascript
JS+canvas画一个圆锥实例代码
2017/12/13 Javascript
JS实现textarea通过换行或者回车把多行数字分割成数组并且去掉数组中空的值
2018/10/29 Javascript
简单了解JavaScript中的执行上下文和堆栈
2019/06/24 Javascript
Vue图片浏览组件v-viewer用法分析【支持旋转、缩放、翻转等操作】
2019/11/04 Javascript
Javascript实现简易天数计算器
2020/05/18 Javascript
微信小程序实现多选框功能的实例代码
2020/06/24 Javascript
Python使用Paramiko模块编写脚本进行远程服务器操作
2016/05/05 Python
python将文本分每两行一组并保存到文件
2018/03/19 Python
详解使用Python下载文件的几种方法
2019/10/13 Python
Python第三方库的几种安装方式(小结)
2020/04/03 Python
keras实现VGG16 CIFAR10数据集方式
2020/07/07 Python
Python 使用生成器代替线程的方法
2020/08/04 Python
YesStyle美国/全球:购买亚洲时装、美容化妆品和生活百货
2017/01/16 全球购物
俄罗斯花园种植材料批发和零售网上商店:Беккер
2019/07/22 全球购物
Speedo速比涛法国官方网站:泳衣、泳镜、泳帽、泳裤
2019/07/30 全球购物
为什么要使用servlet
2016/01/17 面试题
优秀毕业生推荐信
2013/11/02 职场文书
咖啡店的创业计划书,让你hold不住
2014/01/03 职场文书
《临死前的严监生》教学反思
2014/02/13 职场文书
项目总经理岗位职责
2014/02/14 职场文书
应届生求职自荐信范文
2014/04/07 职场文书
党支部工作总结2015
2015/04/01 职场文书
python实现简单的名片管理系统
2021/04/26 Python