#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ 深度测试B站字幕提取 - 尝试所有可能的方法 """ import asyncio import json import re from playwright.async_api import async_playwright from bs4 import BeautifulSoup async def test_all_methods(bvid): """尝试所有方法获取字幕""" url = f"https://www.bilibili.com/video/{bvid}" 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) # 等待页面完全加载 print("\n1. 访问页面...") await page.goto(url, wait_until='networkidle', timeout=60000) await asyncio.sleep(3) # 获取基本信息 print("\n2. 获取视频基本信息...") 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: video_data = data.get('data', {}) print(f" 标题: {video_data.get('title')}") print(f" aid: {video_data.get('aid')}") print(f" cid: {video_data.get('cid')}") cid = video_data.get('cid') aid = video_data.get('aid') else: print(f" ❌ 获取基本信息失败: {data.get('message')}") await browser.close() return except Exception as e: print(f" ❌ 异常: {e}") await browser.close() return # 方法1: 使用标准 API(官方 CC 字幕) print("\n3. 方法1: 官方 CC 字幕 API...") try: player_url = "https://api.bilibili.com/x/player/v2" player_response = await page.request.get(player_url, params={'bvid': bvid, 'cid': cid}) player_data = await player_response.json() if player_data.get('code') == 0: subtitle_info = player_data.get('data', {}).get('subtitle', {}) subtitles_list = subtitle_info.get('subtitles', []) if subtitles_list: print(f" ✅ 找到 {len(subtitles_list)} 个官方字幕") for i, sub in enumerate(subtitles_list): print(f" [{i+1}] {sub.get('lan_doc')} (id={sub.get('id')})") # 获取第一个字幕内容 subtitle_url = subtitles_list[0].get('subtitle_url', '') if subtitle_url: full_url = f"https:{subtitle_url}" print(f" URL: {subtitle_url}") sub_response = await page.request.get(full_url) if sub_response.ok: sub_data = await sub_response.json() body = sub_data.get('body', []) print(f" ✅ 获取成功,共 {len(body)} 条字幕") print(f" 示例: {body[0] if body else '无'}") else: print(" ❌ 没有找到官方 CC 字幕") print(f" Subtitle info: {json.dumps(subtitle_info, indent=8, ensure_ascii=False)}") except Exception as e: print(f" ❌ 异常: {e}") # 方法2: 尝试 AI 字幕 API print("\n4. 方法2: AI 自动生成字幕...") try: # B站 AI 字幕可能在另一个 API ai_subtitle_url = "https://api.bilibili.com/x/player/wbi/v2/playurl" # 这个通常用于视频流,但也可能包含字幕信息 ai_params = { 'bvid': bvid, 'cid': cid, 'qn': 80, # 清晰度 'fnval': 16, # 包含字幕的标志 } ai_response = await page.request.get(ai_subtitle_url, params=ai_params) ai_data = await ai_response.json() if ai_data.get('code') == 0: print(f" AI API 响应: {json.dumps(list(ai_data.get('data', {}).keys()), indent=8, ensure_ascii=False)}") # 检查是否有字幕信息 if 'subtitle' in ai_data.get('data', {}): subtitle = ai_data['data']['subtitle'] print(f" ✅ 找到 AI 字幕信息") print(f" {json.dumps(subtitle, indent=8, ensure_ascii=False)}") except Exception as e: print(f" ❌ 异常: {e}") # 方法3: 从页面脚本标签中提取(可能包含字幕配置) print("\n5. 方法3: 从页面 HTML 中提取字幕配置...") try: content = await page.content() soup = BeautifulSoup(content, 'lxml') script_tags = soup.find_all('script') found_subtitle = False for script in script_tags: if script.string and ('subtitle' in script.string or 'cc_subtitle' in script.string): # 提取包含字幕配置的 JSON matches = re.findall(r'window\.__(?:playinfo|INITIAL_STATE|subtitle__)\s*=\s*({.+?});', script.string) for match in matches: try: data = json.loads(match) # 查找字幕相关字段 if 'subtitle' in str(data) or 'subtitles' in str(data): print(f" ✅ 找到字幕配置") print(f" {json.dumps(data, indent=8, ensure_ascii=False)[:500]}") found_subtitle = True break except: pass if found_subtitle: break if not found_subtitle: print(" ❌ 页面中没有找到字幕配置") except Exception as e: print(f" ❌ 异常: {e}") # 方法4: 尝试不同的播放器 API 端点 print("\n6. 方法4: 尝试其他 API 端点...") endpoints = [ f"https://api.bilibili.com/x/player/wbi/v2/player?bvid={bvid}&cid={cid}", f"https://api.bilibili.com/x/player/v2?bvid={bvid}&cid={cid}", f"https://api.bilibili.com/x/player/playurl?bvid={bvid}&cid={cid}&qn=80&fnval=16", ] for endpoint in endpoints: try: print(f" 测试: {endpoint}") response = await page.request.get(endpoint) if response.ok: data = await response.json() # 检查响应中是否有字幕信息 json_str = json.dumps(data, ensure_ascii=False) if 'subtitle' in json_str.lower(): print(f" ✅ 可能包含字幕信息") # 只显示相关部分 if 'data' in data and 'subtitle' in str(data['data']): print(f" {json.dumps(data.get('data', {}).get('subtitle', {}), indent=12, ensure_ascii=False)}[:500]") else: print(f" 状态码: {response.status}") except Exception as e: print(f" 异常: {e}") # 方法5: 尝试使用 aid 而不是 cid print("\n7. 方法5: 尝试使用 aid 参数...") try: if aid: player_url = "https://api.bilibili.com/x/player/v2" response = await page.request.get(player_url, params={'aid': aid, 'bvid': bvid}) data = await response.json() print(f" 响应码: {data.get('code')}") if data.get('code') == 0: subtitle_info = data.get('data', {}).get('subtitle', {}) subtitles_list = subtitle_info.get('subtitles', []) if subtitles_list: print(f" ✅ 使用 aid 找到 {len(subtitles_list)} 个字幕") else: print(f" ❌ 使用 aid 也没有找到字幕") except Exception as e: print(f" ❌ 异常: {e}") # 方法6: 检查视频是否有多 P(分集) print("\n8. 方法6: 检查是否为分集视频...") 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: pages = data.get('data', {}).get('pages', []) print(f" 共 {len(pages)} 个分集") for i, p in enumerate(pages): print(f" [{i+1}] cid={p.get('cid')}, page={p.get('page')}, title={p.get('part')}") # 如果有多个分集,尝试每个分集的字幕 if len(pages) > 1: print(f"\n 尝试获取第1个分集的字幕...") first_cid = pages[0].get('cid') player_url = "https://api.bilibili.com/x/player/v2" response = await page.request.get(player_url, params={'bvid': bvid, 'cid': first_cid}) data = await response.json() subtitle_info = data.get('data', {}).get('subtitle', {}) subtitles_list = subtitle_info.get('subtitles', []) if subtitles_list: print(f" ✅ 第1个分集有 {len(subtitles_list)} 个字幕") else: print(f" ❌ 第1个分集也没有字幕") except Exception as e: print(f" ❌ 异常: {e}") await browser.close() if __name__ == '__main__': bvid = "BV1KtFzzqEUG" asyncio.run(test_all_methods(bvid))