爬取思路與小結
- 在查找程序中,查看源代碼,bv號可以轉化為av號,ss號可以轉化為ep號
即可以相互轉換,如圖,圖中一個視頻就有ep號,av,bv,cv號,代碼中利用了bv號可以轉化為av號,ss號可以轉化為ep號

- 只能對網頁里已有的鏈接進行爬取,無法爬取大會員視頻,
- 打包Python
- pip install pyinstaller
- cd 到bilbili_down.py檔案所在位置
- 在cmd終端直接使用 pyinstaller bilbili_down.py
- 這是我已經打包好的:感興趣的老鐵可以試一下功能(第一次打包不小心把我自己的快捷方式打包里面去了😅,老鐵們要打開真正的exe檔案啊,不然可能無法保存視頻😯):https://nmydt.lanzous.com/iMkpUlufosd
代碼
import json,requests,os,re,shutil,ssl,time
from concurrent.futures import ThreadPoolExecutor
from lxml import etree
## 設定請求頭等引數,防止被反爬
headers = {
'Accept': '*/*',
'Accept-Language': 'en-US,en;q=0.5',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.116 Safari/537.36'
}
params = {
'from': 'search',
'seid': '9698329271136034665'
}
def re_video_info(text, pattern):
'''利用正則運算式匹配出視頻資訊并轉化成json'''
match = re.search(pattern, text)
return json.loads(match.group(1))
def create_folder(aid):
'''創建檔案夾'''
if not os.path.exists(aid):
os.mkdir(aid)
def remove_move_file(aid):
'''洗掉和移動檔案'''
file_list = os.listdir('./')
for file in file_list:
## 移除臨時檔案
if file.endswith('_video.mp4'):
os.remove(file)
pass
elif file.endswith('_audio.mp4'):
os.remove(file)
pass
## 保存最終的視頻檔案
elif file.endswith('.mp4'):
if os.path.exists(aid + '/' + file):
os.remove(aid + '/' + file)
shutil.move(file, aid)
def BV_move_av(url):
r=requests.get(url)
html = etree.HTML(r.text)
av_url = html.xpath('/html/head/meta[@itemprop="url"]/@content')[0]
aid = re.search('\d+',av_url).group(0)
return aid
def ss_move_ep(url):
r=requests.get(url)
url = "https://www.bilibili.com/bangumi/play/ep"+str(json.loads(re.search('"epList\":(.*?),\"epI',r.text).group(1))[0]['id'])
return url
def download_video_batch(referer_url, video_url, audio_url, video_name, index):
'''批量下載系列視頻'''
## 更新請求頭
headers.update({"Referer": referer_url})
## 獲取檔案名
short_name = video_name.split('/')[2]
print("%d.\t視頻下載開始:%s" % (index, short_name))
## 下載并保存視頻
video_content = requests.get(video_url, headers=headers)
print('%d.\t%s\t視頻大小:' % (index, short_name),
round(int(video_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')
received_video = 0
with open('%s_video.mp4' % video_name, 'ab') as output:
headers['Range'] = 'bytes=' + str(received_video) + '-'
response = requests.get(video_url, headers=headers)
output.write(response.content)
## 下載并保存音頻
audio_content = requests.get(audio_url, headers=headers)
print('%d.\t%s\t音頻大小:' % (index, short_name),
round(int(audio_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')
received_audio = 0
with open('%s_audio.mp4' % video_name, 'ab') as output:
headers['Range'] = 'bytes=' + str(received_audio) + '-'
response = requests.get(audio_url, headers=headers)
output.write(response.content)
received_audio += len(response.content)
return video_name, index
def download_video_single(referer_url, video_url, audio_url, video_name):
'''單個視頻下載'''
## 更新請求頭
headers.update({"Referer": referer_url})
print("視頻下載開始:%s" % video_name)
## 下載并保存視頻
video_content = requests.get(video_url, headers=headers)
print('%s\t視頻大小:' % video_name, round(int(video_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')
received_video = 0
with open('%s_video.mp4' % video_name, 'ab') as output:
headers['Range'] = 'bytes=' + str(received_video) + '-'
response = requests.get(video_url, headers=headers)
output.write(response.content)
## 下載并保存音頻
audio_content = requests.get(audio_url, headers=headers)
print('%s\t音頻大小:' % video_name, round(int(audio_content.headers.get('content-length', 0)) / 1024 / 1024, 2), '\tMB')
received_audio = 0
with open('%s_audio.mp4' % video_name, 'ab') as output:
headers['Range'] = 'bytes=' + str(received_audio) + '-'
response = requests.get(audio_url, headers=headers)
output.write(response.content)
received_audio += len(response.content)
print("視頻下載結束:%s" % video_name)
video_audio_merge_single(video_name)
def video_audio_merge_batch(result):
'''使用ffmpeg批量視頻音頻合并'''
video_name = result.result()[0]
index = result.result()[1]
import subprocess
video_final = video_name.replace('video', 'video_final')
command = 'ffmpeg -i "%s_video.mp4" -i "%s_audio.mp4" -c copy "%s.mp4" -y -loglevel quiet' % (
video_name, video_name, video_final)
subprocess.Popen(command, shell=True)
print("%d.\t視頻下載結束:%s" % (index, video_name.split('/')[2]))
def video_audio_merge_single(video_name):
'''使用ffmpeg單個視頻音頻合并'''
print("視頻合成開始:%s" % video_name)
import subprocess
command = 'ffmpeg -i "%s_video.mp4" -i "%s_audio.mp4" -c copy "%s.mp4" -y -loglevel quiet' % (
video_name, video_name, video_name)
subprocess.Popen(command, shell=True)
print("視頻合成結束:%s" % video_name)
def batch_download():
'''使用多執行緒批量下載視頻'''
## 提示輸入需要下載的系列視頻對應的id
aid = input(
"請輸入要下載的視頻id(舉例:鏈接https://www.bilibili.com/video/BV1Ke411W71L?p=1中id為1Ke411W71L\nhttps://www.bilibili.com/video/av91748877?p=1中id為91748877,默認為91748877)")
if aid:
if re.search('\D',aid):
aid = BV_move_av('https://www.bilibili.com/video/BV'+aid)
else:
aid = '91748877'
## 提示選擇清晰度
quality = input('請選擇清晰度(1代表高清,2代表清晰,3代表流暢),默認高清\t')
if quality == '2':
pass
elif quality == '3':
pass
else:
quality = '1'
acc_quality = int(quality) - 1
## ssl模塊,處理https請求失敗問題,生成證書背景關系
ssl._create_default_https_context = ssl._create_unverified_context
## 獲取視頻主題
url = 'https://www.bilibili.com/video/av{}?p=1'.format(aid)
html = etree.HTML(requests.get(url, params=params, headers=headers).text)
title = html.xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]
print('您即將下載的視頻系列是:', title)
## 創建臨時檔案夾
create_folder('video')
create_folder('video_final')
## 定義一個執行緒池,大小為3
pool = ThreadPoolExecutor(3)
## 通過api獲取視頻資訊
res_json = requests.get('https://api.bilibili.com/x/player/pagelist?aid={}'.format(aid)).json()
video_name_list = res_json['data']
print('共下載視頻{}個'.format(len(video_name_list)))
for i, video_content in enumerate(video_name_list):
video_name = ('./video/' + video_content['part']).replace(" ", "-")
origin_video_url = 'https://www.bilibili.com/video/av{}'.format(aid) + '?p=%d' % (i + 1)
## 請求視頻,獲取資訊
res = requests.get(origin_video_url, headers=headers)
## 決議出視頻詳情的json
video_info_temp = re_video_info(res.text, '__playinfo__=(.*?)</script><script>')
video_info = {}
## 獲取視頻品質
quality = video_info_temp['data']['accept_description'][acc_quality]
## 獲取視頻時長
video_info['duration'] = video_info_temp['data']['dash']['duration']
## 獲取視頻鏈接
video_url = video_info_temp['data']['dash']['video'][acc_quality]['baseUrl']
## 獲取音頻鏈接
audio_url = video_info_temp['data']['dash']['audio'][acc_quality]['baseUrl']
## 計算視頻時長
video_time = int(video_info.get('duration', 0))
video_minute = video_time // 60
video_second = video_time % 60
print('{}.\t當前視頻清晰度為{},時長{}分{}秒'.format(i + 1, quality, video_minute, video_second))
## 將任務加入執行緒池,并在任務完成后回呼完成視頻音頻合并
pool.submit(download_video_batch, origin_video_url, video_url, audio_url, video_name, i + 1).add_done_callback(
video_audio_merge_batch)
pool.shutdown(wait=True)
time.sleep(5)
## 整理視頻資訊
if os.path.exists(title):
shutil.rmtree(title)
os.rename('video_final', title)
try:
shutil.rmtree('video')
except:
shutil.rmtree('video')
def batch_down_fanju():
'''使用多執行緒批量下載番劇'''
## 提示輸入需要下載的系列視頻對應的id
url = input('請輸入下載的番劇鏈接:(支持ep,ss加數字型別,如https://www.bilibili.com/bangumi/play/ep21434,\nhttps://www.bilibili.com/bangumi/play/ss28763/)')
if not url:
url = 'https://www.bilibili.com/bangumi/play/ep21434'
if 'ep' not in url:
url = ss_move_ep(url)
r = requests.get(url)
r.close()
html = etree.HTML(r.text)
title = html.xpath('//*[@id="media_module"]/div/a')[0].text
data = json.loads(re.search('__playinfo__=(.*?)</script>',r.text).group(1))
catalog = json.loads(re.search('__INITIAL_STATE__=(.*?)\;\(function()',r.text).group(1))
# name = ''.join(catalog['epList'][0]['titleFormat']+' '+catalog['epList'][0]['longTitle'])
all_num = len(catalog['epList'])
urls=[]
re.search('\D+',url).group(0)
id = int(re.search('\d+',url).group(0))
url_half = re.search('\D+',url).group(0)
[urls.append(url_half+str(id+i)) for i in range(all_num)]
quality = input('請選擇清晰度(1代表高清,2代表清晰,3代表流暢),默認高清\t')
if quality == '2':
pass
elif quality == '3':
pass
else:
quality = '1'
acc_quality = int(quality) - 1
## 創建臨時檔案夾
create_folder('video')
create_folder('video_final')
pool = ThreadPoolExecutor(3)
for i,ul in enumerate(urls):
r = requests.get(ul)
r.close()
try:
data = json.loads(re.search('__playinfo__=(.*?)</script>',r.text).group(1))
except Exception as e:
break
name = ''.join(catalog['epList'][i]['titleFormat']+' '+catalog['epList'][i]['longTitle'])
video_name = ('./video/' + name).replace(" ", "-")
duration = data['data']['dash']['duration']
quality = data['data']['support_formats'][acc_quality]['display_desc']
video_url = data['data']['dash']['video'][acc_quality]['backupUrl'][0]
audio_url = data['data']['dash']['audio'][acc_quality]['backupUrl'][0]
video_minute = duration // 60
video_second = duration % 60
print('{}.\t當前視頻清晰度為{},時長{}分{}秒'.format(i + 1, quality, video_minute, video_second))
pool.submit(download_video_batch, url, video_url, audio_url, video_name, i + 1).add_done_callback(
video_audio_merge_batch)
pool.shutdown(wait=True)
time.sleep(5)
## 整理視頻資訊
if os.path.exists(title):
shutil.rmtree(title)
os.rename('video_final', title)
try:
shutil.rmtree('video')
except:
shutil.rmtree('video')
def multiple_download():
'''批量下載多個獨立視頻'''
## 提示輸入所有aid
aid_str = input(
'請輸入要下載的所有視頻id,id之間用空格分開\n舉例:有5個鏈接https://www.bilibili.com/video/av89592082、https://www.bilibili.com/video/av68716174、https://www.bilibili.com/video/av87216317、\nhttps://www.bilibili.com/video/av83200644和https://www.bilibili.com/video/av88252843,則輸入89592082 68716174 87216317 83200644 88252843\n默認為89592082 68716174 87216317 83200644 88252843\t')
if aid_str:
pass
else:
aid_str = '89592082 68716174 87216317 83200644 88252843'
if os.path.exists(aid_str):
shutil.rmtree(aid_str)
aids = aid_str.split(' ')
## 提示選擇視頻質量
quality = input('請選擇清晰度(1代表高清,2代表清晰,3代表流暢),默認高清\t')
if quality == '2':
pass
elif quality == '3':
pass
else:
quality = '1'
acc_quality = int(quality) - 1
## 創建檔案夾
create_folder(aid_str)
## 創建執行緒池,執行多任務
pool = ThreadPoolExecutor(3)
for aid in aids:
## 將任務加入執行緒池
pool.submit(single_download, aid, acc_quality)
pool.shutdown(wait=True)
time.sleep(5)
## 洗掉臨時檔案,移動檔案
remove_move_file(aid_str)
def single_download(aid, acc_quality):
'''單個視頻實作下載'''
## 請求視頻鏈接,獲取資訊
origin_video_url = 'https://www.bilibili.com/video/av' + aid
res = requests.get(origin_video_url, headers=headers)
html = etree.HTML(res.text)
title = html.xpath('//*[@id="viewbox_report"]/h1/span/text()')[0]
print('您當前正在下載:', title)
video_info_temp = re_video_info(res.text, '__playinfo__=(.*?)</script><script>')
video_info = {}
## 獲取視頻質量
quality = video_info_temp['data']['accept_description'][acc_quality]
## 獲取視頻時長
video_info['duration'] = video_info_temp['data']['dash']['duration']
## 獲取視頻鏈接
video_url = video_info_temp['data']['dash']['video'][acc_quality]['baseUrl']
## 獲取音頻鏈接
audio_url = video_info_temp['data']['dash']['audio'][acc_quality]['baseUrl']
## 計算視頻時長
video_time = int(video_info.get('duration', 0))
video_minute = video_time // 60
video_second = video_time % 60
print('當前視頻清晰度為{},時長{}分{}秒'.format(quality, video_minute, video_second))
## 呼叫函式下載保存視頻
download_video_single(origin_video_url, video_url, audio_url, title)
def single_input():
'''單個檔案下載,獲取引數'''
## 獲取視頻aid
aid = input('請輸入要下載的視頻id(舉例:鏈接https://www.bilibili.com/video/av89592082中id為89592082),默認為89592082\t')
if aid:
pass
else:
aid = '89592082'
## 提示選擇視頻質量
quality = input('請選擇清晰度(1代表高清,2代表清晰,3代表流暢),默認高清\t')
if quality == '2':
pass
elif quality == '3':
pass
else:
quality = '1'
acc_quality = int(quality) - 1
## 呼叫函式進行下載
single_download(aid, acc_quality)
def main():
'''主函式,提示用戶進行三種下載模式的選擇'''
download_choice = input('請輸入您需要下載的型別:\n1代表下載單個視頻,2代表批量下載系列視頻,3代表批量下載多個不同視頻,默認下載單個視頻\t')
print("*"*30)
## 批量下載系列視頻
if download_choice == '2':
down_choic = input('請輸入您需要下載的型別:\n1代表下載系列視頻,2代表批量下載番劇')
print("*"*30)
if down_choic=='1':
batch_download()
else:
batch_down_fanju()
## 批量下載多個單個視頻
elif download_choice == '3':
multiple_download()
## 下載單個視頻
else:
single_input()
if __name__ == '__main__':
'''呼叫主函式'''
print("*"*14+"宣告"+"*"*14)
print("本程式單個視頻下載只對av加數字有效,\n批量下載視頻對av,bv,ss,ep加數字有效,\n批量下載獨立視頻只對av加數字有效\nBy 霧進\t\t 博客地址:https://blog.csdn.net/a12355556/")
print("*"*30)
main()
本專案在實施的程序中可能參考了其他大佬的實作思路,如有侵犯他人利益,請聯系更改或洗掉,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261389.html
標籤:python
