fix(bilibili): 改进字幕提取,支持 WBI API 和多语言优先级
- 使用 WBI API 获取字幕信息(支持 AI 字幕) - 优先选择中文字幕(ai-zh > zh-CN > zh-Hans) - 添加硬字幕提取实施计划 - 改进错误处理,静默失败不影响主流程
This commit is contained in:
@@ -447,6 +447,13 @@ class BilibiliExtractor(Extractor):
|
||||
"""
|
||||
提取视频字幕
|
||||
|
||||
支持的字幕类型:
|
||||
1. 官方 CC 字幕(需要 UP 主手动上传)
|
||||
2. AI 自动生成字幕(标记为 ai-zh)
|
||||
3. WebVTT 格式字幕
|
||||
|
||||
注意:硬字幕(嵌入视频画面内)无法提取
|
||||
|
||||
Args:
|
||||
page: Playwright page对象
|
||||
bvid: BV号
|
||||
@@ -457,30 +464,87 @@ class BilibiliExtractor(Extractor):
|
||||
subtitles = []
|
||||
|
||||
try:
|
||||
# 获取 aid
|
||||
aid = await page.evaluate('() => window.__INITIAL_STATE__?.videoData?.aid')
|
||||
if not aid:
|
||||
# 首先通过 API 获取视频信息(包含 cid 和 aid)
|
||||
api_url = "https://api.bilibili.com/x/web-interface/view"
|
||||
response = await page.request.get(api_url, params={'bvid': bvid})
|
||||
|
||||
if not response.ok:
|
||||
return ''
|
||||
|
||||
# API获取字幕信息
|
||||
api_url = "https://api.bilibili.com/x/player/v2"
|
||||
response = await page.request.get(api_url, params={'aid': aid, 'bvid': bvid})
|
||||
data = await response.json()
|
||||
if data.get('code') != 0:
|
||||
return ''
|
||||
|
||||
if response.ok:
|
||||
data = await response.json()
|
||||
if data.get('code') == 0:
|
||||
subtitle_info = data.get('data', {}).get('subtitle', {})
|
||||
video_data = data.get('data', {})
|
||||
cid = video_data.get('cid')
|
||||
aid = video_data.get('aid')
|
||||
|
||||
if not cid:
|
||||
return ''
|
||||
|
||||
# 方法1: 使用 WBI API(推荐,支持 AI 字幕)
|
||||
player_api_url = "https://api.bilibili.com/x/player/wbi/v2"
|
||||
player_response = await page.request.get(player_api_url, params={'aid': aid, 'cid': cid})
|
||||
|
||||
if player_response.ok:
|
||||
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:
|
||||
subtitle_url = subtitles_list[0].get('subtitle_url', '')
|
||||
# 优先选择中文字幕(包括 AI 字幕)
|
||||
subtitle_item = None
|
||||
for sub in subtitles_list:
|
||||
# 优先级: ai-zh > zh-CN > zh-Hans > 其他
|
||||
lan = sub.get('lan', '')
|
||||
if lan == 'ai-zh':
|
||||
subtitle_item = sub
|
||||
break
|
||||
elif lan in ['zh-CN', 'zh-Hans', 'zh']:
|
||||
if not subtitle_item:
|
||||
subtitle_item = sub
|
||||
|
||||
# 如果没有找到中文,使用第一个
|
||||
if not subtitle_item:
|
||||
subtitle_item = subtitles_list[0]
|
||||
|
||||
if subtitle_item:
|
||||
subtitle_url = subtitle_item.get('subtitle_url', '')
|
||||
if subtitle_url:
|
||||
full_url = f"https:{subtitle_url}"
|
||||
sub_response = await page.request.get(full_url)
|
||||
if sub_response.ok:
|
||||
sub_data = await sub_response.json()
|
||||
subtitles = self._parse_subtitle_json(sub_data)
|
||||
# 如果找到字幕,直接返回
|
||||
if subtitles:
|
||||
lines = []
|
||||
for sub in subtitles:
|
||||
lines.append(f"[{sub['from']} - {sub['to']}] {sub['content']}")
|
||||
return '\n'.join(lines)
|
||||
|
||||
# 方法2: 如果 WBI 失败,使用旧版 API
|
||||
player_api_url = "https://api.bilibili.com/x/player/v2"
|
||||
player_response = await page.request.get(player_api_url, params={'bvid': bvid, 'cid': cid})
|
||||
|
||||
if player_response.ok:
|
||||
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:
|
||||
subtitle_item = subtitles_list[0]
|
||||
subtitle_url = subtitle_item.get('subtitle_url', '')
|
||||
if subtitle_url:
|
||||
full_url = f"https:{subtitle_url}"
|
||||
response = await page.request.get(full_url)
|
||||
if response.ok:
|
||||
sub_data = await response.json()
|
||||
sub_response = await page.request.get(full_url)
|
||||
if sub_response.ok:
|
||||
sub_data = await sub_response.json()
|
||||
subtitles = self._parse_subtitle_json(sub_data)
|
||||
except Exception as e:
|
||||
# 静默失败,不影响视频信息提取
|
||||
pass
|
||||
|
||||
# 转换为多行字符串
|
||||
|
||||
Reference in New Issue
Block a user