1. 程式人生 > 其它 >Python爬蟲實戰,DecryptLogin模組,Python模擬登入實現載B站指定UP主的所有視訊

Python爬蟲實戰,DecryptLogin模組,Python模擬登入實現載B站指定UP主的所有視訊

前言

下載B站上指定的UP主所上傳的所有視訊。廢話不多說,讓我們愉快地開始吧~

開發工具

** Python版本:**3.6.4

** 相關模組:**

DecryptLogin模組;

argparse模組;

以及一些python自帶的模組。

環境搭建

安裝Python並新增到環境變數,pip安裝需要的相關模組即可。

原理簡介

這裡簡單介紹一下原理吧,因為B站只有在使用者登入狀態下才能下載1080P的視訊。所以我們首先要利用我開源的DecryptLogin庫模擬登入B站(畢竟低畫質的視訊看著不舒服),這個很簡單,幾行程式碼就搞定了:

from DecryptLogin import login
_, session = login.Login().bilibili(username, password)

接下來就是老生常談的抓包了,進入某個UP主的主頁:

首先我們來抓取UP主的基本資訊(抓取UP主所有的視訊時完全不需要這些資訊,只是用來確定你輸入的userid是否和你想抓取的使用者一致用的),這個很簡單,重新整理一下發現:

幾行程式碼就搞定了:

'''根據userid獲得該使用者基本資訊'''
def __getUserInfo(self, userid):
  params = {'mid': userid, 'jsonp': 'jsonp'}
  res = self.session.get(self.user_info_url, params=params, headers=self.headers)
  res_json = res.json()
  user_info = {
          '使用者名稱': res_json['data']['name'],
          '性別': res_json['data']['sex'],
          '個性簽名': res_json['data']['sign'],
          '使用者等級': res_json['data']['level'],
          '生日': res_json['data']['birthday']
        }
  return user_info

接下來就是下載該UP主的所有視訊了,因為感覺搞起來可能比較費時間(年底了時間比較緊張T_T),所以就直接去you-get的原始碼裡找了找有沒有現成的API了,發現主要需要以下三個介面:

'https://space.bilibili.com/ajax/member/getSubmitVideos'
'https://api.bilibili.com/x/web-interface/view'
'https://api.bilibili.com/x/player/playurl'

第一,二個介面用來獲取使用者所有的視訊的基本資訊,根據這些資訊然後再利用第三個介面來獲得視訊的下載連結,最後利用呼叫aria2c下載這些視訊就OK了。具體而言,程式碼實現如下:

'''下載目標使用者的所有視訊'''
def __downloadVideos(self, userid):
  if not os.path.exists(userid):
    os.mkdir(userid)
  # 非會員使用者只能下載到高清1080P
  quality = [('16', '流暢 360P'),
        ('32', '清晰 480P'),
        ('64', '高清 720P'),
        ('74', '高清 720P60'),
        ('80', '高清 1080P'),
        ('112', '高清 1080P+'),
        ('116', '高清 1080P60')][-3]
  # 獲得使用者的視訊基本資訊
  video_info = {'aids': [], 'cid_parts': [], 'titles': [], 'links': [], 'down_flags': []}
  params = {'mid': userid, 'pagesize': 30, 'tid': 0, 'page': 1, 'order': 'pubdate'}
  while True:
    res = self.session.get(self.submit_videos_url, headers=self.headers, params=params)
    res_json = res.json()
    for item in res_json['data']['vlist']:
      video_info['aids'].append(item['aid'])
    if len(video_info['aids']) < int(res_json['data']['count']):
      params['page'] += 1
    else:
      break
  for aid in video_info['aids']:
    params = {'aid': aid}
    res = self.session.get(self.view_url, headers=self.headers, params=params)
    cid_part = []
    for page in res.json()['data']['pages']:
      cid_part.append([page['cid'], page['part']])
    video_info['cid_parts'].append(cid_part)
    title = res.json()['data']['title']
    title = re.sub(r"[‘’\/\\\:\*\?\"\<\>\|\s']", ' ', title)
    video_info['titles'].append(title)
  print('共獲取到使用者ID<%s>的<%d>個視訊...' % (userid, len(video_info['titles'])))
  for idx in range(len(video_info['titles'])):
    aid = video_info['aids'][idx]
    cid_part = video_info['cid_parts'][idx]
    link = []
    down_flag = False
    for cid, part in cid_part:
      params = {'avid': aid, 'cid': cid, 'qn': quality, 'otype': 'json', 'fnver': 0, 'fnval': 16}
      res = self.session.get(self.video_player_url, params=params, headers=self.headers)
      res_json = res.json()
      if 'dash' in res_json['data']:
        down_flag = True
        v, a = res_json['data']['dash']['video'][0], res_json['data']['dash']['audio'][0]
        link_v = [v['baseUrl']]
        link_a = [a['baseUrl']]
        if v['backup_url']:
          for item in v['backup_url']:
            link_v.append(item)
        if a['backup_url']:
          for item in a['backup_url']:
            link_a.append(item)
        link = [link_v, link_a]
      else:
        link = [res_json['data']['durl'][-1]['url']]
        if res_json['data']['durl'][-1]['backup_url']:
          for item in res_json['data']['durl'][-1]['backup_url']:
            link.append(item)
      video_info['links'].append(link)
      video_info['down_flags'].append(down_flag)
  # 開始下載
  out_pipe_quiet = subprocess.PIPE
  out_pipe = None
  aria2c_path = os.path.join(os.getcwd(), 'tools/aria2c')
  ffmpeg_path = os.path.join(os.getcwd(), 'tools/ffmpeg')
  for idx in range(len(video_info['titles'])):
    title = video_info['titles'][idx]
    aid = video_info['aids'][idx]
    down_flag = video_info['down_flags'][idx]
    print('正在下載視訊<%s>...' % title)
    if down_flag:
      link_v, link_a = video_info['links'][idx]
      # --視訊
      url = '"{}"'.format('" "'.join(link_v))
      command = '{} -c -k 1M -x {} -d "{}" -o "{}" --referer="https://www.bilibili.com/video/av{}" {} {}'
      command = command.format(aria2c_path, len(link_v), userid, title+'.flv', aid, "", url)
      process = subprocess.Popen(command, stdout=out_pipe, stderr=out_pipe, shell=True)
      process.wait()
      # --音訊
      url = '"{}"'.format('" "'.join(link_a))
      command = '{} -c -k 1M -x {} -d "{}" -o "{}" --referer="https://www.bilibili.com/video/av{}" {} {}'
      command = command.format(aria2c_path, len(link_v), userid, title+'.aac', aid, "", url)
      process = subprocess.Popen(command, stdout=out_pipe, stderr=out_pipe, shell=True)
      process.wait()
      # --合併
      command = '{} -i "{}" -i "{}" -c copy -f mp4 -y "{}"'
      command = command.format(ffmpeg_path, os.path.join(userid, title+'.flv'), os.path.join(userid, title+'.aac'), os.path.join(userid, title+'.mp4'))
      process = subprocess.Popen(command, stdout=out_pipe, stderr=out_pipe_quiet, shell=True)
      process.wait()
      os.remove(os.path.join(userid, title+'.flv'))
      os.remove(os.path.join(userid, title+'.aac'))
    else:
      link = video_info['links'][idx]
      url = '"{}"'.format('" "'.join(link))
      command = '{} -c -k 1M -x {} -d "{}" -o "{}" --referer="https://www.bilibili.com/video/av{}" {} {}'
      command = command.format(aria2c_path, len(link), userid, title+'.flv', aid, "", url)
      process = subprocess.Popen(command, stdout=out_pipe, stderr=out_pipe, shell=True)
      process.wait()
      os.rename(os.path.join(userid, title+'.flv'), os.path.join(userid, title+'.mp4'))
  print('所有視訊下載完成, 該使用者所有視訊儲存在<%s>資料夾中...' % (userid))

文章到這裡就結束了,感謝你的觀看,關注我每天分享Python模擬登入系列,下篇文章分享網易雲個人歌單下載器

為了感謝讀者們,我想把我最近收藏的一些程式設計乾貨分享給大家,回饋每一個讀者,希望能幫到你們。

乾貨主要有:

① 2000多本Python電子書(主流和經典的書籍應該都有了)

② Python標準庫資料(最全中文版)

③ 專案原始碼(四五十個有趣且經典的練手專案及原始碼)

④ Python基礎入門、爬蟲、web開發、大資料分析方面的視訊(適合小白學習)

⑤ Python學習路線圖(告別不入流的學習)

⑥ 兩天的Python爬蟲訓練營直播許可權

All done~完整原始碼+乾貨詳見個人簡介或者私信獲取相關檔案。。