1. 程式人生 > 程式設計 >python 批量下載bilibili視訊的gui程式

python 批量下載bilibili視訊的gui程式

執行效果:

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,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,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,pervent*465,23))
  root.update()
  pct.set(percent_str)



def Schedule(blocknum,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,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,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,quality)
    start_time = time.time()
    # down_video(video_list,page)
    # 定義執行緒
    th = threading.Thread(target=down_video,args=(video_list,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,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視訊的資料請關注我們其它相關文章!