kuaishou-comment
快手评论分析工具。输入作品链接即可获取一级评论数据,支持分页浏览、评论情感分析(积极/负面/需求/竞品),生成精美 HTML 报告。当用户需要查看快手作品评论、分析评论舆情、了解用户反馈时使用。触发词:快手评论、作品评论、评论查询、评论分析、评论舆情、看评论。
Install
npx skills add https://github.com/redfox-data/redfox-community/tree/main/skills/kuaishou-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
快手评论分析 / kuaishou-comment
简介
输入快手作品链接即可获取评论数据,支持分页浏览、四维情感分析(积极/负面/需求/竞品)和精美可视化报告生成。
核心价值
- 即输即看:粘贴作品链接就能拉取评论区数据,每页展示完整评论表格,点赞、回复、IP 属地一览无余。
- 智能舆情分析:自动将评论归为积极、负面、需求、竞品四类,提取关键词和代表评论,快速了解舆论风向。
- 分页浏览,越看越全:支持逐页翻页,每次翻页自动累计分析所有已读评论,不遗漏任何信息。
- 一键导出报告:生成深色主题交互式 HTML 报告,多页自动合并、超 3 页智能折叠,支持离线保存和分享。
适用对象
- 🔍 快手创作者 — 快速了解作品评论区反馈,发现粉丝需求与选题灵感。
- 📊 运营 / 数据分析 — 追踪快手作品的用户舆情,掌握内容影响力与受众态度。
- 🏢 品牌 / MCN — 监控竞品与行业关键词的评论区表现,辅助内容策略决策。
功能特性
核心功能
- 评论获取:粘贴作品链接即获取评论,支持 cursor 游标逐页翻页,每页展示完整数据。
- 置顶标记:置顶评论特殊标识,一眼识别重要评论。
- 四维情感分析:每次查询自动输出积极/负面/需求/竞品四类占比与摘要,翻页时累计分析更全面。
- 可视化报告:单页生成包含当前页数据的 HTML 报告,多页自动合并为一份、超 3 页智能折叠。
密钥获取与安全说明
- 本技能需要使用环境变量:
REDFOX_API_KEY。 REDFOX_API_KEY由 红狐 hub (https://redfox.hk)提供。- 请前往 红狐 hub 注册账号,获取
REDFOX_API_KEY。 - 配置设备环境变量
REDFOX_API_KEY后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。
- 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。
使用指南
直接用自然语言发送快手作品链接或 opusId 即可,无需记忆命令。
常用说法速查
| 意图 | 示例话术 | 效果 |
|---|---|---|
| 查看评论 | 「https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu 这个作品的评论怎么样」 | 获取评论表格 + 四维情感分析 |
| 直接指定 ID | 「查看快手作品 3x4ibfzs5e68yxu 的评论」 | 同上,跳过链接提取 |
| 翻页 | 「下一页」 | 继续拉取下一页评论,累计分析 |
| 导出报告 | 「生成 html」 | 已浏览的所有页面合并为一份完整报告 |
输出示例
查询后你将收到:
📊 评论表格:当前页全部评论,含评论人、评论内容、点赞数、回复数、时间和 IP 属地。
📈 AI 评论总结:四维分析(积极/负面/需求/竞品),含占比、摘要和代表性评论引用。
📄 翻页提示:若还有下一页,提示「回复『下一页』继续查看」。
使用场景
| 场景 | 角色 | 示例问法 | 收益 |
|---|---|---|---|
| 查看作品反馈 | 快手创作者 | 「我刚发的视频评论区什么情况?」 | 快速了解观众反应,及时互动 |
| 舆情监控 | 品牌运营 | 「看看这个产品的快手评论区风向」 | 掌握竞品或自身舆情,辅助决策 |
| 内容选题 | 内容策划 | 「最近同赛道爆款视频都聊什么?」 | 从评论区发掘用户需求与选题灵感 |
| 数据分析 | 数据分析师 | 「帮我分析这个作品评论的情感分布」 | 量化评论数据,输出结构化报告 |
Skill manifest
快手评论分析
📝 简介
输入快手作品链接即可获取一级评论数据,每页 20 条,支持 cursor 游标分页翻页。在对话中展示当前页全部评论及 AI 总结分析,同时生成包含当前页数据和总结的交互式 HTML 报告。
✨ 功能特性
| 功能模块 | 能力描述 | 核心价值 |
|---|---|---|
| 评论获取 | 粘贴作品链接即可获取评论 | 实时拉取作品评论区数据 |
| 分页浏览 | 每页 20 条,cursor 游标翻页 | 逐页浏览大量评论 |
| AI 总结 | 四维情感分析(积极/负面/需求/竞品) | 快速了解评论舆情全貌 |
| 置顶标记 | 置顶评论特殊标识 | 一眼识别重要评论 |
| HTML 报告 | 深色主题交互式报告 | 离线保存/分享分析结果 |
| 合并报告 | 多页合并,超3页自动折叠 | 一次导出全部评论分析 |
🔑 鉴权
前往 红狐hub 获取 API Key,通过以下方式配置:
# 方式一:配置文件(如 OpenClaw 的 ~/.openclaw/openclaw.json)
{ "env": { "REDFOX_API_KEY": "ak_xxxx..." } }
# 方式二:终端环境变量
export REDFOX_API_KEY="ak_xxxx..."
🔄 工作流程
Step 1:理解用户意图,提取 opusId
⚠️ 核心规则:用户提供作品链接,Agent 从链接中提取 opusId。
- 用户提供作品链接(如
https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu),Agent 从链接中提取 opusId - 用户也可能直接提供 opusId(如
3x4ibfzs5e68yxu),此时直接使用 - 若用户未提供作品链接,主动询问:「请提供快手作品链接」
- 若用户在上一轮对话中查询过某作品的数据,且本轮输入模糊(如"下一页"、"评论分析"),沿用上一轮的 opusId
Step 2:调用评论获取脚本(自动生成 HTML)
⚠️ 每次调用仅请求一页数据(一次 API 请求),不可擅自发起多次调用拉取多页。
脚本每次调用自动生成 HTML 报告(含占位符,未回填分析数据)。终端展示与 HTML 基于同一次 API 调用,确保数据一致。
python3 ~/.agents/skills/kuaishou-comment/scripts/kuaishou_comment_search.py "<opusId>" [--cursor <cursor>] [--page <page>]
参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
opusId |
快手作品 opusId(必填) | — |
--cursor |
游标,首页传空字符串,次页传上一页返回的 nextCursor | "" |
--page |
当前页码(仅用于 HTML 文件名和输出标记) | 1 |
--output-dir |
HTML 输出目录 | ~/Downloads/QoderReports |
脚本返回 JSON 字段详见下方评论字段表。
注意: HTML 报告此时仅含评论原始数据,分析占位符待用户确认后回填。
Step 3:对话中展示评论数据 + AI 总结
⚠️ 每次操作仅调用一次 API,获取一页数据。
A1. 告知查询范围
📊 作品「」共获取 N 条评论(第 页),以下是详细数据:
A2. 渲染评论表格(展示当前页全部评论)
格式规则:
- 评论人列只显示昵称,不展示头像、不生成超链接
- 评论内容超过 40 字截断加 ...
- 时间格式化为 MM-DD HH:MM
- 置顶评论在昵称后加 📌 标记(整行加粗)
- like_count / reply_count 数字 ≥ 10000 使用 x.xw 格式
- 每页全部展示,不得截断
A3. AI 评论总结(⚠️ 每次查询(含翻页)必须输出)
首页:基于当前页评论,四维情感分析。
翻页后:基于目前已获取的所有页面累计数据综合分析,标注累计总条数。
分析前需理解快手社区文化(老铁文化、表情符号语义、"奥利给""双击"等特有表达)。
📈 评论总结分析(基于
✅ 积极评价(
- {要点,引用代表评论关键词}
⚠️ 负面评价(
- {要点}
💡 用户需求(
- {要点}
🔍 竞品对比舆情(
- {仅当提及竞品时输出,否则输出「未提及竞品」}
分析要求:
- 每条要点引用代表评论关键词/短语
- 百分比为该类型评论占比,四类总和可能超 100%
- 必须理解快手社区文化
A4. 翻页提示(⚠️ 紧接在 A3 之后)
- 若 has_next 为 true:
📄 当前第 {page} 页(共 {total_fetched} 条)。回复「下一页」继续查看。 - 若 has_next 为 false:
📄 当前第 {page} 页,已无更多数据。
⚠️ A1~A4 必须在同一轮输出中连续完成,不可省略任何一步。
A4 输出完毕后,询问用户是否需要生成 HTML 可视化报告:
📊 是否需要生成 HTML 可视化报告?
若用户确认,根据已浏览页数选择方式:
仅 1 页 → 单页 HTML(backfill_html.py)
HTML 文件已在 Step 2 生成,直接回填分析数据并打开:
python3 ~/.agents/skills/kuaishou-comment/scripts/backfill_html.py "<html_path>" --analysis-json '<分析JSON>'
open "<html_path>"
2 页及以上 → 合并报告(consolidate_report.py)
收集所有已浏览页面的 JSON 数据,生成一份包含全部评论和累计分析的完整 HTML。按页码分区展示,超过 3 页时自动折叠,提示用户点击展开。
python3 ~/.agents/skills/kuaishou-comment/scripts/consolidate_report.py "<opusId>" \
--pages-json '[{"page":1,"total":31,"comments":[...]},{"page":2,"total":20,"comments":[...]}]' \
--analysis-json '<累计分析JSON>'
open "<html_path>"
| 参数 | 说明 |
|---|---|
| opusId | 快手作品 opusId(位置参数) |
| --pages-json | 多页数据 JSON 数组,每项含 page、total、comments |
| --analysis-json | 累计分析 JSON(字段同 backfill) |
pages 数据从各次脚本调用输出中收集;累计分析基于 A3 翻页后综合分析。
分析 JSON 字段说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| positive_ratio | string | 积极评价占比(纯数字,如 "62",模板自动追加 %) |
| negative_ratio | string | 负面评价占比 |
| demand_ratio | string | 用户需求占比 |
| competitor_ratio | string | 竞品对比占比 |
| positive_summary | string | 积极评价摘要(HTML ul 格式) |
| negative_summary | string | 负面评价摘要 |
| demand_summary | string | 用户需求摘要 |
| competitor_summary | string | 竞品对比摘要 |
Step 4:翻页处理(用户回复「下一页」等时)
- 沿用 opusId
- 使用上一页的 nextCursor
- 重新调用脚本:
python3 ~/.agents/skills/kuaishou-comment/scripts/kuaishou_comment_search.py "<opusId>" --cursor "<nextCursor>" --page <page> - 完整执行 A1~A4,A3 基于累计综合分析
- A4 后询问是否需要 HTML
翻页规则: has_next true → 用 nextCursor 翻页;false → 已无更多。cursor 原样传递,不可修改。
Step 5:错误处理
| 错误类型 | 处理方式 |
|---|---|
| 无 API Key | 提示配置 REDFOX_API_KEY |
| 作品链接无效 | 提示「未找到该作品的评论,请检查作品链接是否正确」 |
| 接口返回错误 | 显示错误码和错误信息 |
| 获取 0 条评论 | 提示「该作品暂无评论」 |
| 网络请求超时 | 提示「网络请求超时,请稍后重试」 |
📋 依赖
脚本使用 urllib(Python 标准库),无需额外安装依赖。
🎯 使用示例
示例 1:查看指定作品的评论
用户:查看快手作品 3x4ibfzs5e68yxu 的评论
助手:调用脚本 → 展示当前页 + AI总结 → 询问HTML → backfill → open
示例 2:通过链接查询
用户:https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu 这个作品的评论怎么样
助手:提取 opusId → 调用脚本 → 展示分析 → 询问HTML
示例 3:翻页后合并导出
用户:下一页 → 下一页 → 生成html
助手:收集3页JSON → consolidate_report.py → 1个HTML含全部评论+折叠 → open
Files (redfox-community)
-
assets
-
consolidate_report_template.html 12 KB · in bundle
-
report_template.html 12 KB · in bundle
-
-
scripts
-
backfill_html.py 2.8 KB
#!/usr/bin/env python3 """ HTML 报告分析数据回填脚本 将 AI 分析结果回填到脚本生成的 HTML 报告中的 {{PLACEHOLDER}} 占位符。 用法: python3 backfill_html.py <html_path> --analysis-json '<json>' 或通过 stdin 传入 JSON: echo '<json>' | python3 backfill_html.py <html_path> """ 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>",...}', ) args = parser.parse_args() # 优先从 --analysis-json 参数读取,其次从 stdin 读取 if 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 try: raw = sys.stdin.read() 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() -
consolidate_report.py 9.1 KB
#!/usr/bin/env python3 """ 快手评论分析 - 合并报告生成脚本 将多页评论数据合并生成一份完整的 HTML 报告, 包含所有评论和累计分析。超过 3 页时自动折叠。 用法: echo '<json_array>' | python3 consolidate_report.py "<opusId>" --analysis-json '<json>' 或: python3 consolidate_report.py "<opusId>" --pages-json '<json_array>' --analysis-json '<json>' """ import sys import os import json import argparse import subprocess from datetime import datetime SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) TEMPLATE_PATH = os.path.join(SCRIPT_DIR, "..", "assets", "consolidate_report_template.html") def escape_html(text: str) -> str: return (text or "").replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) def build_comment_rows(comments: list) -> str: """根据评论列表生成 HTML 表格行""" rows = [] for c in comments: is_top = c.get("is_top", False) row_class = ' class="top-comment"' if is_top else "" pin_badge = '<span class="top-badge">📌 置顶</span>' if is_top else "" avatar = c.get("user_avatar", "") or "" name = escape_html(c.get("user_name", "")) content = escape_html(c.get("content", "")) like = c.get("like_count", 0) or 0 reply = c.get("reply_count", 0) or 0 time_str = (c.get("create_time", "") or "")[5:16] ip = escape_html(c.get("ip_location", "")) row = ( f'<tr{row_class}>' f'<td>' f'<div class="user-cell">' f'<img src="{avatar}" class="user-avatar" alt="" onerror="this.style.display=\'none\'" referrerpolicy="no-referrer">' f'<span class="user-name">{name}{pin_badge}</span>' f'</div>' f'</td>' f'<td class="content-cell">{content}</td>' f'<td class="num-cell">{like}</td>' f'<td class="num-cell">{reply}</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 build_page_sections(pages: list, max_visible: int = 3) -> tuple: """ 生成页面 HTML 片段和折叠切换按钮。 pages: [{"page": 1, "total": 31, "comments": [...]}, ...] 返回 (page_sections_html, collapse_toggle_html) """ visible_count = 0 visible_sections = [] collapsed_sections = [] # 过滤空页 pages = [p for p in pages if p.get("comments") and len(p["comments"]) > 0] total_pages = len(pages) for i, p in enumerate(pages): page_num = p.get("page", i + 1) total = p.get("total", len(p.get("comments", []))) comments = p.get("comments", []) table_rows = build_comment_rows(comments) section_html = ( f'<div class="page-section">' f'<h3>📄 第 {page_num} 页 ' f'<span class="page-badge">{total} 条</span>' f'</h3>' f'<div class="table-wrapper">' f'<table class="comment-table">' f'<thead>' f'<tr>' f'<th style="width: 160px;">评论人</th>' f'<th>评论内容</th>' f'<th style="width: 70px; text-align: right;">👍 点赞</th>' f'<th style="width: 70px; text-align: right;">💬 回复</th>' f'<th style="width: 110px;">时间</th>' f'<th style="width: 80px;">IP属地</th>' f'</tr>' f'</thead>' f'<tbody>' f'{table_rows}' f'</tbody>' f'</table>' f'</div>' f'</div>' ) if total_pages <= max_visible or page_num <= max_visible: visible_sections.append(section_html) else: collapsed_sections.append(section_html) sections_html = "\n".join(visible_sections) # 折叠区域 if collapsed_sections: hidden_count = sum( p.get("total", len(p.get("comments", []))) for p in pages[max_visible:] ) toggle_html = ( f'<div class="collapse-container" id="collapseToggle">' f'<p style="color: var(--text-secondary); margin-bottom: 0.6rem; font-size: 0.85rem;">' f'⚠️ 评论页数较多,已折叠第 {max_visible + 1}-{total_pages} 页(共 {hidden_count} 条)</p>' f'<button class="collapse-toggle" onclick="toggleCollapsed()">' f'📂 展开全部评论' f'</button>' f'</div>' f'<div class="collapsed-pages" id="collapsedPages">' + "\n".join(collapsed_sections) + f'</div>' ) else: toggle_html = "" return sections_html, toggle_html def generate_consolidated_report( opus_id: str, pages: list, analysis: dict, output_dir: str = None, ) -> str: template_path = os.path.normpath(TEMPLATE_PATH) if not os.path.exists(template_path): print(f"[error] 模板文件不存在: {template_path}", file=sys.stderr) sys.exit(1) with open(template_path, "r", encoding="utf-8") as f: html = f.read() # 过滤空页并合并所有评论 pages = [p for p in pages if p.get("comments") and len(p["comments"]) > 0] all_comments = [] for p in pages: all_comments.extend(p.get("comments", [])) total_pages = len(pages) total_comments = len(all_comments) total_likes = sum(c.get("like_count", 0) or 0 for c in all_comments) now = datetime.now() # 生成页面区域 page_sections, collapse_toggle = build_page_sections(pages) replacements = { "{{OPUS_ID}}": escape_html(opus_id), "{{DATE}}": now.strftime("%Y-%m-%d"), "{{TOTAL_COMMENTS}}": str(total_comments), "{{TOTAL_LIKES}}": str(total_likes), "{{TOTAL_PAGES}}": str(total_pages), "{{TIMESTAMP}}": now.strftime("%Y-%m-%d %H:%M:%S"), "{{PAGE_SECTIONS}}": page_sections, "{{COLLAPSE_TOGGLE}}": collapse_toggle, "{{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", "--")), "{{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 replacements.items(): html = html.replace(key, val) # 添加 JavaScript 折叠逻辑 collapse_js = """ <script> function toggleCollapsed() { var collapsed = document.getElementById('collapsedPages'); var btn = document.getElementById('collapseToggle').querySelector('.collapse-toggle'); if (collapsed.classList.contains('visible')) { collapsed.classList.remove('visible'); btn.textContent = '📂 展开全部评论'; } else { collapsed.classList.add('visible'); btn.textContent = '📁 收起'; } } </script> """ html = html.replace("</body>", collapse_js + "\n</body>") 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"快手评论分析_{opus_id}_全部{total_pages}页.html" file_path = os.path.join(out_dir, filename) with open(file_path, "w", encoding="utf-8") as f: f.write(html) print(f"[consolidate] 合并报告已生成: {file_path}", file=sys.stderr) return file_path def main(): parser = argparse.ArgumentParser(description="快手评论合并报告生成") parser.add_argument("opus_id", help="快手作品 opusId") parser.add_argument( "--pages-json", dest="pages_json", type=str, default=None, help='多页数据 JSON 字符串,格式: [{"page":1,"total":20,"comments":[...]},...]', ) parser.add_argument( "--analysis-json", dest="analysis_json", type=str, default=None, help='累计分析数据 JSON', ) parser.add_argument( "--output-dir", dest="output_dir", type=str, default=None, help="HTML 输出目录", ) args = parser.parse_args() # 读取 pages 数据 if args.pages_json: pages = json.loads(args.pages_json) else: raw = sys.stdin.read() pages = json.loads(raw) if raw.strip() else [] if not pages: print("[error] 未提供评论数据", file=sys.stderr) sys.exit(1) # 读取分析数据 if args.analysis_json: analysis = json.loads(args.analysis_json) else: analysis = {} html_path = generate_consolidated_report( opus_id=args.opus_id, pages=pages, analysis=analysis, output_dir=args.output_dir, ) print(json.dumps({"html_path": html_path}, ensure_ascii=False)) # 自动在浏览器中打开 subprocess.run(["open", html_path]) if __name__ == "__main__": main() -
kuaishou_comment_search.py 8.8 KB
#!/usr/bin/env python3 """ 快手评论分析脚本 调用 Redfox API 获取快手作品一级评论数据,同步生成 HTML 报告 用法: python3 kuaishou_comment_search.py "<opusId>" [--cursor ""] [--page 1] [--no-html] [--output-dir ~/Downloads/QoderReports] """ import sys import os import json import argparse import urllib.request import urllib.error from datetime import datetime API_URL = "https://redfox.hk/story/api/ks/ability/commentList" # 脚本所在目录,用于定位模板 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 call_api(opus_id: str, cursor: str, api_key: str) -> dict: """单次调用评论接口,返回原始 JSON 数据""" payload = json.dumps({ "opusId": opus_id, "cursor": cursor or "", "source": "快手评论分析-GitHub", }).encode("utf-8") req = urllib.request.Request( API_URL, data=payload, 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) code = result.get("code") if code != 2000: print(f"[error] 接口返回错误: code={code}, msg={result.get('msg', '未知')}", file=sys.stderr) sys.exit(1) return result def format_comment(raw: dict) -> dict: """将 API 返回的原始评论格式化为统一结构 API 返回字段(data.comments[]): cid, content, likeNum, replyNum, createTime, area, nickname, avatar, uid, bottomTags """ user_uid = raw.get("uid", "") or "" nickname = (raw.get("nickname", "") or "").strip() avatar = raw.get("avatar", "") or "" # 置顶判断:bottomTags 中包含 "top_comment" bottom_tags_str = raw.get("bottomTags", "") or "" is_top = "top_comment" in bottom_tags_str return { "comment_id": raw.get("cid", ""), "content": (raw.get("content", "") or "").strip(), "like_count": raw.get("likeNum", 0) or 0, "reply_count": raw.get("replyNum", 0) or 0, "create_time": raw.get("createTime", "") or "", "ip_location": raw.get("area", "") or "", "user_name": nickname, "user_avatar": avatar, "user_id": user_uid, "is_top": is_top, } def fetch_comments(opus_id: str, cursor: str, api_key: str) -> tuple: """ 单次调用接口获取当前页评论数据(每次仅调用一次 API)。 返回 (comments_list, has_next, next_cursor) """ result = call_api(opus_id, cursor, api_key) data = result.get("data") or {} page_comments = data.get("comments") or [] if not isinstance(page_comments, list): page_comments = [] all_comments = [format_comment(c) for c in page_comments] # 判断是否有下一页:cursor 有值且不为终止标识 "no_more" raw_cursor = data.get("cursor", "") or "" has_next = bool(raw_cursor) and raw_cursor != "no_more" next_cursor = raw_cursor return all_comments, has_next, next_cursor 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: is_top = c.get("is_top", False) row_class = ' class="top-comment"' if is_top else "" pin_badge = '<span class="top-badge">📌 置顶</span>' if is_top else "" avatar = c.get("user_avatar", "") or "" name = escape_html(c.get("user_name", "")) content = escape_html(c.get("content", "")) like = c.get("like_count", 0) or 0 reply = c.get("reply_count", 0) or 0 time_str = (c.get("create_time", "") or "")[5:16] # YYYY-MM-DD HH:MM:SS → MM-DD HH:MM ip = escape_html(c.get("ip_location", "")) row = ( f'<tr{row_class}>' f'<td>' f'<div class="user-cell">' f'<img src="{avatar}" class="user-avatar" alt="" onerror="this.style.display=\'none\'" referrerpolicy="no-referrer">' f'<span class="user-name">{name}{pin_badge}</span>' f'</div>' f'</td>' f'<td class="content-cell">{content}</td>' f'<td class="num-cell">{like}</td>' f'<td class="num-cell">{reply}</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(opus_id: str, current_page: int, comments: list, 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) replacements = { "{{OPUS_ID}}": escape_html(opus_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), } 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"快手评论分析_{opus_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="快手评论分析") parser.add_argument("opus_id", help="快手作品 opusId") parser.add_argument( "--cursor", dest="cursor", type=str, default="", help="游标(首页传空字符串,次页传上一页返回的 nextCursor)", ) parser.add_argument( "--page", dest="page", type=int, default=1, help="当前页码(默认 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() opus_id = args.opus_id.strip() if not opus_id: print("[error] opusId 不能为空", file=sys.stderr) sys.exit(1) api_key = get_api_key() comments, has_next, next_cursor = fetch_comments(opus_id, args.cursor, api_key) total = len(comments) current_page = args.page output = { "opus_id": opus_id, "current_page": current_page, "cursor": args.cursor, "next_cursor": next_cursor, "total_fetched": total, "has_next": has_next, "comments": comments, "total_count": total, } if not args.no_html: html_path = generate_html_report( opus_id=opus_id, current_page=current_page, comments=comments, 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 4.1 KB
# Kuaishou Comment Analysis / kuaishou-comment --- ## Overview Paste a Kuaishou video link to fetch comment data, with paginated browsing, four-dimensional sentiment analysis (positive/negative/demand/competitor), and polished visual report generation. **Core Value** - **Paste & View Instantly**: Drop a video link to pull comment data — likes, replies, IP locations displayed in a clean table. - **Smart Sentiment Analysis**: Automatically categorizes comments into positive, negative, demand, and competitor types with key phrases and representative quotes for a quick read on public opinion. - **Paginated Browsing, Deeper with Every Page**: Flip through comments page by page, with cumulative analysis across all viewed pages for a complete picture. - **One-Click Report Export**: Generate a dark-themed interactive HTML report; multi-page comments auto-merge and collapse beyond 3 pages for easy sharing and offline access. **Target Users** - 🔍 **Kuaishou Creators** — Quickly gauge audience feedback on your videos and discover content opportunities. - 📊 **Operations / Data Analysts** — Track user sentiment on Kuaishou content and measure influencer impact and audience attitudes. - 🏢 **Brands / MCNs** — Monitor competitor comment sections and industry keyword performance to inform content strategy. --- ## Features ### Core Capabilities - **Comment Fetching**: Paste a video link to retrieve comments with cursor-based pagination. - **Pinned Comment Highlighting**: Top comments are visually distinguished for quick identification. - **Four-Dimensional Analysis**: Each query outputs ratios and summaries across positive, negative, demand, and competitor categories, with cumulative analysis on page turns. - **Visual Reports**: Single-page HTML reports for current data, auto-merged multi-page reports with smart collapse beyond 3 pages. --- ## API Key Acquisition & Security - This skill requires the environment variable `REDFOX_API_KEY`. - `REDFOX_API_KEY` is provided by [RedFoxHub](https://redfox.hk/settings/api-keys?source=github) (`https://redfox.hk`). - Please visit [RedFoxHub](https://redfox.hk?source=github) to register and obtain your `REDFOX_API_KEY`. - Configure the `REDFOX_API_KEY` as a device environment variable before using this skill. - Before providing the key, verify its source, scope, validity period, and whether it supports reset/revocation. - Never hardcode or expose the key in code, prompts, logs, or output files. --- ## Usage Guide Send a Kuaishou video link or opusId in natural language — no commands to memorize. ### Quick Reference | Intent | Example | Result | | ------ | ------- | ------ | | View comments | "https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu — what are people saying?" | Comment table + four-dimensional sentiment analysis | | Use video ID directly | "Check comments on video 3x4ibfzs5e68yxu" | Same as above, skip link extraction | | Next page | "Next page" | Fetch the next page of comments with cumulative analysis | | Export report | "Generate HTML" | Merges all browsed pages into a single complete report | ### Output Example After querying you'll receive: 📊 **Comment Table**: All comments on the current page, including username, content, likes, replies, timestamp, and IP location. 📈 **AI Sentiment Summary**: Four-dimensional analysis with ratios, summaries, and representative comment quotes. 📄 **Pagination Hint**: If more pages are available, prompts you to reply "Next page" to continue. --- ## Use Cases | Scenario | Role | Example Query | Benefit | | -------- | ---- | ------------- | ------- | | Viewer feedback | Creator | "What are people saying under my latest video?" | Quickly gauge audience reactions and engage | | Sentiment monitoring | Brand operator | "Show me the comment sentiment on this product" | Track public opinion for data-driven decisions | | Content ideation | Content strategist | "What are viewers discussing in trending videos?" | Mine comment sections for audience needs and topic inspiration | | Data analysis | Data analyst | "Analyze the emotional distribution of comments on this video" | Quantify comment data into a structured report | -
README.md 3.8 KB
# 快手评论分析 / kuaishou-comment --- ## 简介 输入快手作品链接即可获取评论数据,支持分页浏览、四维情感分析(积极/负面/需求/竞品)和精美可视化报告生成。 **核心价值** - **即输即看**:粘贴作品链接就能拉取评论区数据,每页展示完整评论表格,点赞、回复、IP 属地一览无余。 - **智能舆情分析**:自动将评论归为积极、负面、需求、竞品四类,提取关键词和代表评论,快速了解舆论风向。 - **分页浏览,越看越全**:支持逐页翻页,每次翻页自动累计分析所有已读评论,不遗漏任何信息。 - **一键导出报告**:生成深色主题交互式 HTML 报告,多页自动合并、超 3 页智能折叠,支持离线保存和分享。 **适用对象** - 🔍 **快手创作者** — 快速了解作品评论区反馈,发现粉丝需求与选题灵感。 - 📊 **运营 / 数据分析** — 追踪快手作品的用户舆情,掌握内容影响力与受众态度。 - 🏢 **品牌 / MCN** — 监控竞品与行业关键词的评论区表现,辅助内容策略决策。 --- ## 功能特性 ### 核心功能 - **评论获取**:粘贴作品链接即获取评论,支持 cursor 游标逐页翻页,每页展示完整数据。 - **置顶标记**:置顶评论特殊标识,一眼识别重要评论。 - **四维情感分析**:每次查询自动输出积极/负面/需求/竞品四类占比与摘要,翻页时累计分析更全面。 - **可视化报告**:单页生成包含当前页数据的 HTML 报告,多页自动合并为一份、超 3 页智能折叠。 --- ## 密钥获取与安全说明 - 本技能需要使用环境变量:`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` 后使用本技能。 - 在提供密钥前,请先确认密钥来源、可用范围、有效期及是否支持重置/撤销。 - 禁止在代码、提示词、日志或输出文件中硬编码/明文暴露密钥。 --- ## 使用指南 直接用自然语言发送快手作品链接或 opusId 即可,无需记忆命令。 ### 常用说法速查 | 意图 | 示例话术 | 效果 | | ---- | -------- | ---- | | 查看评论 | 「https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu 这个作品的评论怎么样」 | 获取评论表格 + 四维情感分析 | | 直接指定 ID | 「查看快手作品 3x4ibfzs5e68yxu 的评论」 | 同上,跳过链接提取 | | 翻页 | 「下一页」 | 继续拉取下一页评论,累计分析 | | 导出报告 | 「生成 html」 | 已浏览的所有页面合并为一份完整报告 | ### 输出示例 查询后你将收到: 📊 **评论表格**:当前页全部评论,含评论人、评论内容、点赞数、回复数、时间和 IP 属地。 📈 **AI 评论总结**:四维分析(积极/负面/需求/竞品),含占比、摘要和代表性评论引用。 📄 **翻页提示**:若还有下一页,提示「回复『下一页』继续查看」。 --- ## 使用场景 | 场景 | 角色 | 示例问法 | 收益 | | ---- | ---- | -------- | ---- | | 查看作品反馈 | 快手创作者 | 「我刚发的视频评论区什么情况?」 | 快速了解观众反应,及时互动 | | 舆情监控 | 品牌运营 | 「看看这个产品的快手评论区风向」 | 掌握竞品或自身舆情,辅助决策 | | 内容选题 | 内容策划 | 「最近同赛道爆款视频都聊什么?」 | 从评论区发掘用户需求与选题灵感 | | 数据分析 | 数据分析师 | 「帮我分析这个作品评论的情感分布」 | 量化评论数据,输出结构化报告 | -
SKILL.md 8.5 KB
--- name: kuaishou-comment description: 快手评论分析工具。输入作品链接即可获取一级评论数据,支持分页浏览、评论情感分析(积极/负面/需求/竞品),生成精美 HTML 报告。当用户需要查看快手作品评论、分析评论舆情、了解用户反馈时使用。触发词:快手评论、作品评论、评论查询、评论分析、评论舆情、看评论。 --- # 快手评论分析 ## 📝 简介 输入快手作品链接即可获取一级评论数据,每页 20 条,支持 cursor 游标分页翻页。在对话中展示当前页全部评论及 AI 总结分析,同时生成包含当前页数据和总结的交互式 HTML 报告。 ## ✨ 功能特性 | 功能模块 | 能力描述 | 核心价值 | |---------|---------|---------| | 评论获取 | 粘贴作品链接即可获取评论 | 实时拉取作品评论区数据 | | 分页浏览 | 每页 20 条,cursor 游标翻页 | 逐页浏览大量评论 | | AI 总结 | 四维情感分析(积极/负面/需求/竞品) | 快速了解评论舆情全貌 | | 置顶标记 | 置顶评论特殊标识 | 一眼识别重要评论 | | HTML 报告 | 深色主题交互式报告 | 离线保存/分享分析结果 | | 合并报告 | 多页合并,超3页自动折叠 | 一次导出全部评论分析 | ## 🔑 鉴权 前往 [红狐hub](https://redfox.hk/settings/api-keys?source=github) 获取 API Key,通过以下方式配置: ```bash # 方式一:配置文件(如 OpenClaw 的 ~/.openclaw/openclaw.json) { "env": { "REDFOX_API_KEY": "ak_xxxx..." } } # 方式二:终端环境变量 export REDFOX_API_KEY="ak_xxxx..." ``` ## 🔄 工作流程 ### Step 1:理解用户意图,提取 opusId **⚠️ 核心规则:用户提供作品链接,Agent 从链接中提取 opusId。** - 用户提供作品链接(如 `https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu`),Agent 从链接中提取 opusId - 用户也可能直接提供 opusId(如 `3x4ibfzs5e68yxu`),此时直接使用 - 若用户未提供作品链接,主动询问:「请提供快手作品链接」 - 若用户在上一轮对话中查询过某作品的数据,且本轮输入模糊(如"下一页"、"评论分析"),沿用上一轮的 opusId ### Step 2:调用评论获取脚本(自动生成 HTML) **⚠️ 每次调用仅请求一页数据(一次 API 请求),不可擅自发起多次调用拉取多页。** **脚本每次调用自动生成 HTML 报告**(含占位符,未回填分析数据)。终端展示与 HTML 基于**同一次 API 调用**,确保数据一致。 ```bash python3 ~/.agents/skills/kuaishou-comment/scripts/kuaishou_comment_search.py "<opusId>" [--cursor <cursor>] [--page <page>] ``` **参数说明:** | 参数 | 说明 | 默认值 | |------|------|--------| | `opusId` | 快手作品 opusId(必填) | — | | `--cursor` | 游标,首页传空字符串,次页传上一页返回的 nextCursor | "" | | `--page` | 当前页码(仅用于 HTML 文件名和输出标记) | 1 | | `--output-dir` | HTML 输出目录 | ~/Downloads/QoderReports | 脚本返回 JSON 字段详见下方评论字段表。 > **注意:** HTML 报告此时仅含评论原始数据,分析占位符待用户确认后回填。 ### Step 3:对话中展示评论数据 + AI 总结 **⚠️ 每次操作仅调用一次 API,获取一页数据。** #### A1. 告知查询范围 > 📊 作品「**{opusId}**」共获取 **N 条**评论(第 {page} 页),以下是详细数据: #### A2. 渲染评论表格(展示当前页全部评论) **格式规则:** - 评论人列只显示昵称,不展示头像、不生成超链接 - 评论内容超过 40 字截断加 ... - 时间格式化为 MM-DD HH:MM - 置顶评论在昵称后加 📌 标记(整行加粗) - like_count / reply_count 数字 ≥ 10000 使用 x.xw 格式 - 每页全部展示,不得截断 #### A3. AI 评论总结(⚠️ 每次查询(含翻页)必须输出) **首页**:基于当前页评论,四维情感分析。 **翻页后**:基于目前已获取的所有页面累计数据综合分析,标注累计总条数。 **分析前需理解快手社区文化**(老铁文化、表情符号语义、"奥利给""双击"等特有表达)。 > ## 📈 评论总结分析(基于 {total} 条评论) > > ### ✅ 积极评价({positive_ratio}%) > - {要点,引用代表评论关键词} > > ### ⚠️ 负面评价({negative_ratio}%) > - {要点} > > ### 💡 用户需求({demand_ratio}%) > - {要点} > > ### 🔍 竞品对比舆情({competitor_ratio}%) > - {仅当提及竞品时输出,否则输出「未提及竞品」} **分析要求:** - 每条要点引用代表评论关键词/短语 - 百分比为该类型评论占比,四类总和可能超 100% - 必须理解快手社区文化 #### A4. 翻页提示(⚠️ 紧接在 A3 之后) - 若 has_next 为 true:`📄 当前第 {page} 页(共 {total_fetched} 条)。回复「下一页」继续查看。` - 若 has_next 为 false:`📄 当前第 {page} 页,已无更多数据。` **⚠️ A1~A4 必须在同一轮输出中连续完成,不可省略任何一步。** A4 输出完毕后,**询问用户是否需要生成 HTML 可视化报告**: > 📊 是否需要生成 HTML 可视化报告? **若用户确认**,根据已浏览页数选择方式: --- #### 仅 1 页 → 单页 HTML(backfill_html.py) HTML 文件已在 Step 2 生成,直接回填分析数据并打开: ```bash python3 ~/.agents/skills/kuaishou-comment/scripts/backfill_html.py "<html_path>" --analysis-json '<分析JSON>' open "<html_path>" ``` --- #### 2 页及以上 → 合并报告(consolidate_report.py) 收集所有已浏览页面的 JSON 数据,生成一份包含**全部评论和累计分析**的完整 HTML。按页码分区展示,**超过 3 页时自动折叠**,提示用户点击展开。 ```bash python3 ~/.agents/skills/kuaishou-comment/scripts/consolidate_report.py "<opusId>" \ --pages-json '[{"page":1,"total":31,"comments":[...]},{"page":2,"total":20,"comments":[...]}]' \ --analysis-json '<累计分析JSON>' open "<html_path>" ``` | 参数 | 说明 | |------|------| | opusId | 快手作品 opusId(位置参数) | | --pages-json | 多页数据 JSON 数组,每项含 page、total、comments | | --analysis-json | 累计分析 JSON(字段同 backfill) | > pages 数据从各次脚本调用输出中收集;累计分析基于 A3 翻页后综合分析。 --- **分析 JSON 字段说明:** | 字段 | 类型 | 说明 | |------|------|------| | positive_ratio | string | 积极评价占比(纯数字,如 "62",模板自动追加 %) | | negative_ratio | string | 负面评价占比 | | demand_ratio | string | 用户需求占比 | | competitor_ratio | string | 竞品对比占比 | | positive_summary | string | 积极评价摘要(HTML ul 格式) | | negative_summary | string | 负面评价摘要 | | demand_summary | string | 用户需求摘要 | | competitor_summary | string | 竞品对比摘要 | ### Step 4:翻页处理(用户回复「下一页」等时) 1. 沿用 opusId 2. 使用上一页的 nextCursor 3. 重新调用脚本:`python3 ~/.agents/skills/kuaishou-comment/scripts/kuaishou_comment_search.py "<opusId>" --cursor "<nextCursor>" --page <page>` 4. 完整执行 A1~A4,A3 基于累计综合分析 5. A4 后询问是否需要 HTML **翻页规则:** has_next true → 用 nextCursor 翻页;false → 已无更多。cursor 原样传递,不可修改。 ### Step 5:错误处理 | 错误类型 | 处理方式 | |---------|---------| | 无 API Key | 提示配置 REDFOX_API_KEY | | 作品链接无效 | 提示「未找到该作品的评论,请检查作品链接是否正确」 | | 接口返回错误 | 显示错误码和错误信息 | | 获取 0 条评论 | 提示「该作品暂无评论」 | | 网络请求超时 | 提示「网络请求超时,请稍后重试」 | --- ## 📋 依赖 脚本使用 urllib(Python 标准库),无需额外安装依赖。 --- ## 🎯 使用示例 **示例 1:查看指定作品的评论** ``` 用户:查看快手作品 3x4ibfzs5e68yxu 的评论 助手:调用脚本 → 展示当前页 + AI总结 → 询问HTML → backfill → open ``` **示例 2:通过链接查询** ``` 用户:https://www.kuaishou.com/short-video/3x4ibfzs5e68yxu 这个作品的评论怎么样 助手:提取 opusId → 调用脚本 → 展示分析 → 询问HTML ``` **示例 3:翻页后合并导出** ``` 用户:下一页 → 下一页 → 生成html 助手:收集3页JSON → consolidate_report.py → 1个HTML含全部评论+折叠 → open ```
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.