#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 使用旧版 API 获取并下载视频流 """ import asyncio import json import os from playwright.async_api import async_playwright async def download_video_stream(bvid, output_dir="/tmp/bilibili_downloads"): """下载视频流""" url = f"https://www.bilibili.com/video/{bvid}" # 创建输出目录 os.makedirs(output_dir, exist_ok=True) async with async_playwright() as playwright: browser = await playwright.chromium.launch(headless=True) context = await browser.new_context() page = await context.new_page() print(f"正在下载视频: {url}") print("=" * 80) # 首先获取 aid 和 cid print("\n1. 获取视频信息...") try: api_url = "https://api.bilibili.com/x/web-interface/view" response = await page.request.get(api_url, params={'bvid': bvid}) data = await response.json() if data.get('code') != 0: print(f" ❌ 失败: {data.get('message')}") await browser.close() return None video_data = data.get('data', {}) title = video_data.get('title') aid = video_data.get('aid') cid = video_data.get('cid') duration = video_data.get('duration') print(f" 标题: {title}") print(f" 时长: {duration}秒") except Exception as e: print(f" ❌ 异常: {e}") await browser.close() return None # 获取视频流 URL print("\n2. 获取视频流 URL...") try: playurl_api = "https://api.bilibili.com/x/player/playurl" params = { 'bvid': bvid, 'cid': cid, 'qn': 80, # 1080P 'otype': 'json', 'fnver': 0, 'fnval': 0, # FLV 格式 } playurl_response = await page.request.get(playurl_api, params=params) if not playurl_response.ok: print(f" ❌ HTTP 错误: {playurl_response.status}") await browser.close() return None playurl_data = await playurl_response.json() if playurl_data.get('code') != 0: print(f" ❌ API 错误: {playurl_data.get('message')}") await browser.close() return None play_data = playurl_data.get('data', {}) # 检查支持的清晰度 print(f"\n 支持的清晰度:") for i, (q, desc) in enumerate(zip(play_data.get('accept_quality', []), play_data.get('accept_description', []))): current = " (当前)" if q == play_data.get('quality') else "" print(f" {desc} ({q}){current}") # 获取视频 URL durl = play_data.get('durl', []) if not durl: print(f" ❌ 没有找到视频流") await browser.close() return None print(f"\n 找到 {len(durl)} 个视频分段") video_urls = [] for i, segment in enumerate(durl): segment_url = segment.get('url') segment_size = segment.get('size', 0) video_urls.append(segment_url) print(f" 分段 {i+1}: {segment_size / 1024 / 1024:.2f}MB") print(f" {segment_url[:100]}...") except Exception as e: print(f" ❌ 异常: {e}") import traceback traceback.print_exc() await browser.close() return None # 下载视频 print(f"\n3. 开始下载视频...") all_segments = [] total_size = 0 for i, video_url in enumerate(video_urls, 1): print(f"\n 下载分段 {i}/{len(video_urls)}...") try: # 添加必要的请求头 headers = { 'Referer': 'https://www.bilibili.com/', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', } # 下载分段 segment_response = await page.request.get( video_url, headers=headers, timeout=60000 ) if not segment_response.ok: print(f" ❌ 下载失败: {segment_response.status}") continue segment_data = await segment_response.body() segment_size = len(segment_data) total_size += segment_size all_segments.append(segment_data) print(f" ✅ 下载成功: {segment_size / 1024 / 1024:.2f}MB") except Exception as e: print(f" ❌ 下载异常: {e}") continue if not all_segments: print(f"\n ❌ 没有成功下载任何分段") await browser.close() return None # 合并分段 print(f"\n4. 合并视频分段...") video_data = b''.join(all_segments) # 保存文件 safe_title = "".join(c for c in title if c.isalnum() or c in (' ', '-', '_')).strip()[:50] output_path = os.path.join(output_dir, f"{safe_title}.flv") with open(output_path, 'wb') as f: f.write(video_data) print(f" ✅ 视频已保存: {output_path}") print(f" 文件大小: {total_size / 1024 / 1024:.2f}MB") await browser.close() return output_path if __name__ == '__main__': bvid = "BV1KtFzzqEUG" result = asyncio.run(download_video_stream(bvid)) if result: print(f"\n✅ 下载完成!") print(f"文件路径: {result}") else: print(f"\n❌ 下载失败")