爬虫下载B站视频简单步伐(仅供学习)

[复制链接]
发表于 2025-10-23 20:15:54 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
请输入视频链接:https://www.bilibili.com/video/BV1owFSeREoh (示例所在)                                 步伐表现结果如下:              
下载进度: 100.00%
下载完成
视频已生存到: ./video.mp4
核心功能

1. 视频信息剖析

  • 主动辨认B站视频格式(新版DASH/旧版FLV)


  • 提取最高画质视频流所在


  • 支持分段视频检测
2.下载

  • 多线程分块下载(1MB/块)


  • 主动重试机制(最多5次)


  • 及时下载进度表现


  • 支持署理和反爬计谋
- 通过正则提取B站视频核心数据

  • 包罗分辨率、码率、分段所在等关键信息   re.search(r'<script>window\.__playinfo__=(.*?)</script>', html)
注意:

  • 仅下载已得到观看权限的内容


  • 本代码仅供学习利用,请服从B站用户协媾和版权法规
  1. import re
  2. import json
  3. import requests
  4. from urllib.parse import urlparse
  5. import subprocess
  6. import time
  7. class BilibiliDownloader:
  8.     def __init__(self, url):
  9.         self.url = url
  10.         self.headers = {
  11.             'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
  12.             'Referer': 'https://www.bilibili.com/'
  13.         }
  14.    
  15.     def get_video_info(self):
  16.         # 获取视频页面源码
  17.         response = requests.get(self.url, headers=self.headers)
  18.         html = response.text
  19.         
  20.         # 提取JSON格式的视频信息
  21.         match = re.search(r'<script>window\.__playinfo__=(.*?)</script>', html)
  22.         if match:
  23.             return json.loads(match.group(1))
  24.         else:
  25.             raise Exception("无法提取视频信息")
  26.     def download_video(self, save_path='./video.mp4'):
  27.         # 获取视频下载链接
  28.         video_info = self.get_video_info()
  29.         
  30.         # 处理不同视频格式(优先dash格式)
  31.         try:
  32.             if video_info['data'].get('dash'):
  33.                 # Dash格式视频流
  34.                 video_list = video_info['data']['dash']['video']
  35.                 best_quality = max(video_list, key=lambda x: x['bandwidth'])
  36.                 video_url = best_quality['baseUrl']
  37.             else:  
  38.                 # 旧版FLV格式
  39.                 video_list = video_info['data']['durl']
  40.                 best_quality = max(video_list, key=lambda x: x['size'])
  41.                 video_url = best_quality['url']
  42.                
  43.                 # 处理分段视频(需要合并多个flv)
  44.                 if len(video_list) > 1:
  45.                     print("检测到分段视频,需要合并多个文件...")
  46.                     return self._download_flv_segments(video_list, save_path)
  47.         except KeyError as e:
  48.             raise Exception(f"不支持的视频格式: {str(e)}")
  49.         
  50.         # 使用requests流式下载(带重试机制)
  51.         max_retries = 5
  52.         timeout = 15  # 设置超时时间(秒)
  53.         chunk_size = 1024 * 1024  # 1MB chunks
  54.         
  55.         for attempt in range(max_retries):
  56.             try:
  57.                 with requests.get(video_url, headers=self.headers,
  58.                                 stream=True, timeout=timeout) as r:
  59.                     r.raise_for_status()
  60.                     total_size = int(r.headers.get('content-length', 0))
  61.                     
  62.                     with open(save_path, 'wb') as f:
  63.                         downloaded = 0
  64.                         for chunk in r.iter_content(chunk_size=chunk_size):
  65.                             if chunk:  # 过滤保持连接的空白chunk
  66.                                 f.write(chunk)
  67.                                 downloaded += len(chunk)
  68.                                 print(f"\r下载进度: {downloaded/total_size:.2%}", end='')
  69.                     print("\n下载完成")
  70.                     break
  71.                     
  72.             except (requests.exceptions.Timeout,
  73.                   requests.exceptions.ConnectionError) as e:
  74.                 print(f"\n第 {attempt+1} 次尝试失败: {str(e)}")
  75.                 if attempt == max_retries - 1:
  76.                     raise Exception("多次尝试下载失败,请检查网络连接")
  77.                 time.sleep(2 ** attempt)  # 指数退避
  78.         
  79.         # 合并音视频(需要ffmpeg)
  80.         self._merge_audio_video(save_path)
  81.         print(f"视频已保存到: {save_path}")
  82.     def _download_flv_segments(self, segments, save_path):
  83.         """下载并合并FLV分段视频"""
  84.         temp_files = []
  85.         for i, seg in enumerate(segments):
  86.             part_url = seg['url']
  87.             part_file = f"{save_path}.part{i}"
  88.             temp_files.append(part_file)
  89.             
  90.             with requests.get(part_url, headers=self.headers, stream=True) as r:
  91.                 with open(part_file, 'wb') as f:
  92.                     for chunk in r.iter_content(chunk_size=8192):
  93.                         f.write(chunk)
  94.         self._merge_files(temp_files, save_path)
  95.   
  96. if __name__ == "__main__":
  97.     video_url = input("请输入视频链接:")
  98.     downloader = BilibiliDownloader(video_url)
  99.     downloader.download_video()
复制代码
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表