bilibili-comment
B站评论分析工具。输入B站视频链接或BV号即可获取一级评论数据,支持分页浏览、评论情感分析(积极/负面/需求/竞品),生成精美 HTML 报告。当用户需要查看B站视频评论、分析评论舆情、了解用户反馈时使用。触发词:B站评论、B站视频评论、评论查询、评论分析、评论舆情、看评论、bilibili评论。
Install
npx skills add https://github.com/redfox-data/redfox-community/tree/main/skills/bilibili-comment
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install redfox-data-redfox-community@llmmart
git clone https://github.com/redfox-data/redfox-community.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole redfox-data/redfox-community collection as a plugin from our marketplace. Git is the plain clone.
README
B站评论分析 / bilibili-comment
简介
输入任意B站视频链接或BV号,一键获取评论数据,AI 自动完成四维情感分析(积极/负面/需求/竞品),同时生成可离线保存的交互式 HTML 报告。
功能介绍
B站评论分析是专为内容创作者、UP主和品牌运营打造的智能评论洞察工具。基于红狐 API 实时拉取视频一级评论,每页20条,支持分页浏览;AI 深度理解B站梗文化(awsl / yyds / 爷青回 等弹幕术语),完成积极/负面/需求/竞品四维分析并引用代表评论;最终生成深色主题交互式 HTML 报告,便于保存与分享。
核心价值
- 实时评论拉取:粘贴链接即可获取最新评论,无需手动翻找评论区。
- B站梗文化理解:AI 识别弹幕术语和梗的真实语境,分析结果更准确。
- 结构化四维分析:积极/负面/需求/竞品清晰分层,各维度附代表评论引用,快速抓住舆情核心。
适用对象
- 🎬 UP主 — 复盘视频反馈,优化选题和内容策略。
- 🏢 品牌运营 — 监测舆情,及时发现用户声音和风险信号。
- 📊 内容策划 / 产品经理 — 挖掘竞品评论,研究用户真实需求。
功能特性
核心功能
- 评论获取:输入BV号或视频链接,自动拉取一级评论,每页20条
- 分页浏览:支持「下一页」「第N页」等自然语言翻页,逐步浏览大量评论
- 四维 AI 分析:积极/负面/需求/竞品四类情感分析,各附代表评论引用,百分比展示
- 置顶标记:置顶评论特殊标识,一眼识别重要内容
- HTML 报告:深色主题交互式报告,支持离线保存与分享
特色亮点
- B站梗文化理解:识别 awsl / yyds / xswl / 爷青回 / 下次一定 等弹幕术语及梗的真实含义
- 数据完整展示:作品详情(播放/点赞/投币/收藏/弹幕等)与评论表格全量呈现
- 编码安全回填:智能编码保障,中文分析内容完整无误地注入报告
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub (https://redfox.hk) 提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
使用指南
直接用自然语言说出视频链接或BV号即可,无需记忆固定命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 查看视频评论 | 「查看B站视频 BV1xx411c7mD 的评论」 | 获取第1页评论 + AI总结 |
| 通过链接查询 | 粘贴 https://www.bilibili.com/video/BV1xx411c7mD |
自动提取BV号并拉取评论 |
| 翻页 | 「下一页」「第3页」 | 沿用当前视频,获取指定页评论 |
| 生成报告 | 「需要」「生成HTML」 | 回填AI分析,生成交互式HTML报告 |
输出示例
完成查询后,你将依次收到:
作品详情
🎬 视频标题
UP主:昵称 | 发布:2024-01-01 | 时长:12:34
▶ 12.5w 👍 3.2w 🪙 1.8w ⭐ 2.1w 🔄 5.6k 💬 1.2k 📝 8.9k
评论表格(当前页全部评论)
| # | 用户昵称 | 评论内容 | 👍 | 时间 | IP |
|---|---|---|---|---|---|
| 1 | 用户名 | 评论内容... | 1223 | 01-12 15:30 | 广东 |
四维情感分析(AI总结,含代表评论引用)
询问是否生成 HTML 可视化报告
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 内容复盘 | UP主 | 「看看这个视频的评论怎么样」 | 快速了解观众反馈,优化选题策略 |
| 舆情监测 | 品牌运营 | 「分析这条视频的负面评论」 | 及时发现危机信号,制定应对方案 |
| 竞品分析 | 内容策划 | 「分析竞品视频的用户评论」 | 了解竞品优劣势,找准差异化方向 |
| 用户研究 | 产品经理 | 「看看用户在评论区提了哪些需求」 | 挖掘真实需求,指导产品迭代 |
Skill manifest
B站评论分析
简介
B站评论分析是一款专为内容创作者、UP主、品牌运营和研究人员设计的智能评论洞察工具。输入任意B站视频链接或BV号,即可一键获取评论数据,并通过四维情感分析(积极/负面/需求/竞品)快速了解用户真实反馈,同时生成可离线保存的交互式 HTML 报告。
通过本技能,你可以:
- 📊 逐页获取视频一级评论(每页20条,支持翻页)
- 🤖 AI 自动完成四维情感分析,理解B站梗文化
- 📋 生成深色主题交互式 HTML 报告,便于保存与分享
适用于 UP主复盘、品牌舆情监测、竞品分析、内容策略研究等场景。
功能特性
核心功能
- 评论获取:粘贴视频链接或BV号即可拉取最新评论数据
- 分页浏览:每页20条,支持前后翻页,逐步浏览大量评论
- 四维 AI 分析:积极/负面/需求/竞品四类情感分析,各维度附代表评论引用
- 置顶标记:置顶评论特殊标识,一眼识别重要内容
- HTML 报告:深色主题交互式报告,支持离线保存与分享
特色亮点
- B站梗文化理解:识别弹幕术语、缩写(awsl / yyds / xswl / 爷青回 等)及热门梗的真实语境
- 数据完整展示:作品详情(播放/点赞/投币/收藏/弹幕等)与评论表格全量展示,不截断
- 编码安全回填:智能编码保障,中文分析内容完整无误地注入报告
鉴权
- 获取 API Key:前往 红狐hub
- 配置方式1:写入
~/.openclaw/openclaw.json→{ "env": { "REDFOX_API_KEY": "ak_xxxx..." } } - 配置方式2:终端执行
export REDFOX_API_KEY="ak_xxxx..."
工作流程
核心执行流程详见
references/core_workflow.md
使用指南
直接用自然语言说出视频链接或BV号即可,无需记忆固定命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 查看视频评论 | 「查看B站视频 BV1xx411c7mD 的评论」 | 获取第1页评论 + AI总结 |
| 通过链接查询 | 粘贴 https://www.bilibili.com/video/BV1xx411c7mD |
自动提取BV号并拉取评论 |
| 翻页 | 「下一页」「第3页」 | 沿用当前视频,获取指定页评论 |
| 生成报告 | 「需要」「生成HTML」 | 回填AI分析,生成交互式HTML报告 |
输出示例
完成查询后,你将依次收到:
- 作品详情(标题、UP主、播放量、点赞等核心数据)
- 评论表格(当前页全部评论,含昵称链接、内容、点赞数、时间、IP属地)
- 四维情感分析(积极/负面/需求/竞品,各附代表评论)
- 询问是否生成 HTML 报告
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 内容复盘 | UP主 | 「看看这个视频的评论怎么样」 | 快速了解观众反馈,优化选题策略 |
| 舆情监测 | 品牌运营 | 「分析这条视频的负面评论」 | 及时发现危机信号,制定应对方案 |
| 竞品分析 | 内容策划 | 「分析竞品视频的用户评论」 | 了解竞品优劣势,找准差异化方向 |
| 用户研究 | 产品经理 | 「看看用户在评论区提了哪些需求」 | 挖掘真实需求,指导产品迭代 |
项目架构
bilibili-comment/
├── SKILL.md # 技能配置与使用说明
├── references/
│ └── core_workflow.md # Agent 核心执行流程
├── assets/
│ └── report_template.html # HTML 报告模板
└── scripts/
├── bili_comment_search.py # 评论获取脚本(调用 API,自动生成 HTML)
└── backfill_html.py # AI 分析回填脚本(将分析结果写入 HTML)
| 模块 | 职责 |
|---|---|
bili_comment_search.py |
调用红狐 API 获取评论数据,生成含占位符的 HTML 报告 |
backfill_html.py |
将 AI 四维分析 JSON 安全回填至 HTML 报告,支持文件直读模式 |
report_template.html |
深色主题交互式报告模板 |
技术栈:Python 标准库(urllib),无需额外安装依赖。
常见问答
安装相关
Q1: 如何获取 REDFOX_API_KEY?
A: 前往 红狐hub 注册账号并申请 API Key,按鉴权章节说明配置到本地环境变量。
Q2: 需要安装额外依赖吗?
A: 不需要。脚本使用 Python 标准库 urllib,Python 3.6+ 环境即可直接运行。
使用相关
Q3: 一次能获取多少条评论?
A: 每次调用获取1页(20条)。如需更多数据,通过翻页命令逐页获取,或前往 红狐平台 联系客服获取批量能力。
Q4: 支持获取二级评论(回复)吗?
A: 当前版本仅支持一级评论,二级回复暂不支持。
故障排除
Q5: HTML 报告中部分中文显示乱码(问号)怎么办?
A: 这是编码回填环节的兼容性问题。解决方案:用 Python 写入分析数据为 UTF-8 JSON 文件,再使用 backfill_html.py --json-file 直接读取回填。详见 references/core_workflow.md Step 3。
Q6: 如有其他问题如何获取帮助?
A: 前往 红狐平台 联系客服,或查阅技能文档。
Files (redfox-community)
-
assets
-
report_template.html 14.5 KB · in bundle
-
-
references
-
core_workflow.md 4.3 KB
# Core Workflow ## Step 1:理解用户意图,提取 bvId - 从用户输入中提取 bvId(BV号);若提供视频链接则从链接中提取 - 若用户未提供,主动询问:「请提供B站视频链接或BV号」 - 若上一轮已查询某视频且本轮输入模糊(如「下一页」「评论分析」),沿用上一轮 bvId ## Step 2:调用评论获取脚本 每次调用仅请求一页数据(一次 API 请求),不可擅自发起多次调用拉取多页。 ```bash python3 "$SKILL_PATH/scripts/bili_comment_search.py" "<bvId>" [--page <page>] ``` 参数:`bvId`(必填)、`--page`(默认1)、`--output-dir`(默认 ~/Downloads/QoderReports) 脚本自动生成含 `{{PLACEHOLDER}}` 占位符的 HTML 报告(未回填分析数据)。 脚本返回 JSON 字段:`work_detail`、`total_count`、`total_fetched`、`has_next`、`comments`、`html_path` ## Step 3:对话中展示评论数据 + AI 总结 **A0(每次查询必须输出)** 作品详情(标题、UP主链接、发布时间、时长、播放/点赞/投币/收藏/分享/评论/弹幕数);数字 ≥ 10000 使用 x.xw 格式。 **A1** 告知查询范围(获取条数) **A2** 渲染评论表格:用户昵称带 Bilibili 主页链接、评论超40字截断加 `...`、时间格式 MM-DD HH:MM、点赞数 ≥ 10000 用 x.xw 格式;每页最多20条全部展示,不得截断。 **A3(每次查询必须输出)** 四维情感分析(积极/负面/需求/竞品): - 基于B站梗文化理解(awsl / 爷青回 / xswl / yyds / 绝绝子 等) - 每条要点引用代表评论关键词/短语 - 百分比为各类评论占总评论数比例,四类总和可超100% - 若 `total_fetched` < `work_detail.reply_count`,追加提示引导用户前往红狐平台 A0~A3 在同一轮输出中连续完成,不可省略任何一步。 A3 完毕后询问用户:「📊 是否需要生成 HTML 可视化报告?」 **用户确认后(无需重新调用脚本,HTML 已在 Step 2 生成):** **① 写入分析 JSON 文件(用 Python,确保 UTF-8)** ⚠️ 不可用 Write 工具写 JSON 文件(Windows 上默认 GBK 会损坏中文)。 必须用 Python 写: ```bash python -c "import json; json.dump({分析数据}, open('temp.json','w',encoding='utf-8'), ensure_ascii=False)" ``` 若 Python 字符串中含中文标点/引号导致 PowerShell 转义错误,改用 unicode 转义: ```bash $env:PYTHONIOENCODING='utf-8'; python -c "import json; d={...}; open('temp.json','w',encoding='utf-8').write(json.dumps(d,ensure_ascii=False))" ``` **② 回填 AI 分析到 HTML(--json-file 绕过管道)** ```bash python "$SKILL_PATH/scripts/backfill_html.py" "<html_path>" --json-file "<temp_json_file>" ``` JSON 字段:`positive_ratio`、`negative_ratio`、`demand_ratio`、`competitor_ratio`(纯整数); `positive_summary`、`negative_summary`、`demand_summary`、`competitor_summary`(HTML `<ul><li>` 格式) **③ 打开 HTML 报告** - Windows: `Start-Process "<html_path>"` - macOS/Linux: `open "<html_path>"` ## Step 4:翻页处理 当用户回复「下一页」「上一页」「第N页」时: 1. 沿用已有 bvId,计算新 page 值(`offset = (page - 1) × 20`) 2. 重新调用脚本(`--page <page>`),脚本自动生成当页 HTML 3. 完整执行 Step 3(A1~A3),A3 后询问是否生成当前页 HTML 4. 翻页规则:`has_next=true` 有下一页;`has_next=false` 已是最后一页 ## Step 5:错误处理 | 错误类型 | 处理方式 | |---------|---------| | 无 API Key | 提示配置 REDFOX_API_KEY,给出配置指引 | | 视频链接无效 | 提示「未找到该视频的评论,请检查视频链接是否正确」 | | 接口返回 502 错误 | 服务返回 502 错误,可能存在网络不稳定问题,请稍后重试 | | 获取 0 条评论 | 提示「该视频暂无评论」并建议检查视频是否存在或已删除 | | 网络请求超时 | 提示「网络请求超时,请稍后重试」 | | 评论任务超时 | 提示「评论数据获取超时,请稍后重试」 | | HTML 中文乱码 | 根本原因:Write 工具写 JSON 为 GBK 或 PowerShell 管道二次编码损坏。解决方案:用 Python `json.dump(ensure_ascii=False)` + `open(f,'w',encoding='utf-8')` 写 JSON,再用 `backfill_html.py --json-file` 直接读文件回填,全程不经过 Write 工具和管道 |
-
-
scripts
-
backfill_html.py 3.7 KB
#!/usr/bin/env python3 """ HTML 报告分析数据回填脚本 将 AI 分析结果回填到脚本生成的 HTML 报告中的 {{PLACEHOLDER}} 占位符。 用法: python3 backfill_html.py <html_path> --json-file <json_file> python3 backfill_html.py <html_path> --analysis-json '<json>' 或通过 stdin 传入 JSON: echo '<json>' | python3 backfill_html.py <html_path> 推荐 --json-file:从文件直接读取,彻底规避 Windows 管道/Write 工具编码问题。 """ import sys import os import json import argparse def backfill(html_path: str, analysis: dict) -> None: """读取 HTML 文件,替换分析占位符,写回文件""" if not os.path.exists(html_path): print(f"[error] HTML 文件不存在: {html_path}", file=sys.stderr) sys.exit(1) with open(html_path, "r", encoding="utf-8") as f: html = f.read() # 统计栏比例 ratio_map = { "{{POSITIVE_RATIO}}": str(analysis.get("positive_ratio", "--")), "{{NEGATIVE_RATIO}}": str(analysis.get("negative_ratio", "--")), "{{DEMAND_RATIO}}": str(analysis.get("demand_ratio", "--")), "{{COMPETITOR_RATIO}}": str(analysis.get("competitor_ratio", "--")), } for key, val in ratio_map.items(): html = html.replace(key, val) # 摘要卡片内容 summary_map = { "{{SUMMARY_POSITIVE}}": analysis.get("positive_summary", ""), "{{SUMMARY_NEGATIVE}}": analysis.get("negative_summary", ""), "{{SUMMARY_DEMAND}}": analysis.get("demand_summary", ""), "{{SUMMARY_COMPETITOR}}": analysis.get("competitor_summary", ""), } for key, val in summary_map.items(): html = html.replace(key, val) with open(html_path, "w", encoding="utf-8") as f: f.write(html) print(f"[backfill] 分析数据已回填到: {html_path}", file=sys.stderr) def main(): parser = argparse.ArgumentParser(description="HTML 报告分析数据回填") parser.add_argument("html_path", help="HTML 报告文件路径") parser.add_argument( "--analysis-json", dest="analysis_json", type=str, default=None, help='分析数据 JSON 字符串,格式: {"positive_ratio":45,"positive_summary":"<ul>...</ul>",...}', ) parser.add_argument( "--json-file", dest="json_file", type=str, default=None, help="分析数据 JSON 文件路径(推荐,绕过管道编码问题)", ) args = parser.parse_args() # 优先级:--json-file > --analysis-json > stdin if args.json_file: if not os.path.exists(args.json_file): print(f"[error] JSON 文件不存在: {args.json_file}", file=sys.stderr) sys.exit(1) try: with open(args.json_file, "r", encoding="utf-8") as f: analysis = json.load(f) except json.JSONDecodeError as e: print(f"[error] JSON 文件解析失败: {e}", file=sys.stderr) sys.exit(1) elif args.analysis_json: try: analysis = json.loads(args.analysis_json) except json.JSONDecodeError as e: print(f"[error] JSON 解析失败: {e}", file=sys.stderr) sys.exit(1) else: # 从 stdin 读取 JSON(显式 UTF-8,避免 Windows 管道编码问题) try: raw = sys.stdin.buffer.read().decode('utf-8') analysis = json.loads(raw) if raw.strip() else {} except json.JSONDecodeError as e: print(f"[error] stdin JSON 解析失败: {e}", file=sys.stderr) sys.exit(1) if not analysis: print("[warn] 未提供分析数据,跳过回填", file=sys.stderr) return backfill(args.html_path, analysis) if __name__ == "__main__": main() -
bili_comment_search.py 16.2 KB
#!/usr/bin/env python3 """ B站评论分析脚本 调用 Redfox API 获取B站视频一级评论数据,同步生成 HTML 报告 两步异步接口:commentSubmit 提交任务 → commentResult 轮询结果 用法: python3 bili_comment_search.py "<bvId>" [--page 1] [--no-html] [--output-dir ~/Downloads/QoderReports] """ import sys import os import re import json import time import argparse import urllib.request import urllib.error import urllib.parse from datetime import datetime SUBMIT_API = "https://redfox.hk/story/api/bili/commentSubmit" RESULT_API = "https://redfox.hk/story/api/bili/commentResult" WORK_DETAIL_API = "https://redfox.hk/story/api/bili/workDetail" PAGE_SIZE = 20 # 每页固定 20 条 MAX_POLL_ATTEMPTS = 30 # 最大轮询次数 POLL_INTERVAL = 2 # 轮询间隔(秒) # 脚本所在目录,用于定位模板 SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) TEMPLATE_PATH = os.path.join(SCRIPT_DIR, "..", "assets", "report_template.html") def get_api_key() -> str: val = os.environ.get("REDFOX_API_KEY", "") if not val: print("[error] 未找到环境变量 REDFOX_API_KEY,请确认已设置 API Key", file=sys.stderr) print("[hint] 获取 API Key: https://redfox.hk/settings/api-keys?source=github", file=sys.stderr) print("[hint] 配置: export REDFOX_API_KEY=ak_xxxx...", file=sys.stderr) sys.exit(1) return val def extract_bv_id(input_str: str) -> str: """从用户输入中提取 BV号(支持直接 BV号或完整链接)""" input_str = input_str.strip() # 匹配 BV号(以 BV 开头,后跟字母数字) match = re.search(r"(BV[A-Za-z0-9]+)", input_str) if match: return match.group(1) return input_str def fetch_work_detail(bv_id: str, api_key: str) -> dict: """获取作品详情(同步接口,POST JSON)""" payload = { "bvid": bv_id, "source": "B站评论分析-GitHub", } result = api_request(WORK_DETAIL_API, payload, api_key) code = result.get("code") if code != 2000: print(f"[warn] 获取作品详情失败: code={code}, msg={result.get('msg', '未知')}", file=sys.stderr) return {} data = result.get("data") or {} if not data: return {} # 发布时间(时间戳转字符串) publish_time = data.get("publishTime") or "" if isinstance(publish_time, (int, float, str)): try: ts = int(publish_time) if ts > 1000000000: publish_time = datetime.fromtimestamp(ts).strftime("%Y-%m-%d %H:%M:%S") except (ValueError, TypeError): publish_time = str(publish_time) # 时长(秒转 MM:SS) duration = data.get("duration") or 0 if isinstance(duration, (int, float)): mins = int(duration) // 60 secs = int(duration) % 60 duration_str = f"{mins}:{secs:02d}" else: duration_str = str(duration) owner = data.get("owner") or {} stats = data.get("stats") or {} return { "title": data.get("title") or "", "author_name": owner.get("nickname") or "", "author_uid": str(owner.get("uid") or ""), "author_avatar": owner.get("avatar") or "", "cover": data.get("cover") or "", "publish_time": publish_time, "duration": duration_str, "view_count": stats.get("viewCount") or 0, "like_count": stats.get("likeCount") or 0, "coin_count": stats.get("coinCount") or 0, "favorite_count": stats.get("favoriteCount") or 0, "share_count": stats.get("shareCount") or 0, "reply_count": stats.get("replyCount") or 0, "danmaku_count": stats.get("danmakuCount") or 0, } def api_request(url: str, payload: dict, api_key: str) -> dict: """通用 API 请求函数""" data = json.dumps(payload).encode("utf-8") req = urllib.request.Request( url, data=data, headers={ "Content-Type": "application/json", "X-API-Key": api_key, "User-Agent": "QoderWork/1.0", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as resp: result = json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") print(f"[error] HTTP {e.code}: {body}", file=sys.stderr) sys.exit(1) except urllib.error.URLError as e: print(f"[error] 网络请求失败: {e.reason}", file=sys.stderr) sys.exit(1) except (json.JSONDecodeError, KeyError, TypeError) as e: print(f"[error] 数据解析异常: {e}", file=sys.stderr) sys.exit(1) return result def submit_comment_task(bv_id: str, offset: int, api_key: str, sort_type: str = "1", data_num: int = PAGE_SIZE) -> str: """提交评论查询任务,返回 taskId API 必填参数:opusId(视频标识)、sortType(排序方式)、dataNum(每页条数) """ payload = { "opusId": bv_id, "sortType": sort_type, "dataNum": str(data_num), "offset": str(offset), "source": "B站评论分析-GitHub", } result = api_request(SUBMIT_API, payload, api_key) code = result.get("code") if code != 2000: print(f"[error] 提交任务失败: code={code}, msg={result.get('msg', '未知')}", file=sys.stderr) sys.exit(1) data = result.get("data") or {} task_id = data.get("taskId") or data.get("task_id") or "" if not task_id: # 某些接口可能直接在 data 中返回结果(非异步),将完整 data 作为结果返回 return "__direct__" return task_id def poll_comment_result(task_id: str, api_key: str) -> dict: """轮询评论结果(POST + form-urlencoded),直到数据就绪或超时。 commentResult 接口要求 POST + application/x-www-form-urlencoded 格式。 """ for attempt in range(MAX_POLL_ATTEMPTS): form_data = urllib.parse.urlencode({ "taskId": task_id, "source": "B站评论分析-GitHub", }).encode("utf-8") req = urllib.request.Request( RESULT_API, data=form_data, headers={ "Content-Type": "application/x-www-form-urlencoded", "X-API-Key": api_key, "User-Agent": "QoderWork/1.0", }, method="POST", ) try: with urllib.request.urlopen(req, timeout=30) as resp: result = json.loads(resp.read().decode("utf-8")) except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") print(f"[error] HTTP {e.code}: {body}", file=sys.stderr) sys.exit(1) except urllib.error.URLError as e: print(f"[error] 网络请求失败: {e.reason}", file=sys.stderr) sys.exit(1) except (json.JSONDecodeError, KeyError, TypeError) as e: print(f"[error] 数据解析异常: {e}", file=sys.stderr) sys.exit(1) code = result.get("code") if code != 2000: print(f"[error] 查询结果失败: code={code}, msg={result.get('msg', '未知')}", file=sys.stderr) sys.exit(1) data = result.get("data") or {} # 检查任务状态 status = data.get("status") or data.get("state") or "" if status in ("pending", "processing", "running"): print(f"[poll] 第 {attempt + 1} 次轮询,任务状态: {status},等待 {POLL_INTERVAL}s...", file=sys.stderr) time.sleep(POLL_INTERVAL) continue # 任务完成或数据已就绪 return result print(f"[error] 评论数据获取超时(已轮询 {MAX_POLL_ATTEMPTS} 次),请稍后重试", file=sys.stderr) sys.exit(1) def format_comment(raw: dict) -> dict: """将 API 返回的原始评论格式化为统一结构 commentResult 实际返回字段: commentId, content, likeNum, createTime, ipLocation, nickname, commenterUid, avatar, avId, isPinned, pictures 输出字段:用户昵称、用户ID、评论内容、点赞数、评论时间、IP属地 """ user_name = (raw.get("nickname") or raw.get("authorName") or "").strip() user_id = str(raw.get("commenterUid") or raw.get("authorUid") or "") content = raw.get("content") or raw.get("message") or raw.get("text") or "" if isinstance(content, dict): content = content.get("message", "") or content.get("content", "") like_count = raw.get("likeNum") or raw.get("like") or raw.get("likeCount") or 0 create_time = raw.get("createTime") or raw.get("ctime") or raw.get("publishTime") or "" if isinstance(create_time, (int, float)) and create_time > 1000000000: try: create_time = datetime.fromtimestamp(int(create_time)).strftime("%Y-%m-%d %H:%M:%S") except Exception: create_time = str(create_time) ip_raw = raw.get("ipLocation") or "" ip_location = ip_raw.replace("IP属地:", "").replace("IP属地:", "").strip() return { "user_name": user_name, "user_id": user_id, "content": str(content).strip(), "like_count": int(like_count) if like_count else 0, "create_time": str(create_time), "ip_location": ip_location, } def fetch_comments(bv_id: str, page: int, api_key: str, sort_type: str = "1") -> tuple: """ 两步获取评论:提交任务 → 轮询结果。 返回 (comments_list, has_next, total_count) """ offset = (page - 1) * PAGE_SIZE # Step 1: 提交任务 task_id = submit_comment_task(bv_id, offset, api_key, sort_type=sort_type) # Step 2: 获取结果 if task_id == "__direct__": payload = { "opusId": bv_id, "sortType": sort_type, "dataNum": str(PAGE_SIZE), "offset": str(offset), "source": "B站评论分析-GitHub", } result = api_request(SUBMIT_API, payload, api_key) else: result = poll_comment_result(task_id, api_key) data = result.get("data") or {} # 评论列表 comment_list = data.get("commentList") or data.get("comments") or [] if not isinstance(comment_list, list): comment_list = [] all_comments = [format_comment(c) for c in comment_list] # 分页判断:基于 total/received 字段 total_count = data.get("total", 0) or 0 received = data.get("received", 0) or 0 has_next = bool(data.get("hasMore") or data.get("has_next") or False) if not has_next: # 如果 offset + 本页数量 < 总评论数,说明还有下一页 has_next = (offset + len(all_comments)) < total_count return all_comments, has_next, total_count def escape_html(text: str) -> str: """HTML 实体转义""" return ( (text or "") .replace("&", "&") .replace("<", "<") .replace(">", ">") .replace('"', """) ) def build_comment_rows(comments: list) -> str: """根据评论列表生成 HTML 表格行""" rows = [] for c in comments: name = escape_html(c.get("user_name", "")) uid = escape_html(c.get("user_id", "")) content = escape_html(c.get("content", "")) like = c.get("like_count", 0) or 0 time_str = (c.get("create_time", "") or "")[5:16] # MM-DD HH:MM ip = escape_html(c.get("ip_location", "")) user_url = f"https://space.bilibili.com/{uid}" if uid and uid != "0" else "#" row = ( f'<tr>' f'<td><a href="{user_url}" target="_blank" class="user-name">{name}</a></td>' f'<td class="uid-cell">{uid}</td>' f'<td class="content-cell">{content}</td>' f'<td class="num-cell">{like}</td>' f'<td class="time-cell">{time_str}</td>' f'<td class="ip-cell">{ip}</td>' f'</tr>' ) rows.append(row) return "\n".join(rows) def generate_html_report(bv_id: str, current_page: int, comments: list, work_detail: dict = None, output_dir: str = None) -> str: """读取 HTML 模板,填充评论数据,生成报告文件。返回生成的 HTML 文件绝对路径。""" template_path = os.path.normpath(TEMPLATE_PATH) if not os.path.exists(template_path): print(f"[warn] 模板文件不存在: {template_path},跳过 HTML 生成", file=sys.stderr) return "" with open(template_path, "r", encoding="utf-8") as f: html = f.read() total = len(comments) now = datetime.now() total_likes = sum(c.get("like_count", 0) or 0 for c in comments) wd = work_detail or {} replacements = { "{{VIDEO_ID}}": escape_html(bv_id), "{{DATE}}": now.strftime("%Y-%m-%d"), "{{TOTAL_COMMENTS}}": str(total), "{{COMMENT_ROWS}}": build_comment_rows(comments), "{{TIMESTAMP}}": now.strftime("%Y-%m-%d %H:%M:%S"), "{{TOTAL_LIKES}}": str(total_likes), "{{PAGE}}": str(current_page), "{{TITLE}}": escape_html(wd.get("title") or ""), "{{AUTHOR_NAME}}": escape_html(wd.get("author_name") or ""), "{{AUTHOR_UID}}": escape_html(wd.get("author_uid") or ""), "{{AUTHOR_AVATAR}}": wd.get("author_avatar") or "", "{{COVER}}": wd.get("cover") or "", "{{PUBLISH_TIME}}": escape_html(wd.get("publish_time") or ""), "{{DURATION}}": escape_html(wd.get("duration") or ""), "{{VIEW_COUNT}}": str(wd.get("view_count") or 0), "{{LIKE_COUNT}}": str(wd.get("like_count") or 0), "{{COIN_COUNT}}": str(wd.get("coin_count") or 0), "{{FAVORITE_COUNT}}": str(wd.get("favorite_count") or 0), "{{SHARE_COUNT}}": str(wd.get("share_count") or 0), "{{REPLY_COUNT}}": str(wd.get("reply_count") or 0), "{{DANMAKU_COUNT}}": str(wd.get("danmaku_count") or 0), } for key, val in replacements.items(): html = html.replace(key, val) # 确定输出目录 if output_dir: out_dir = os.path.expanduser(output_dir) else: out_dir = os.path.expanduser("~/Downloads/QoderReports") os.makedirs(out_dir, exist_ok=True) filename = f"B站评论分析_{bv_id}_p{current_page}.html" file_path = os.path.join(out_dir, filename) with open(file_path, "w", encoding="utf-8") as f: f.write(html) print(f"[html] HTML 报告已生成: {file_path}", file=sys.stderr) return file_path def main(): parser = argparse.ArgumentParser(description="B站视频评论查询") parser.add_argument("bv_id", help="B站视频 BV号、AV号或完整链接") parser.add_argument( "--page", dest="page", type=int, default=1, help="页码,从1开始(默认1,每页20条)", ) parser.add_argument( "--sort", dest="sort_type", default="1", choices=["1", "2", "3"], help="排序方式:1-按时间 2-按热度 3-按回复数(默认1)", ) parser.add_argument( "--no-html", dest="no_html", action="store_true", help="跳过 HTML 报告生成", ) parser.add_argument( "--output-dir", dest="output_dir", type=str, default=None, help="HTML 输出目录(默认 ~/Downloads/QoderReports)", ) args = parser.parse_args() bv_id = extract_bv_id(args.bv_id) if not bv_id: print("[error] 视频ID不能为空", file=sys.stderr) sys.exit(1) if args.page < 1: print("[error] 页码必须为正整数", file=sys.stderr) sys.exit(1) api_key = get_api_key() # 获取作品详情(同步接口,与评论获取并行逻辑但顺序执行) work_detail = fetch_work_detail(bv_id, api_key) comments, has_next, total_count = fetch_comments(bv_id, args.page, api_key, sort_type=args.sort_type) total = len(comments) output = { "work_detail": work_detail, "total_count": total_count, "total_fetched": total, "has_next": has_next, "comments": comments, } # HTML 报告生成 if not args.no_html: html_path = generate_html_report( bv_id=bv_id, current_page=args.page, comments=comments, work_detail=work_detail, output_dir=args.output_dir, ) output["html_path"] = html_path print(json.dumps(output, ensure_ascii=False, indent=2)) if __name__ == "__main__": main()
-
-
README.en.md 5.1 KB
# Bilibili Comment Analyzer / bilibili-comment --- ## Overview Enter any Bilibili video link or BV ID to **instantly retrieve comment data**. AI automatically performs four-dimension sentiment analysis (positive / negative / demand / competitor) and generates an interactive HTML report for offline saving. **Feature Overview** Bilibili Comment Analyzer is an intelligent comment insight tool built for content creators, UP owners, and brand operators. It pulls first-level comments in real time via the RedFox API — 20 comments per page with pagination support. The AI deeply understands Bilibili meme culture (awsl / yyds / 爷青回 and other barrage slang) and delivers four-dimension analysis with representative comment citations, then generates a dark-themed interactive HTML report for easy saving and sharing. **Core Value** - **Real-time comment retrieval**: Paste a link and get the latest comments instantly — no manual scrolling through the comment section. - **Bilibili meme culture understanding**: AI identifies barrage slang and meme context for more accurate analysis results. - **Structured four-dimension analysis**: Positive / negative / demand / competitor layers clearly separated, each with representative comment citations to capture the core of public sentiment quickly. **Intended Users** - 🎬 **UP owners** — Review audience feedback and refine topic and content strategy. - 🏢 **Brand operators** — Monitor public sentiment and detect user voices and risk signals promptly. - 📊 **Content planners / product managers** — Mine competitor comments and research genuine user needs. --- ## Features ### Core Capabilities - **Comment retrieval**: Enter a BV ID or video link to automatically pull first-level comments, 20 per page - **Pagination**: Navigate with natural language — "next page", "page 3", etc. — to browse large volumes of comments progressively - **Four-dimension AI analysis**: Positive / negative / demand / competitor sentiment analysis, each with representative comment citations and percentage breakdown - **Pinned comment marking**: Pinned comments are specially highlighted for quick identification - **HTML report**: Dark-themed interactive report for offline saving and sharing ### Highlights - **Bilibili meme culture understanding**: Recognizes awsl / yyds / xswl / 爷青回 / 下次一定 and other barrage slang in their real context - **Complete data display**: Work details (views / likes / coins / favorites / danmaku, etc.) and full comment table — no truncation - **Encoding-safe backfill**: Intelligent encoding ensures Chinese analysis content is injected into reports without corruption --- ## API Key Acquisition & Security - This skill requires the environment variable: `REDFOX_API_KEY`. - `REDFOX_API_KEY` is issued by [RedFox Hub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk`). - Register at [RedFox Hub](https://redfox.hk?source=github) to obtain `REDFOX_API_KEY`. - Configure `REDFOX_API_KEY` as a device environment variable before using this skill. - Before providing your key, confirm its source, scope, validity period, and whether it can be reset or revoked. - Do not hard-code or expose keys in plain text in code, prompts, logs, or output files. --- ## Usage Guide Simply describe your video in natural language — no fixed commands to memorize. ### Quick Reference | Intent | Example phrase | Result | |--------|---------------|--------| | View video comments | "Show me the comments for BV1xx411c7mD" | Fetches page 1 comments + AI summary | | Query via link | Paste `https://www.bilibili.com/video/BV1xx411c7mD` | Auto-extracts BV ID and retrieves comments | | Paginate | "Next page" / "Page 3" | Reuses current video BV ID, fetches specified page | | Generate report | "Yes" / "Generate HTML" | Backfills AI analysis, generates interactive HTML report | ### Output Example After a successful query, you receive in sequence: **Work Details** > 🎬 **Video Title** > > UP: [Username](profile link) | Published: 2024-01-01 | Duration: 12:34 > > ▶ 12.5w 👍 3.2w 🪙 1.8w ⭐ 2.1w 🔄 5.6k 💬 1.2k 📝 8.9k **Comment Table** (all comments on current page) | # | Username | Comment | 👍 | Time | IP | |---|---------|---------|-----|------|-----| | 1 | [Username](profile) | Comment content... | 1223 | 01-12 15:30 | Guangdong | **Four-dimension Sentiment Analysis** (AI summary with representative citations) **Prompt asking whether to generate an HTML visualization report** --- ## Use Cases | Scenario | Role | Example question | Benefit | |----------|------|-----------------|---------| | Content review | UP owner | "How are the comments on this video?" | Quickly understand audience feedback and refine content strategy | | Sentiment monitoring | Brand operator | "Analyze the negative comments on this video" | Detect crisis signals early and plan response | | Competitor analysis | Content planner | "Analyze user comments on a competitor's video" | Understand competitor strengths and weaknesses to find differentiation | | User research | Product manager | "What user needs are mentioned in the comments?" | Uncover genuine needs to guide product iteration | --- -
README.md 4.4 KB
# B站评论分析 / bilibili-comment --- ## 简介 输入任意B站视频链接或BV号,**一键获取评论数据**,AI 自动完成四维情感分析(积极/负面/需求/竞品),同时生成可离线保存的交互式 HTML 报告。 **功能介绍** B站评论分析是专为内容创作者、UP主和品牌运营打造的智能评论洞察工具。基于红狐 API 实时拉取视频一级评论,每页20条,支持分页浏览;AI 深度理解B站梗文化(awsl / yyds / 爷青回 等弹幕术语),完成积极/负面/需求/竞品四维分析并引用代表评论;最终生成深色主题交互式 HTML 报告,便于保存与分享。 **核心价值** - **实时评论拉取**:粘贴链接即可获取最新评论,无需手动翻找评论区。 - **B站梗文化理解**:AI 识别弹幕术语和梗的真实语境,分析结果更准确。 - **结构化四维分析**:积极/负面/需求/竞品清晰分层,各维度附代表评论引用,快速抓住舆情核心。 **适用对象** - 🎬 **UP主** — 复盘视频反馈,优化选题和内容策略。 - 🏢 **品牌运营** — 监测舆情,及时发现用户声音和风险信号。 - 📊 **内容策划 / 产品经理** — 挖掘竞品评论,研究用户真实需求。 --- ## 功能特性 ### 核心功能 - **评论获取**:输入BV号或视频链接,自动拉取一级评论,每页20条 - **分页浏览**:支持「下一页」「第N页」等自然语言翻页,逐步浏览大量评论 - **四维 AI 分析**:积极/负面/需求/竞品四类情感分析,各附代表评论引用,百分比展示 - **置顶标记**:置顶评论特殊标识,一眼识别重要内容 - **HTML 报告**:深色主题交互式报告,支持离线保存与分享 ### 特色亮点 - **B站梗文化理解**:识别 awsl / yyds / xswl / 爷青回 / 下次一定 等弹幕术语及梗的真实含义 - **数据完整展示**:作品详情(播放/点赞/投币/收藏/弹幕等)与评论表格全量呈现 - **编码安全回填**:智能编码保障,中文分析内容完整无误地注入报告 --- ## 密钥获取与安全说明 - 本技能需要使用环境变量:`REDFOX_API_KEY`。 - `REDFOX_API_KEY` 由 [红狐 hub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk`) 提供。 - 请前往 [红狐 hub](https://redfox.hk?source=github) 注册账号,获取 `REDFOX_API_KEY`。 - 配置设备环境变量 `REDFOX_API_KEY` 后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。 - 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。 --- ## 使用指南 直接用自然语言说出视频链接或BV号即可,无需记忆固定命令。 ### 常用说法速查 | 意图 | 示例话术 | 效果 | |------|---------|------| | 查看视频评论 | 「查看B站视频 BV1xx411c7mD 的评论」 | 获取第1页评论 + AI总结 | | 通过链接查询 | 粘贴 `https://www.bilibili.com/video/BV1xx411c7mD` | 自动提取BV号并拉取评论 | | 翻页 | 「下一页」「第3页」 | 沿用当前视频,获取指定页评论 | | 生成报告 | 「需要」「生成HTML」 | 回填AI分析,生成交互式HTML报告 | ### 输出示例 完成查询后,你将依次收到: **作品详情** > 🎬 **视频标题** > > UP主:[昵称](主页链接) | 发布:2024-01-01 | 时长:12:34 > > ▶ 12.5w 👍 3.2w 🪙 1.8w ⭐ 2.1w 🔄 5.6k 💬 1.2k 📝 8.9k **评论表格**(当前页全部评论) | # | 用户昵称 | 评论内容 | 👍 | 时间 | IP | |---|---------|---------|-----|------|-----| | 1 | [用户名](主页) | 评论内容... | 1223 | 01-12 15:30 | 广东 | **四维情感分析**(AI总结,含代表评论引用) **询问是否生成 HTML 可视化报告** --- ## 使用场景 | 场景 | 角色 | 示例问法 | 收益 | |------|------|---------|------| | 内容复盘 | UP主 | 「看看这个视频的评论怎么样」 | 快速了解观众反馈,优化选题策略 | | 舆情监测 | 品牌运营 | 「分析这条视频的负面评论」 | 及时发现危机信号,制定应对方案 | | 竞品分析 | 内容策划 | 「分析竞品视频的用户评论」 | 了解竞品优劣势,找准差异化方向 | | 用户研究 | 产品经理 | 「看看用户在评论区提了哪些需求」 | 挖掘真实需求,指导产品迭代 | --- -
SKILL.md 5.8 KB
--- name: bilibili-comment description: B站评论分析工具。输入B站视频链接或BV号即可获取一级评论数据,支持分页浏览、评论情感分析(积极/负面/需求/竞品),生成精美 HTML 报告。当用户需要查看B站视频评论、分析评论舆情、了解用户反馈时使用。触发词:B站评论、B站视频评论、评论查询、评论分析、评论舆情、看评论、bilibili评论。 --- # B站评论分析 ## 简介 B站评论分析是一款专为内容创作者、UP主、品牌运营和研究人员设计的智能评论洞察工具。输入任意B站视频链接或BV号,即可一键获取评论数据,并通过四维情感分析(积极/负面/需求/竞品)快速了解用户真实反馈,同时生成可离线保存的交互式 HTML 报告。 通过本技能,你可以: - 📊 逐页获取视频一级评论(每页20条,支持翻页) - 🤖 AI 自动完成四维情感分析,理解B站梗文化 - 📋 生成深色主题交互式 HTML 报告,便于保存与分享 适用于 UP主复盘、品牌舆情监测、竞品分析、内容策略研究等场景。 ## 功能特性 ### 核心功能 - **评论获取**:粘贴视频链接或BV号即可拉取最新评论数据 - **分页浏览**:每页20条,支持前后翻页,逐步浏览大量评论 - **四维 AI 分析**:积极/负面/需求/竞品四类情感分析,各维度附代表评论引用 - **置顶标记**:置顶评论特殊标识,一眼识别重要内容 - **HTML 报告**:深色主题交互式报告,支持离线保存与分享 ### 特色亮点 - **B站梗文化理解**:识别弹幕术语、缩写(awsl / yyds / xswl / 爷青回 等)及热门梗的真实语境 - **数据完整展示**:作品详情(播放/点赞/投币/收藏/弹幕等)与评论表格全量展示,不截断 - **编码安全回填**:智能编码保障,中文分析内容完整无误地注入报告 ## 鉴权 - 获取 API Key:前往 [红狐hub](https://redfox.hk/settings/api-keys?source=github) - 配置方式1:写入 `~/.openclaw/openclaw.json` → `{ "env": { "REDFOX_API_KEY": "ak_xxxx..." } }` - 配置方式2:终端执行 `export REDFOX_API_KEY="ak_xxxx..."` ## 工作流程 > 核心执行流程详见 `references/core_workflow.md` ## 使用指南 直接用自然语言说出视频链接或BV号即可,无需记忆固定命令。 ### 常用说法速查 | 意图 | 示例话术 | 效果 | |------|---------|------| | 查看视频评论 | 「查看B站视频 BV1xx411c7mD 的评论」 | 获取第1页评论 + AI总结 | | 通过链接查询 | 粘贴 `https://www.bilibili.com/video/BV1xx411c7mD` | 自动提取BV号并拉取评论 | | 翻页 | 「下一页」「第3页」 | 沿用当前视频,获取指定页评论 | | 生成报告 | 「需要」「生成HTML」 | 回填AI分析,生成交互式HTML报告 | ### 输出示例 完成查询后,你将依次收到: 1. **作品详情**(标题、UP主、播放量、点赞等核心数据) 2. **评论表格**(当前页全部评论,含昵称链接、内容、点赞数、时间、IP属地) 3. **四维情感分析**(积极/负面/需求/竞品,各附代表评论) 4. **询问是否生成 HTML 报告** ## 使用场景 | 场景 | 角色 | 示例问法 | 收益 | |------|------|---------|------| | 内容复盘 | UP主 | 「看看这个视频的评论怎么样」 | 快速了解观众反馈,优化选题策略 | | 舆情监测 | 品牌运营 | 「分析这条视频的负面评论」 | 及时发现危机信号,制定应对方案 | | 竞品分析 | 内容策划 | 「分析竞品视频的用户评论」 | 了解竞品优劣势,找准差异化方向 | | 用户研究 | 产品经理 | 「看看用户在评论区提了哪些需求」 | 挖掘真实需求,指导产品迭代 | ## 项目架构 ``` bilibili-comment/ ├── SKILL.md # 技能配置与使用说明 ├── references/ │ └── core_workflow.md # Agent 核心执行流程 ├── assets/ │ └── report_template.html # HTML 报告模板 └── scripts/ ├── bili_comment_search.py # 评论获取脚本(调用 API,自动生成 HTML) └── backfill_html.py # AI 分析回填脚本(将分析结果写入 HTML) ``` | 模块 | 职责 | |------|------| | `bili_comment_search.py` | 调用红狐 API 获取评论数据,生成含占位符的 HTML 报告 | | `backfill_html.py` | 将 AI 四维分析 JSON 安全回填至 HTML 报告,支持文件直读模式 | | `report_template.html` | 深色主题交互式报告模板 | 技术栈:Python 标准库(`urllib`),无需额外安装依赖。 ## 常见问答 ### 安装相关 **Q1: 如何获取 REDFOX_API_KEY?** A: 前往 [红狐hub](https://redfox.hk/settings/api-keys?source=github) 注册账号并申请 API Key,按鉴权章节说明配置到本地环境变量。 **Q2: 需要安装额外依赖吗?** A: 不需要。脚本使用 Python 标准库 `urllib`,Python 3.6+ 环境即可直接运行。 ### 使用相关 **Q3: 一次能获取多少条评论?** A: 每次调用获取1页(20条)。如需更多数据,通过翻页命令逐页获取,或前往 [红狐平台](https://redfox.hk?source=github) 联系客服获取批量能力。 **Q4: 支持获取二级评论(回复)吗?** A: 当前版本仅支持一级评论,二级回复暂不支持。 ### 故障排除 **Q5: HTML 报告中部分中文显示乱码(问号)怎么办?** A: 这是编码回填环节的兼容性问题。解决方案:用 Python 写入分析数据为 UTF-8 JSON 文件,再使用 `backfill_html.py --json-file` 直接读取回填。详见 `references/core_workflow.md` Step 3。 **Q6: 如有其他问题如何获取帮助?** A: 前往 [红狐平台](https://redfox.hk?source=github) 联系客服,或查阅技能文档。
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.