#!/usr/bin/env node /** * V2:完整提炼 VOC 报告中的"复合体方向"分析所需数据 * 基于 voc-analyze.js 的实际输出结构 */ const fs = require('fs'); const path = require('path'); const { analyze } = require('./voc-analyze.js'); const PRODUCTS = [ { key: 'liver', label: '江中肝纯片(OTC 保肝片剂)', file: 'jiangzhong-liver-voc.json' }, { key: 'probiotic', label: '江中儿童乳酸菌片', file: 'jiangzhong-probiotic-voc.json' }, { key: 'monkey', label: '江中猴姑饼干', file: 'jiangzhong-monkey-voc.json' }, ]; const buffer = []; const out = (s) => buffer.push(s); for (const p of PRODUCTS) { const fp = path.join('data', p.file); if (!fs.existsSync(fp)) continue; const voc = JSON.parse(fs.readFileSync(fp, 'utf-8')); const A = analyze(voc); out('\n\n' + '='.repeat(80)); out(`【${p.label}】`); out('='.repeat(80)); // === 规模 === out('\n▌规模'); out(` XHS: ${A.metrics.notes}笔记 / ${A.metrics.sampleComments}评论 / ${A.kols?.length || 0}作者`); out(` 互动: 点赞${A.metrics.totalLikes} 评论${A.metrics.totalComments} 收藏${A.metrics.totalCollected} 分享${A.metrics.totalShared}`); if (A.amazon && !A.amazon.empty) { out(` Amazon: ${A.amazon.keywords?.length || 0}关键词 × ${A.amazon.total || 0}款产品`); } // === 小红书 Top 原声 (VOC 真实语料) === if (A.topComments && A.topComments.length) { out('\n▌小红书 Top 高赞原声'); A.topComments.slice(0, 15).forEach((c, i) => { const content = (c.content || '').replace(/\s+/g, ' ').slice(0, 140); out(` ${String(i + 1).padStart(2, '0')}. [${c.like_count || 0}赞] ${content}`); }); } // === Top Notes(爆款笔记标题 → 用户内容倾向)=== if (A.topNotes && A.topNotes.length) { out('\n▌爆款笔记 Top 10 标题(揭示用户内容兴趣)'); A.topNotes.slice(0, 10).forEach((n, i) => { const title = (n.title || n.display_title || '').replace(/\s+/g, ' ').slice(0, 80); out(` ${String(i + 1).padStart(2, '0')}. [${n.liked_count || 0}赞] ${title}`); }); } // === Amazon 分析层 === if (A.amazon && !A.amazon.empty) { // 主题:英文 Amazon reviews 的 VOC 痛点/亮点/场景 const th = A.amazon.amazonThemes; if (th) { if (th.pain && th.pain.length) { out('\n▌Amazon 英文评论 · 痛点主题 (海外用户差评)'); th.pain.slice(0, 8).forEach((t, i) => { out(` ${i + 1}. ${t.tag} (提及 ${t.count} 次 · ${t.brandCoverage}品牌)`); }); } if (th.bright && th.bright.length) { out('\n▌Amazon 英文评论 · 亮点主题 (海外用户好评)'); th.bright.slice(0, 8).forEach((t, i) => { out(` ${i + 1}. ${t.tag} (提及 ${t.count} 次 · ${t.brandCoverage}品牌)`); }); } if (th.scenarios && th.scenarios.length) { out('\n▌Amazon 英文评论 · 使用场景'); th.scenarios.slice(0, 6).forEach((t, i) => { out(` ${i + 1}. ${t.tag} (${t.count} 次)`); }); } } // 品类机会评估 if (A.amazon.opportunity) { const op = A.amazon.opportunity; out('\n▌品类机会评估'); out(` 等级: ${op.grade} (${op.totalScore}/${op.maxScore}) · 甜点价位: ${op.sweetSpot}`); if (op.dimensions) { op.dimensions.forEach((d) => out(` · ${d.name}: ${d.level} — ${(d.note || '').replace(/<[^>]+>/g, '').slice(0, 100)}`)); } } // 竞品档案(4 家) if (A.amazon.competitorDossiers) { out('\n▌竞品档案 (Top 4)'); A.amazon.competitorDossiers.slice(0, 4).forEach((d, i) => { out(` ${i + 1}. ${d.brand} · ${d.positioning} · ${d.share?.toFixed(1)}%份额 · 均价$${d.avgPrice?.toFixed(0)} · ★${d.avgRating?.toFixed(2)}`); if (d.topProduct) out(` Top产品: ${(d.topProduct.title || '').slice(0, 80)}`); if (d.strengths?.length) out(` 优势: ${d.strengths.slice(0, 3).map(s => `${s.label}×${s.n}`).join(' / ')}`); if (d.gaps?.length) out(` 缝隙: ${d.gaps.slice(0, 3).map(g => `${g.label}×${g.n}`).join(' / ')}`); }); } // 深度产品详情(已有卖点) if (A.amazon.detailProducts) { out('\n▌Amazon Top 6 深度产品 (成分卖点参考)'); A.amazon.detailProducts.slice(0, 6).forEach((prod, i) => { const title = (prod.title || '').slice(0, 100); const price = prod.price ? (prod.price / 100).toFixed(2) : '-'; out(` ${i + 1}. [${prod.brand || '-'}] ${title}`); out(` $${price} · ★${prod.rating || '-'} · ${prod.ratingsCount || prod.reviewCount || 0} 评`); }); } // 关键发现 if (A.amazon.keyFindings) { out('\n▌关键发现'); A.amazon.keyFindings.slice(0, 5).forEach((f, i) => { out(` ${i + 1}. ${f.title}`); out(` ${(f.body || '').replace(/<[^>]+>/g, '').slice(0, 200)}`); }); } // 战略建议 if (A.amazon.strategy) { const s = A.amazon.strategy; out('\n▌战略建议 (短/中/长)'); ['short', 'mid', 'long'].forEach((phase) => { if (s[phase]?.items) { out(` ${phase.toUpperCase()}:`); s[phase].items.forEach((it) => out(` · ${it.replace(/<[^>]+>/g, '').slice(0, 140)}`)); } }); } } // === TikTok hashtag 网络 (Amazon 内容端) === if (A.tiktok && !A.tiktok.empty) { out('\n▌TikTok 海外内容端'); if (A.tiktok.topHashtags) { out(` Top 20 Hashtag: ${A.tiktok.topHashtags.slice(0, 20).map(h => '#' + (h.tag || h[0])).join(' ')}`); } if (A.tiktok.topComments) { out(' Top 8 海外用户原声:'); A.tiktok.topComments.slice(0, 8).forEach((c, i) => { const text = (c.text || '').replace(/\s+/g, ' ').slice(0, 140); out(` ${i + 1}. [${c.digg_count || 0}赞] ${text}`); }); } } // === TikTok Ads 关键词洞察 === if (A.marketIntel && !A.marketIntel.empty && A.marketIntel.ttAds?.hasData) { const mi = A.marketIntel.ttAds; out('\n▌TikTok Ads 关键词洞察 (广告主视角)'); const keys = Object.keys(mi).filter(k => !['hasData'].includes(k)); out(` (可用字段: ${keys.join(', ')})`); if (mi.byProduct) { Object.entries(mi.byProduct).forEach(([prod, data]) => { out(` 产品: ${prod}`); (data.insights || []).forEach((ins) => { out(` · keyword="${ins.keyword}" | imp ${ins.impression || 0} | ctr ${ins.ctr ? (ins.ctr*100).toFixed(2)+'%' : '-'} | cvr ${ins.cvr ? (ins.cvr*100).toFixed(2)+'%' : '-'} | post ${ins.post_count || 0} (${ins.post_change ? (ins.post_change>0?'+':'')+(ins.post_change*100).toFixed(0)+'%' : '-'})`); }); }); } } // === Douyin 站级热榜基线 === if (A.marketIntel && !A.marketIntel.empty && A.marketIntel.douyin) { const dy = A.marketIntel.douyin; out('\n▌Douyin 站级基线'); if (dy.hotWords?.length) out(` 热词: ${dy.hotWords.slice(0,6).map(w => w.word || w[0]).join(' / ')}`); if (dy.hotChallenges?.length) out(` 热挑战: ${dy.hotChallenges.slice(0,5).map(c => c.title || c[0]).join(' / ')}`); if (dy.categoryFilterHit !== undefined) out(` 品类过滤命中: ${dy.categoryFilterHit} (${dy.categoryFilterHit === 0 ? '品牌蓝海' : '已有内容'})`); } // === XHS 痛点/亮点/场景 主题(来自 voc-analyze 的 extractThemes)=== // 主报告里的 themesSection 是基于 xhs comments 做的 // 让我直接从 voc.xiaohongshu.by_keyword[*].comments 提取 const xhsComments = []; const byKw = voc.xiaohongshu?.by_keyword || {}; Object.values(byKw).forEach((kwData) => { // comments 是 { noteId: [comment...] } 的对象结构 const cmap = kwData.comments || {}; if (Array.isArray(cmap)) { cmap.forEach((c) => { if (c && c.content) xhsComments.push(c.content); }); } else { Object.values(cmap).forEach((arr) => { (arr || []).forEach((c) => { if (c && c.content) xhsComments.push(c.content); }); }); } }); if (xhsComments.length) { const { extractThemes } = require('./voc-analyze.js'); if (extractThemes) { const xhsThemes = extractThemes(xhsComments); if (xhsThemes) { out('\n▌小红书中文 VOC 主题'); ['pain', 'bright', 'scenarios'].forEach((key) => { const arr = xhsThemes[key]; if (arr && arr.length) { out(` ${key === 'pain' ? '痛点' : key === 'bright' ? '亮点' : '场景'}:`); arr.slice(0, 8).forEach((t, i) => out(` ${i + 1}. ${t.tag || t.label} (${t.count || t.freq || 0})`)); } }); } } } } const outPath = 'data/voc-summary-for-product-dev.txt'; fs.writeFileSync(outPath, buffer.join('\n'), 'utf-8'); console.log(`\n✅ ${outPath} (${(fs.statSync(outPath).size / 1024).toFixed(1)}KB)`);