amz_review_analyse/VOC分析方法论与报告生成逻辑.md
OnesvmWhoops cb6692c0c0 新增 voc_业务_2 报告流水线,并完善聚类与词频模块。
包含 Persona 锚定聚类、LLM 报告生成、方法论文档及 .gitignore 更新,便于在 Gitee 独立部署。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 17:12:39 +08:00

33 KiB
Raw Permalink Blame History

VOC 数据清洗、分析与报告生成通用方法论

文档版本:v1.0 · 2026-06-11
适用范围:亚马逊任意品类竞品 VOC 分析
数据来源:卖家精灵 / Shulex 导出的实时评论 CSV
报告输出:双层结构 HTML 报告(描述层 What + 分析层 Why)


目录

  1. 原始数据结构
  2. 数据清洗规则
  3. 描述层 What — 分析逻辑
    • 3.1 有效评论统计
    • 3.2 用户画像(Persona)识别
    • 3.3 正负反馈主题提取
    • 3.4 情感关键词分析
  4. 分析层 Why — 分析逻辑
    • 4.1 KANO 模型需求分类
    • 4.2 JTBD 动机框架
    • 4.3 人群 × 场景 × 需求矩阵
    • 4.4 痛点根因分析
  5. 报告生成逻辑
    • 5.1 HTML 整体结构
    • 5.2 可视化组件
    • 5.3 使用场景字段写入规则(核心规则)
  6. 执行 SOP(逐步操作流程)
  7. 关键阈值与判断规则速查表
  8. 新品类接入清单

1. 原始数据结构

文件命名规则

{ASIN}_realtime.csv

每个竞品 ASIN 对应一个独立文件,分析时批量读取同一目录下的全部文件。

CSV 字段说明

字段名 类型 说明
asin string 亚马逊标准识别号,文件可能带 BOM 头(\ufeffasin),读取时须用 utf-8-sig 编码
rating float(字符串形式) 评分,取值 1.0 / 2.0 / 3.0 / 4.0 / 5.0,须用 float() 转换,不能用 int()
title string 评论标题
content string 评论正文(主要分析字段)
verified string "True" / "False",是否已验证购买
vine string "True" / "False",是否为 Vine 评测
review_date string ISO 8601 格式,如 2026-05-28T00:00:00+00:00

2. 数据清洗规则

2.1 有效评论筛选(不可更改的核心规则)

只保留以下两类评论,其余全部排除:

def is_valid(row):
    return (
        row.get('verified', '').strip().lower() == 'true'
        or
        row.get('vine', '').strip().lower() == 'true'
    )

排除理由:未验证且非 Vine 的评论可能包含刷评、竞品恶意差评或未实际购买的猜测,会干扰真实用户体验数据。

2.2 差评 / 好评 / 中性评论定义

分类 星级 用途
好评(Positive) ★★★★★ / ★★★★ 提取正向主题、魅力型需求、用户满意点
中性(Neutral) ★★★ 单独记录,不进入主题频次统计
差评(Negative) ★★ / ★ 主要分析对象,提取痛点主题和根因

2.3 新买家 vs 复购买家区分规则

类型 content 字段识别关键词
新买家 first time / just got / just bought / new to
复购买家 reorder / bought again / second time / repurchase / keep buying

两类买家的差评重点通常不同:

  • 新买家 → 效果不符预期、开箱即坏、使用门槛高
  • 复购买家 → 某个功能在长期使用后失效、品质下降

3. 描述层 What — 分析逻辑

3.1 有效评论统计

对每个 ASIN 分别计算,再汇总全市场数据:

指标 计算公式
有效评论数 通过 is_valid() 筛选后的总行数
加权平均评分 Σ(各ASIN均分 × 各ASIN有效评论数) / 全市场有效评论总数
差评率 ≤2星评论数 / 有效总数
正评率 ≥4星评论数 / 有效总数
各星级分布 1–5 星各自数量及占比

市场竞争状态判断:

加权均分 判断
< 3.5 市场存在严重系统性缺陷,是新品进入的明确窗口期
3.5 – 4.0 市场有改进空间,部分功能存在普遍短板
> 4.0 市场整体较成熟,需通过差异化或细分切入

3.2 用户画像(Persona)识别

识别方法

在评论 title + content 字段中搜索特征词,将评论人归入对应 Persona。一条评论可同时归入多个 Persona。

Persona 识别词的建立原则

  1. 阅读全部差评(≤2星),找出用户描述自身处境的词汇("I have... / I am... / As a...")
  2. 阅读全部好评(≥4星),找出用户描述自身需求背景的词汇
  3. 从中归纳出 4–6 个差异化的用户群体
  4. 每个群体设定 5–10 个识别关键词

⚠️ Persona 必须覆盖的三个分类维度(缺一不可)

在最终确认 Persona 列表前,必须检查是否已从以下三个维度进行了覆盖,不能只按其中一个维度拆分就停止:

维度 说明 典型信号词
A. 物理/生理特征 用户身体特征决定了产品对他们的效果上限(最易被遗漏) thick/dark/coarse hair / sensitive skin / pregnant / curly / Latina / Type 4 hair
B. 行为/场景 用户在什么情境下使用产品 travel / in the shower / gift / daily
C. 购买动机/背景 用户为何从其他方案切换过来 switched from razor / too expensive / saw on TikTok / first time

关键原则:如果你的 Persona 列表里只有场景类和动机类群体,而没有任何一个群体是按身体特征定义的,说明维度 A 被遗漏了,必须重新检查差评中的自我标注词汇。

自我标注信号强制检查步骤

在完成初步 Persona 归纳后,必须额外执行以下搜索,确认是否有被遗漏的物理特征用户群:

搜索差评中所有含以下模式的句子:
  "I have [adj] [noun]"(如 I have thick hair / I have sensitive skin)
  "My [noun] is/are [adj]"(如 My skin is super sensitive)
  "As a [noun/adj person]"(如 As a Latina / As a curly-haired person)
  "[族裔/肤色/发质形容词]"(如 Latina / dark hair / coarse / Type 4)

若上述词汇出现 ≥ 5 条,则该物理特征代表一个独立 Persona,必须单独列出。

Persona 识别词模板格式

PERSONA_KEYWORDS = {
    '{群体名称A}': ['{关键词1}', '{关键词2}', ...],
    '{群体名称B}': ['{关键词1}', '{关键词2}', ...],
    # 根据实际品类补充
}

Persona 占比估算规则

占比 = 命中该Persona识别词的评论数 / 有效评论总数
四舍五入至整5%

因一条评论可被多个 Persona 命中,各 Persona 占比之和可超过 100%。

Persona 卡片内容规格(每个群体输出以下信息)

字段 来源 说明
群体名称 自定义 简洁描述身份特征,≤6 字
占比 统计计算 见上方公式
核心痛点 该群体差评 ≤3 条,原文语义概括
核心需求 该群体好评+诉求 ≤3 条
购买动机 JTBD 分析 用"雇佣产品做什么"句式
代表性引用 真实评论原文 必须来自实际评论,注明 ASIN

3.3 正负反馈主题提取

主题识别关键词组的建立方法

  1. 阅读全部差评(≤2星),记录用户描述问题时的高频词
  2. 将语义相近的词归为同一主题,形成关键词组
  3. 每个主题设定 5–10 个关键词
  4. 覆盖 80%+ 的差评内容(长尾主题可合并为"其他")

⚠️ 主题拆分规则:相近但机制不同的问题必须独立成主题

语义相近不等于根因相同。以下情况必须拆分为独立主题,不得合并:

合并后失真的典型例子 应该如何拆分 原因
"剃效差"(笼统) ① 留茬/剃不干净 ② 拉扯/扯毛而非切断 机制不同:留茬=刀头贴肤不足;拉扯=刀片咬不断粗硬毛,影响人群完全不同
"皮肤问题"(笼统) ① 割伤/出血 ② 摩擦热/灼烧感 ③ 剃须疹/内生毛 根因不同,对应不同的工程解决方案
"产品损坏"(笼统) ① 充电失效 ② 配件断裂/脱落 分属电气系统和结构系统,受影响时间节点不同(充电=使用初期;断裂=一段时间后)

判断是否需要拆分的问题:
"同一主题下的差评,是否描述的是同一个物理/工程原因?"
如果不是,必须拆开。

通用差评主题模板格式:

NEGATIVE_THEMES = {
    '{主题名称}': ['{关键词1}', '{关键词2}', ...],
    # 品类相关主题
}

通用好评主题模板格式:

POSITIVE_THEMES = {
    '{主题名称}': ['{关键词1}', '{关键词2}', ...],
}

频次统计规则

  1. 在 title + content 中搜索关键词
  2. 同一评论中同一关键词出现多次,仍计为 1 次(避免重复计数)
  3. 差评主题只统计 ≤2 星评论;好评主题只统计 ≥4 星评论
  4. 频次 = 命中该主题的评论条数(非词语出现总次数)

主题优先级判定规则

优先级 差评频次门槛 涉及竞品范围
P0(立即处理) ≥ 总有效差评数的 20% 80%+ 竞品均出现
P1(短期处理) 总有效差评数的 10–20% 60%+ 竞品出现
P2(中期关注) 总有效差评数的 3–10% 40%+ 竞品出现

频次门槛的动态计算:
P0 绝对门槛 = 全市场有效差评数 × 20%
例:1123 条有效评论,差评率 35% ≈ 393 条差评,P0 门槛 ≈ 79 条

3.4 情感关键词分析

对所有有效评论进行词频统计,提取高频情感词。

输出字段规格:

字段 说明 规则
词汇 英文原词或词组 保留原文,不翻译
出现频次 在有效评论中出现的条数 同一评论多次出现计1次
情感极性 正面 / 负面 / 中性 根据语境判断,同一词在不同语境可有不同极性
含义/使用场景 该词汇在评论中的具体语境 只写评论中明确出现的内容,不推断
主要关联人群 对应的 Persona 名称 可多个

4. 分析层 Why — 分析逻辑

4.0 Persona 完整性验证(进入分析层前的强制关卡)

在开始 KANO / JTBD 分析之前,必须完成以下交叉验证,发现遗漏立即返回 3.2 节补充。

验证方法:每个 P0/P1 主题 → 强制归因到 Persona

为每一个 P0/P1 差评主题填写下表:

差评主题 频次 该主题的典型描述 主要影响哪类用户? 对应已有 Persona?
{主题1} {N条} {原文特征} {用户特征描述} {Persona名 / ❌未覆盖}
{主题2} ... ... ... ...

如果某个 P0/P1 主题在"对应已有 Persona"列填写了 ❌,说明存在遗漏的用户群体,必须新增 Persona。

常见漏洞场景

被遗漏的情况 漏洞原因 补救方式
身体特征群体(如粗硬发质用户) 只按场景/动机分群,未检查维度 A(物理特征) 返回 3.2 节执行自我标注信号强制检查
长期使用复购用户 只看差评内容,未注意时间轴("after months of use") 检查含 months / after a while / second bottle 的差评是否形成独立群体
特定人群的特殊需求 该群体占比较小但痛点极具体 即使占比低(~5%),若痛点独特且无法被其他 Persona 代表,必须单独列出

4.1 KANO 模型需求分类

四种类型定义与判断标准

类型 定义 判断标准 常见错误
基本型(Must-be) 不满足→强烈差评;满足→用户不会特别提及或表扬 ① 差评频次达 P0 级别 ② 80%+ 竞品均出现该缺陷 ③ 好评中几乎不出现"因为做到了 X 所以好评" 把"剃净度"归为基本型——剃净度好坏都会被用户提及,属期望型
期望型(Performance) 做得越好评分越高,做得越差评分越低,线性关系 ① 好评中被作为"这款优于竞品"的主要理由 ② 差评中作为"原本期待但未达到"的失望点 ③ 用户用程度词描述(better/worse/not as good as) 把"电池续航"归为基本型——续航差才差评,续航超长会被用户特别称赞
魅力型(Attractive) 满足→产生超预期惊喜和好评;不满足→用户不会差评 ① 好评中出现强情感词 love / obsessed / amazing / didn't expect / bonus ② 该功能在差评中几乎不出现 ③ 竞品普遍缺失,属市场空白 把"附赠收纳袋"归为期望型——用户从未因为"没有收纳袋"而差评,属意外惊喜
反向型(Reverse) 某些用户认为该功能是负担,反而差评 ① 差评中出现对某个"功能"的明确抱怨 ② 该内容在好评中也受另一部分人喜爱(说明用户分歧) 把"产品损坏"归为反向型——没有用户"希望产品能损坏"

各类型的输出格式要求

每个 KANO 条目必须包含以下 5 个字段,缺一不可:

需求项:[具体需求描述,动词+名词形式]
评论频次证据:[支撑该分类的评论条数及代表性原文片段]
主要影响 Persona:[哪类用户群对该需求最敏感]
分类原因:[用一句话解释为什么是这个 KANO 类型,而不是其他类型]
竞品现状:[现有竞品是否满足,满足程度如何]

示例(基本型):

需求项:充电后可正常启动
评论频次证据:118条差评(P0级别),"stopped working after a few uses" / "won't charge at all"
主要影响 Persona:所有群体,尤其是复购用户(第二台也坏后彻底失去信任)
分类原因:充电失效是"有就正常、坏了就1星"的底线需求,好评中没有人因"能充电"而特别表扬
竞品现状:全部5款竞品均有此问题,说明是行业普遍工程缺陷

示例(魅力型):

需求项:LCD 电量显示
评论频次证据:好评中 28条提及,"love that I can see the battery level" / "so convenient",差评中0条因缺少LCD而差评
主要影响 Persona:旅行护理族(出行前确认电量)/ 所有群体
分类原因:用户不会因为"没有电量显示"而差评,但有了之后会主动提及并作为推荐理由
竞品现状:仅1款(FANKRUAI)有此功能,属差异化空白

KANO 归类操作步骤

步骤一:基本型识别

  • 列出所有 P0/P1 差评主题
  • 检查每个主题对应的好评:如果好评中几乎没有人因"做到了这点"而表扬,确认为基本型
  • 每个基本型需求必须注明:频次(条数)+ 出现该问题的竞品数量

步骤二:期望型 vs 魅力型区分

在好评中对每个高频好评主题做以下判断:

判断问题 若"是"→ 若"否"→
差评中有人因该功能不够好而差评? 期望型 魅力型候选
好评用程度词描述(better/works great/very)? 期望型 魅力型候选
好评中出现 love/obsessed/amazing/bonus/didn't expect? 魅力型 继续判断
竞品普遍缺失,属市场新鲜感? 魅力型 继续判断

步骤三:反向型搜索(不得以"未发现"一笔带过)

必须主动在差评中搜索以下关键词,并记录每个词的出现频次:

搜索词组(在全部有效评论 title+content 中搜索):
  过于复杂:too many parts / too complicated / confusing / hard to use
  过于嘈杂:too loud / so loud / noise / noisy
  功能多余:don't need / unnecessary / didn't ask for / useless feature
  操作繁琐:takes too long / too many steps / annoying to clean

结果处理规则:

  • 若任意词组频次 ≥ 5 条 → 该功能为反向型,单独列出并附引用
  • 若所有词组总频次 < 5 条 → 填写:"反向型:经主动搜索 [列出搜索词],出现频次共 [N] 条,低于阈值,本品类暂无明确反向需求"(禁止直接写"无"或"未发现")

4.2 JTBD 动机框架

JTBD(Jobs To Be Done):用户"雇佣"产品来完成什么任务。分析维度:功能性动机、情感性动机、社会性动机。

输出格式(每个 Persona 一行):

字段 说明 填写规则
用户群 Persona 名称 —
核心 Job 用户想完成的任务 动词+宾语形式,如"用电动工具替代传统方式"
功能性动机 实用层面的驱动因素 必须能从评论中找到佐证句子
情感性动机 情绪/心理层面的驱动因素 必须能从评论中找到佐证句子
社会性动机 他人视角/社交驱动(如无评论佐证则留空) 可选
购买触发时机 什么具体事件让用户决定购买 来自评论中的具体描述

4.3 人群 × 场景 × 需求矩阵

矩阵将 Persona、使用场景、KANO 需求分层和当前满意度整合为一张全景视图。

列结构:

列 填写来源
用户群 Persona 名称 + 占比
使用场景(When/Where) 严格遵守场景字段规则(见 5.3 节)
基本型需求 KANO 基本型 + 该群体 P0 差评
期望型需求 KANO 期望型 + 该群体 P1 差评
魅力型需求 KANO 魅力型 + 该群体好评加分点
当前满意度 该群体对应评论的均分和好评率综合判断

满意度评级标准:

当前满意度 对应均分参考 显示样式
高 ≥ 4.0 绿色
中等 3.3 – 3.9 黄色
低 ⚠ < 3.3 红色

4.4 痛点根因分析

适用条件:差评主题达到 P0 或 P1 级别时,必须进行根因分析。

分析框架:

根因 N:[工程/设计/材料/体验设计问题名称]
→ 导致后果:[差评主题名称] × [频次]
→ 失效机制:[从产品结构或工作原理层面解释为什么会出现这个问题]
→ 关键引用:[2-3条真实评论原文(英文)— 所属ASIN品牌]

根因分析的层次要求:

层次 示例(错误 → 正确)
停留在现象层(❌) "产品质量差"
到达机制层(✅) "充电口防水胶圈未达到IP67标准,浴室蒸汽渗入导致腐蚀"

5. 报告生成逻辑

5.1 HTML 整体结构

报告采用纯 HTML 内嵌 CSS + JS,无外部文件依赖,单文件可直接分享。

{产品关键词}-voc-v{版本号}.html
├── <head>
│   ├── Chart.js CDN(可视化依赖)
│   │   └── https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js
│   └── <style> 内嵌 CSS
│
├── <nav class="top-nav">(固定顶部导航,支持锚点跳转)
│   ├── [描述层 What] → 数据总览 / 受众画像 / 正负反馈 / 各ASIN主题分布 / 情感词频
│   └── [分析层 Why]  → KANO模型 / JTBD动机 / 人群矩阵 / 痛点根因
│
└── <div class="page">(主体内容)
    ├── 标题 + 副标题 + 阅读指引 callout
    ├── ── 描述层 What ──
    │   ├── #sec-overview:KPI 总览卡片
    │   ├── #sec-persona:Persona 卡片网格
    │   ├── #sec-feedback:全市场差评/好评主题柱状图(汇总)
    │   ├── #sec-asin-theme:各 ASIN 差评/好评主题频次分布(新增)
    │   └── #sec-keyword:情感词频表格
    ├── ── 分析层 Why ──
    │   ├── #sec-kano:KANO 四象限卡片
    │   ├── #sec-jtbd:JTBD 动机表格
    │   ├── #sec-matrix:人群×场景×需求矩阵
    │   └── #sec-rootcause:痛点根因分析块
    └── <footer>(数据来源声明)

文件存放路径:{产品文件夹}/分析报告 HTML/

文件命名:{产品关键词(连字符)}-voc-v{版本号}.html
示例:dog-calming-chews-voc-v1.html

5.2 可视化组件规格

KPI 卡片(4 个,固定布局)

位置 指标 颜色规则
卡片1 有效评论总数 蓝色(中性)
卡片2 加权平均评分 < 3.5 红色 / 3.5–4.0 黄色 / > 4.0 绿色
卡片3 正评率(≥4星占比) 绿色
卡片4 差评率(≤2星占比) 红色

柱状图(使用 Chart.js,水平条形图)

差评主题柱状图(全市场汇总):

new Chart(document.getElementById('negChart'), {
  type: 'bar',
  data: {
    labels: ['主题1', '主题2', ...],  // 按频次降序排列
    datasets: [{
      data: [频次1, 频次2, ...],
      // 颜色按优先级:P0 = '#ef4444',P1 = '#f97316',P2 = '#eab308'
      backgroundColor: ['#ef4444', '#ef4444', '#f97316', '#f97316', '#eab308', '#eab308'],
    }]
  },
  options: {
    indexAxis: 'y',
    responsive: true,
    maintainAspectRatio: false,
    plugins: { legend: { display: false } },
    scales: { x: { beginAtZero: true } }
  }
});

好评主题柱状图(全市场汇总):配色统一使用绿色系(#22c55e)。


各 ASIN 主题频次分布图(新增,ID: sec-asin-theme)

用途:揭示同一痛点在不同竞品间的严重程度差异,帮助判断哪款竞品在哪个维度最弱/最强。

图表类型:分组柱状图(Grouped Bar Chart),每个主题一组,每组内各 ASIN 一根柱子。

差评版 — 数据结构:

new Chart(document.getElementById('asinNegChart'), {
  type: 'bar',
  data: {
    labels: ['主题1', '主题2', ...],  // x轴:差评主题(P0/P1 主题,按全市场频次降序)
    datasets: [
      // 每个 ASIN 一个 dataset
      { label: 'ASIN1(品牌名)', data: [主题1频次, 主题2频次, ...], backgroundColor: '#色值' },
      { label: 'ASIN2(品牌名)', data: [...], backgroundColor: '#色值' },
      // ...
    ]
  },
  options: {
    responsive: true,
    maintainAspectRatio: false,
    plugins: {
      legend: { display: true, position: 'bottom' }
    },
    scales: {
      x: { beginAtZero: true },
      y: { beginAtZero: true }
    }
  }
});

好评版 — 与差评版结构完全相同,labels 换成好评主题,颜色使用绿色系渐变。

ASIN 配色规则(固定,每次分析保持一致):

ASIN 序号 差评图颜色 好评图颜色
ASIN 1 #ef4444(红) #22c55e(绿)
ASIN 2 #f97316(橙) #86efac(浅绿)
ASIN 3 #eab308(黄) #4ade80(草绿)
ASIN 4 #8b5cf6(紫) #2dd4bf(青绿)
ASIN 5 #3b82f6(蓝) #60a5fa(浅蓝绿)

数据提取逻辑(Python):

def count_theme_per_asin(all_reviews, themes, neg=True):
    """
    按 ASIN 分别统计各主题的差评/好评频次
    返回格式:{asin: {theme: count}}
    """
    from collections import defaultdict
    result = defaultdict(lambda: defaultdict(int))
    for r in all_reviews:
        if neg and r['rating'] > 2: continue
        if not neg and r['rating'] < 4: continue
        text = (r['title'] + ' ' + r['content']).lower()
        for theme, keywords in themes.items():
            if any(kw.lower() in text for kw in keywords):
                result[r['asin']][theme] += 1
    return dict(result)

# 转为 Chart.js datasets 格式
def to_chartjs_datasets(per_asin_data, asin_labels, theme_order):
    """
    asin_labels: {asin: '品牌名'} 映射
    theme_order: 主题列表(按全市场频次排序)
    """
    colors = ['#ef4444', '#f97316', '#eab308', '#8b5cf6', '#3b82f6']
    datasets = []
    for i, (asin, label) in enumerate(asin_labels.items()):
        datasets.append({
            'label': label,
            'data': [per_asin_data.get(asin, {}).get(t, 0) for t in theme_order],
            'backgroundColor': colors[i % len(colors)],
        })
    return datasets

图表尺寸:高度建议 360px(主题数 ≤ 8)或 480px(主题数 > 8)。

图表标题规则:

  • 差评版:各竞品差评主题频次对比(≤2星评论)
  • 好评版:各竞品好评主题频次对比(≥4星评论)

表格通用样式规则

元素 CSS 类 用途
P0 行 .row-r 红色背景底色(#fff5f5)
P1 行 .row-y 黄色背景底色(#fffbeb)
P2 行 .row-b 蓝色背景底色(#eff6ff)
机会行 .row-g 绿色背景底色(#f0fdf4)
优先级徽章 .pill pill-danger / .pill-warn / .pill-info 红/黄/蓝圆角标签

5.3 使用场景字段写入规则(核心规则)

这是本方法论中最容易出错、最需要严格执行的规则:

【强制规则】
所有涉及"使用场景(When/Where)"的字段,
只允许填写在评论 content 中能找到原词佐证的场景描述。
如果找不到评论佐证,一律填写 "—"。
禁止基于产品功能、品类常识或逻辑推断填写任何场景描述。

执行步骤

Step 1:确定该 Persona 的特征词组
Step 2:在该 Persona 对应的评论中搜索场景类词汇
        (地点词:shower / bathroom / office / car / gym...)
        (时间词:morning / night / before / after / daily...)
        (情境词:traveling / pregnant / postpartum / gift...)
Step 3:统计每个场景词在该 Persona 评论中的出现条数
Step 4:按以下规则决定是否填写
出现条数 处理方式
≥ 5 条 可以填写该场景
2 – 4 条 慎重填写,建议留空或标注"少数提及"
< 2 条 必须留空,填写 —

常见错误示例

错误写法(❌ 推断) 正确写法(✅ 仅来自评论)
夏季前突击整理 —(无评论提及"before summer"作为使用时机)
日常护理 —("daily"在评论中属产品使用频率描述,非场景)
坐姿/斜靠操作 —(用户姿势属推断,评论未明确提及)
任何场所 —(无具体场景词,不可用"泛化"代替留空)
浴室/淋浴 ✅(评论中出现 in the shower / bathroom ≥5条)
旅行途中 ✅(评论中出现 travel / on a trip ≥5条)
孕期护理 ✅(评论中出现 pregnant / 37 weeks pregnant ≥5条)

6. 执行 SOP(逐步操作流程)

Step 1:数据读取

import csv
import os

def load_reviews(filepath):
    """读取单个ASIN的评论CSV,返回有效评论列表"""
    reviews = []
    with open(filepath, encoding='utf-8-sig') as f:  # utf-8-sig 处理BOM头
        for row in csv.DictReader(f):
            if (row.get('verified', '').strip().lower() == 'true' or
                    row.get('vine', '').strip().lower() == 'true'):
                reviews.append({
                    'asin':    row.get('asin', '').strip(),
                    'rating':  float(row.get('rating', 0) or 0),
                    'title':   row.get('title', ''),
                    'content': row.get('content', ''),
                    'date':    row.get('review_date', ''),
                })
    return reviews

def load_all_reviews(directory):
    """批量读取目录下所有ASIN的评论"""
    all_reviews = []
    for fname in os.listdir(directory):
        if fname.endswith('_realtime.csv'):
            all_reviews.extend(load_reviews(os.path.join(directory, fname)))
    return all_reviews

Step 2:基础统计

def basic_stats(reviews):
    """计算有效评论的基础统计指标"""
    ratings = [r['rating'] for r in reviews if r['rating'] > 0]
    total = len(ratings)
    if not total:
        return {}
    return {
        'total':    total,
        'avg':      round(sum(ratings) / total, 2),
        'pos_rate': round(sum(1 for r in ratings if r >= 4) / total, 3),
        'neg_rate': round(sum(1 for r in ratings if r <= 2) / total, 3),
        'dist':     {i: ratings.count(float(i)) for i in range(1, 6)},
    }

Step 3:主题频次统计

# 根据品类自定义主题关键词(见第3.3节)
NEGATIVE_THEMES = {
    '{主题名称}': ['{关键词1}', '{关键词2}', ...],
}
POSITIVE_THEMES = {
    '{主题名称}': ['{关键词1}', '{关键词2}', ...],
}

def count_themes(reviews, themes, neg=True):
    """
    统计各主题频次
    neg=True 时只统计差评(≤2星),neg=False 时只统计好评(≥4星)
    """
    result = {}
    for theme, keywords in themes.items():
        count = 0
        for r in reviews:
            if neg and r['rating'] > 2: continue
            if not neg and r['rating'] < 4: continue
            text = (r['title'] + ' ' + r['content']).lower()
            if any(kw.lower() in text for kw in keywords):
                count += 1
        result[theme] = count
    return dict(sorted(result.items(), key=lambda x: x[1], reverse=True))

Step 4:Persona 识别

# 根据品类自定义(见第3.2节)
PERSONA_KEYWORDS = {
    '{群体名称}': ['{关键词1}', '{关键词2}', ...],
}

def identify_personas(reviews, persona_keywords):
    """统计各Persona的命中评论数量"""
    result = {p: 0 for p in persona_keywords}
    for r in reviews:
        text = (r['title'] + ' ' + r['content']).lower()
        for persona, kws in persona_keywords.items():
            if any(kw.lower() in text for kw in kws):
                result[persona] += 1
    total = len(reviews)
    return {p: {'count': c, 'pct': round(c / total * 100)} for p, c in result.items()}

Step 5:场景词验证(填写矩阵前必须执行)

def verify_scene(persona_reviews, scene_keywords, min_count=5):
    """
    验证某个场景词是否在该Persona评论中出现足够多次
    返回 (是否可填写, 实际出现条数)
    """
    count = 0
    for r in persona_reviews:
        text = (r['title'] + ' ' + r['content']).lower()
        if any(kw.lower() in text for kw in scene_keywords):
            count += 1
    return count >= min_count, count

# 示例用法
persona_reviews = [r for r in all_reviews if '粗硬发质' in identify_personas_for_review(r)]
can_fill, cnt = verify_scene(persona_reviews, ['shower', 'bathroom', 'in the shower'])
scene_text = '浴室/淋浴' if can_fill else '—'

Step 6:报告组装流程

  1. 准备数据:运行 Step 1–5,收集所有统计结果
  2. 起草 Canvas(在 IDE 中生成 .canvas.tsx 文件),等待用户确认内容无误
  3. 用户确认后:生成 HTML 文件,保存至 分析报告 HTML/ 目录
  4. Canvas 与 HTML 内容必须保持同步,修改 Canvas 后须同步更新 HTML

7. 关键阈值与判断规则速查表

决策点 规则
有效评论筛选 verified == True 或 vine == True
差评定义 rating ≤ 2.0
好评定义 rating ≥ 4.0
P0 主题门槛 频次 ≥ 有效差评数 × 20%,且 80%+ 竞品出现
P1 主题门槛 频次为有效差评数的 10–20%,60%+ 竞品出现
P2 主题门槛 频次为有效差评数的 3–10%,40%+ 竞品出现
Persona 占比 命中评论数 / 有效总数,四舍五入至整5%
场景词可填写门槛 ≥ 5 条 Persona 评论中出现该场景词,否则填 —
市场整体判断 均分 < 3.5 = 系统性缺陷,新品窗口期
KANO 基本型判断 P0 级差评,且与低评分强相关
KANO 魅力型判断 好评中出现 love/amazing/didn't expect,非差评主题
新买家识别 content 含 first time/just got/just bought/new to
复购买家识别 content 含 reorder/bought again/second time/repurchase
根因分析触发 差评主题达到 P0 或 P1 级别
Persona 引用合规 只能引用 content 中确实存在的原文,禁止改写或虚构

8. 新品类接入清单

每次分析新品类时,依次完成以下配置,其余分析框架直接复用:

□ 1. 确认 CSV 文件路径和 ASIN 列表
□ 2. 阅读**全部差评和全部好评**,按三个维度(A物理特征 / B行为场景 / C购买动机)归纳 Persona,编写 PERSONA_KEYWORDS
□ 3. 执行**自我标注信号强制检查**(搜索 "I have [adj]..." / "As a [noun]..." 等模式),确认无遗漏的物理特征群体
□ 4. 阅读**全部差评(≤2星)**,归纳差评主题,注意根因不同的问题必须拆分为独立主题,编写 NEGATIVE_THEMES
□ 5. 阅读**全部好评(≥4星)**,归纳 4–6 个好评主题,编写 POSITIVE_THEMES
□ 6. 运行 Step 1–3,验证频次统计结果与人工阅读印象一致;同时用 count_theme_per_asin() 生成各 ASIN 的主题频次分布数据,用于 #sec-asin-theme 图表
□ 7. **Persona 完整性验证**:为每个 P0/P1 主题强制归因到 Persona,有 ❌ 则返回步骤 2 补充
□ 8. 对每个 Persona 执行 verify_scene(),确认使用场景字段
□ 9. 完成 KANO 归类(基于统计结果,无需额外数据)
□ 10. 完成 JTBD 框架(基于 Persona 评论,无需额外数据)
□ 11. 撰写根因分析(P0/P1 主题,每条根因附 2-3 条原文引用)
□ 12. 生成 Canvas → 用户确认 → 生成 HTML

本文档为通用框架,无品类特定数据。新品类分析时,只需填写第 8 节清单中的品类相关配置,其余规则和代码模板均可直接复用。