本帖最后由 QuiryRain 于 2023-2-15 22:14 编辑
原因:前两天一朋友问有没有小破站的视频下载工具。然后就想着,最近学了点python是不是可以实现。
新增:
1. 视频以当前最高清晰度进行下载,在带上cookie的前提下,可下载大会员视频。
2. 增加音轨、视频轨下载进度条
原理: 下载视频轨道文件、和音频轨道文件,最后用ffmpeg进行视频轨和音轨的合成
目前下载工具可以在 win/linux/mac上运行。其实就是执行python
Linux/Mac OS X
在linux/Mac OS X系统中可能需要修改下文件权限
Linux
cd /path/to/BiliDown
chmod +x tools/linux/ffmpeg
Mac
cd /path/to/BiliDown
chmod +x tools/mac/ffmpeg
教程
pip install -r reqirements.txt
编辑 download.py 文件,在最后 url 中填上你需要下载的B站链接,
如果需要换 cookie, 在Cookies中填写你浏览器中的cookie内容
最后用python执行就行了
最终需要的视频会在当前目录下的 Output 中生成
代码:
[Python] 纯文本查看 复制代码 #!/usr/bin/env python3
# -*- coding: utf8 -*-
import os
import sys
import re
import time
import platform
import json
import traceback
import requests
import subprocess
requests.packages.urllib3.disable_warnings()
sess = requests.Session()
sess.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36',
})
def CMD(command):
p = subprocess.Popen(command, shell=True, universal_newlines=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, encoding='utf8')
stdout, stderr = p.communicate(timeout=10)
stdout = stdout.strip()
return stdout, stderr
def download_file(filename, url):
resp = sess.get(url, verify=False, stream=True)
content_size = int(resp.headers['content-length'])
if resp.status_code != 200:
print('请求失败')
sys.exit()
size = 0
start_time = time.time()
print('Downloading ... File Size: {size:.2f} MB'.format(size=content_size/1024/1024.0))
with open(filename, 'wb') as f:
for chunk in resp.iter_content(chunk_size=1024):
if chunk:
f.write(chunk)
size += len(chunk)
print(
'\r[下载进度]: %s %.2f%%' % (
'>'*int(size*50 / content_size),
float(size / content_size * 100)),
end=' ')
print('\nDownload completed!,times: {}秒'.format(int(time.time() - start_time)))
return filename
def delete_cache_file(path):
try:
os.remove(path)
except Exception as e:
print(e)
def get_video_and_audio_url_from_bilibili(url):
re_playinfo = re.compile(r'window\.__playinfo__=(\{.*?)</script>', re.I)
re_initial_state = re.compile(r'window\.__INITIAL_STATE__=(\{.*?);\(function\(\)', re.I)
sess.headers.update({'referer': url})
resp = sess.get(url, verify=False, timeout=(8, 8))
html = resp.text
# print(html)
_initial_state = re_initial_state.search(html)
_playinfo = re_playinfo.search(html)
if not all([_initial_state, _playinfo]):
print('请求失败')
sys.exit()
playinfo = _playinfo.group(1)
playinfo_json = json.loads(playinfo)
initial_state = _initial_state.group(1)
initial_state_json = json.loads(initial_state)
dash = playinfo_json.get('data', {}).get('dash', {})
video_urls = []
for video in dash.get('video', []):
base_url = video.get('baseUrl', '')
if not base_url:
continue
video_urls.append(base_url)
audio_urls = []
for audio in dash.get('audio', []):
base_url = audio.get('baseUrl')
if not base_url:
continue
audio_urls.append(base_url)
title = (initial_state_json.get('h1Title')
or initial_state_json.get('videoData', {}).get('title')
or get_bvid(url))
return video_urls, audio_urls, title
def create_cache_directory():
if not os.path.exists('BVcache'):
os.mkdir('BVcache')
if not os.path.exists('Output'):
os.mkdir('Output')
def get_bvid(url):
create_cache_directory()
return url.split('?')[0].rsplit('/', maxsplit=1)[1]
def main(url):
bvid = get_bvid(url)
video_urls, audio_urls, video_name = get_video_and_audio_url_from_bilibili(url)
# print(video_urls, audio_urls)
video_filename = f'BVcache/video_{bvid}.m4s'
audio_filename = f'BVcache/audio_{bvid}.m4s'
print(f'{video_name}: {bvid} 开始下载视频轨道数据...')
for video_url in video_urls:
try:
download_file(video_filename, video_url)
break
except Exception as e:
print('Video Error. {}'.format(e))
print(traceback.format_exc())
print(f'{video_name}: {bvid} 视频轨道下载完毕')
print(f'{video_name}: {bvid} 开始下载音频轨道数据...')
for audio_url in audio_urls:
try:
download_file(audio_filename, audio_url)
break
except Exception as e:
print('Audio Error. {}'.format(e))
print(f'{video_name}: {bvid} 音频轨道下载完毕!')
system = (platform.platform()).lower()
if 'windows' in system:
ffmpeg_tool = r'.\tools\win\ffmpeg.exe'
elif 'macOS' in system:
ffmpeg_tool = './tools/mac/ffmpeg'
else:
ffmpeg_tool = './tools/linux/ffmpeg'
filename = video_name.replace(" ", "-") or bvid
ffmpeg_command = (
ffmpeg_tool + f' -i {video_filename} -i {audio_filename} -codec '
f'copy Output/{filename}.mp4'
)
# print(ffmpeg_command)
CMD(ffmpeg_command)
delete_cache_file(video_filename)
delete_cache_file(audio_filename)
print(f'{bvid} 视频mp4文件已经生成,请在当前运行路径中的"Output"文件夹中查看,'
f'生成的文件名称为: {filename}.mp4')
if __name__ == '__main__':
# url = 'https://www.bilibili.com/video/BV1yr4y1r7f6?spm_id_from=333.851.b_7265636f6d6d656e64.5'
url = "https://www.bilibili.com/bangumi/play/ep706667?spm_id_from=333.337.0.0"
# url = "https://www.bilibili.com/video/BV1Px4y1G7Ua"
# url = 'https://www.bilibili.com/bangumi/play/ss44096?theme=movie&from_spmid=666.7.hotlist.2'
url = 'https://www.bilibili.com/bangumi/play/ss41936?from_spmid=666.23.0.0'
url = 'https://www.bilibili.com/bangumi/play/ep468228?from_spmid=666.25.episode.0&from_outer_spmid=666.24.0.0'
sess.headers.update({
# 'cookie': ("Your Cookie")
})
main(url)
"""
运行环境: Linux、python
安装环境包:pip install requests
将上面的url修改为要下载的视频地址。
运行:python download.py
输出:视频mp4文件会存放到当前目录中的Output文件夹中
"""
下载链接放上:https://quiryrain.lanzoue.com/iR3zM0notoja 密码:52pj |