下载b站视频

 效果展示:

FFmpeg  用来合成音频和视频

代码如下

import os
import pprint
import re
import requests
from bs4 import BeautifulSoup
from lxml import etree
import json

class bilibili:

    def __init__(self,url):
        self.head = {
            ## 此处设置防盗链:指明连接的请求来源于B站,合法
            #https://www.bilibili.com/video/BV17w4m1e7PT/
             'referer':
                 'https://www.bilibili.com/',
            'user-agent':
                'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0'
        }
        self.response = requests.get(url=url, headers=self.head)
    def askURL(self,url):
        self.url = url
        response = requests.get(url=self.url, headers=self.head)
        return response


    def download(self):
       
        VideoInfo = re.findall('<script>window.__playinfo__=(.*?)</script>', self.response.text)[0]
        VideoName = re.findall('<title data-vue-meta="true">(.*?)</title>', self.response.text)[0].translate(str.maketrans('','',' !:/'))
       #re.findall('<title>(.*?)</title>',
       # pprint.pprint(json.loads(VideoInfo))
        jsonData = json.loads(VideoInfo)


        audioURL = jsonData['data']['dash']['audio'][0]['baseUrl']
        #videoURL = jsonData['data']['dash']['video'][0]['baseUrl']
        videoURL = jsonData['data']['dash']['video'][4]['baseUrl']


        if not os.path.exists(rf"download_video/"):
            os.mkdir(rf"download_video/")
        if not os.path.exists(rf"download_video/{VideoName}"):
            os.mkdir(rf"download_video/{VideoName}")
        audioContent = self.askURL(audioURL).content
        with open(f'download_video/{VideoName}/' + VideoName + '.mp3', mode='wb') as f:
            f.write(audioContent)
            print(f"正在下载视频:{VideoName}")
        videoContent = self.askURL(videoURL).content
        with open(f'download_video/{VideoName}/' + VideoName + '.mp4', mode='wb') as f:
            print(f"正在下载音频:{VideoName}")
            f.write(videoContent)
        return VideoName

    def ffpmeg(self,VideoName,Ffmpeg_path):
        self.Ffmpeg_path = Ffmpeg_path
        root = os.getcwd()

        audioFile = rf'{root}\download_video\{VideoName}/' + VideoName + '.mp3'
        videoFile = rf'{root}\download_video\{VideoName}/' + VideoName + '.mp4'


        outfile_name = rf'{root}\download_video\{VideoName}/' + VideoName + 'synthesis.mp4'
        cmd = f'{self.Ffmpeg_path} -i {audioFile} -i {videoFile} -acodec copy -vcodec copy {outfile_name}'
        #

        text = os.popen(cmd).read()
        print(text)
        print(f"视频下载路径为:{outfile_name}")

if __name__ == '__main__':
    #视频地址
    #url = 'https://www.bilibili.com/video/BV1Qy411H7Ne/'
    url = input("请输入视频地址:")
    #ffmpeg路径
    Ffmpeg_path = r'F:\pycharm\project\ffmpeg\bin\ffmpeg'

    if url.strip() == '':
        pass
    else:
        b = bilibili(url)
        b.askURL(url)

        VideoName = b.download()
        #合成音视频,需要自行下载ffpmeg
        #b.ffpmeg(VideoName,Ffmpeg_path)

更新

import json
import os
import re
import time
import requests
import subprocess
from urllib.parse import quote
from typing import Optional


class BilibiliDownloader:
    def __init__(self):
        self.headers = {
            'referer': 'https://www.bilibili.com/',
            'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36'
        }
        self.session = requests.Session()
        self.session.headers.update(self.headers)

    def sanitize_filename(self, filename: str) -> str:
        """清理非法文件名字符"""
        return re.sub(r'[\\/:*?"<>|!]', '', filename).strip()

    def get_video_info(self, url: str) -> tuple:
        """获取视频元数据"""
        try:
            response = self.session.get(url, timeout=10)
            response.raise_for_status()
        except requests.RequestException as e:
            raise RuntimeError(f"请求失败: {str(e)}")

        # 提取视频标题
        title_match = re.search(r'<title>(.*?)</title>', response.text)
        if not title_match:
            raise ValueError("无法提取视频标题")
        video_title = self.sanitize_filename(title_match.group(1))

        # 提取视频信息
        play_info_match = re.search(r'<script>window.__playinfo__=(.*?)</script>', response.text)
        if not play_info_match:
            raise ValueError("无法提取视频播放信息")

        try:
            play_info = json.loads(play_info_match.group(1))
            audio_url = play_info['data']['dash']['audio'][0]['baseUrl']
            video_url = play_info['data']['dash']['video'][0]['baseUrl']
        except (KeyError, IndexError, json.JSONDecodeError) as e:
            raise ValueError(f"解析视频信息失败: {str(e)}")

        return video_title, audio_url, video_url

    def download_file(self, url: str, filepath: str, chunk_size: int = 8192) -> None:
        """通用下载方法(流式下载)"""
        try:
            with self.session.get(url, stream=True, timeout=30) as response:
                response.raise_for_status()
                total_size = int(response.headers.get('content-length', 0))

                with open(filepath, 'wb') as f:
                    downloaded = 0
                    for chunk in response.iter_content(chunk_size=chunk_size):
                        if chunk:
                            f.write(chunk)
                            downloaded += len(chunk)
                            print(f"\r下载进度: {downloaded}/{total_size} bytes", end='')
                    print()  # 换行
        except Exception as e:
            if os.path.exists(filepath):
                os.remove(filepath)
            raise RuntimeError(f"下载失败: {str(e)}")

    def process_video(self, url: str, output_dir: str = "download_video") -> Optional[str]:
        """主处理流程"""
        try:
            # 获取视频信息
            video_title, audio_url, video_url = self.get_video_info(url)

            # 创建输出目录
            output_path = os.path.join(output_dir, video_title)
            os.makedirs(output_path, exist_ok=True)

            # 下载文件
            print(f"开始下载视频: {video_title}")
            audio_path = os.path.join(output_path, f"{video_title}.mp3")
            video_path = os.path.join(output_path, f"{video_title}.mp4")

            self.download_file(audio_url, audio_path)
            self.download_file(video_url, video_path)

            return output_path
        except Exception as e:
            print(f"\n处理失败: {str(e)}")
            return None

    @staticmethod
    def merge_media(output_path: str, ffmpeg_path: str = "ffmpeg") -> bool:
        """合并音视频"""
        video_title = os.path.basename(output_path)
        audio_file = os.path.join(output_path, f"{video_title}.mp3")
        video_file = os.path.join(output_path, f"{video_title}.mp4")
        output_file = os.path.join(output_path, f"{video_title}_merged.mp4")

        if not os.path.exists(ffmpeg_path):
            raise FileNotFoundError("未找到FFmpeg可执行文件")

        cmd = [
            ffmpeg_path,
            '-y',  # 覆盖已有文件
            '-i', audio_file,
            '-i', video_file,
            '-c', 'copy',
            output_file
        ]

        try:
            subprocess.run(
                cmd,
                check=True,
                stdout=subprocess.DEVNULL,
                stderr=subprocess.DEVNULL
            )
            # 清理临时文件
            os.remove(audio_file)
            os.remove(video_file)
            return True
        except subprocess.CalledProcessError as e:
            print(f"合并失败: {str(e)}")
            return False


if __name__ == '__main__':
    downloader = BilibiliDownloader()

    # 用户输入
    video_url = input("请输入B站视频地址(留空退出): ").strip()
    if not video_url:
        exit()

    # FFmpeg路径配置(默认在环境变量中)
    ffmpeg_path = r'D:\pycharm\demo\pythonProject1\study\video\download_video\ffmpeg\bin\ffmpeg.exe'  # 可配置为环境变量

    try:
        # 下载处理
        output_path = downloader.process_video(video_url)
        if not output_path:
            exit()

        # 合并文件
        print("开始合并音视频...")
        if downloader.merge_media(output_path, ffmpeg_path):
            print("处理完成!最终文件:", os.path.join(output_path, f"{os.path.basename(output_path)}_merged.mp4"))

    except KeyboardInterrupt:
        print("\n用户中断操作")
    except Exception as e:
        print(f"发生未知错误: {str(e)}")

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值