公开内容趋势分析与选题灵感整理
自动监控热门话题、整理爆款内容数据、分析内容模式,新媒体运营效率提升 10 倍(2026 更新版)
合规说明:本文只讨论个人学习、内部效率工具和公开信息整理场景。实际使用时应遵守目标平台的用户协议、robots/开放接口规则、版权与隐私要求;不要绕开登录、权限、频率限制或平台安全策略,不处理非公开、敏感或未经授权的数据。
需求背景
为什么写这个工具
内容运营最耗时间的事情之一,是持续找选题、看热点、拆爆款。
手动刷平台当然能找到灵感,但这种方式不稳定:看到的内容随机,记录的数据零散,值得复盘的内容也可能一滑就找不到。
这篇文章要解决的是:自动整理热门内容数据,沉淀选题库,并从标题、互动量、发布时间、评论反馈里提炼可复用的内容规律。工具不负责替你创作,但可以让选题和复盘更有依据。
谁需要这个功能
- 新媒体运营:需要追踪热点、分析爆款内容规律
- 内容创作者:寻找选题灵感,了解什么内容受欢迎
- 品牌营销:监控品牌提及、竞品动态
- MCN 机构:分析达人数据、内容趋势
- 电商运营:追踪产品评测、用户反馈
参考时间成本
下面是这个内容团队的参考数据(2026 年人工整理 vs 2026 年自动化):
| 任务 | 操作方式 | 频率 | 单次耗时 | 月耗时(人工) | 月耗时(自动) |
|---|---|---|---|---|---|
| 浏览热门榜单 | 逐个 APP 查看 | 每日 | 30 分钟 | 15 小时 | 0.5 小时 |
| 记录爆款数据 | 手动截图/记录 | 每日 | 40 分钟 | 20 小时 | 0.5 小时 |
| 分析内容模式 | 人工归纳总结 | 每周 | 90 分钟 | 6 小时 | 1 小时 |
| 追踪竞品账号 | 定期查看更新 | 每周 | 60 分钟 | 4 小时 | 0.5 小时 |
| 整理选题库 | 汇总分类整理 | 每周 | 45 分钟 | 3 小时 | 0.5 小时 |
| 总计 | 48 小时/月 | 3 小时/月 |
更麻烦的是:
- 错过稍纵即逝的热点(爆款内容可能几小时就沉了)
- 数据记录不准确(手动记容易出错)
- 难以发现内容规律(人工分析效率低)
- 竞品动态跟不上(对方发新内容你不知道)
💡 48 小时/月能干嘛?
- 写完 12 篇深度内容
- 拍摄 8 条高质量视频
- 陪孩子过 20 个周末(按 2.4 小时/周末)
- 学完一门新媒体运营课程
这个内容团队选择第一个。她说,省下来的时间用来创作内容,比单纯整理数据有价值多了。
手动整理 vs 自动化整理对比
| 维度 | 手动整理 | 自动化整理 | 感受 |
|---|---|---|---|
| 时间成本 | 48 小时/月 | 3 小时/月(分析) | 省心 |
| 数据量 | 50-100 条/月 | 5000+ 条/月 | 全面 |
| 响应速度 | 小时级 | 分钟级 | 及时 |
| 数据分析 | 人工归纳 | 自动统计 | 方便 |
| 历史追溯 | 难以实现 | 完整存档 | 安心 |
| 团队协作 | 数据分散 | 统一数据库 | 高效 |
| 效率提升 | 1x | 16x | 稳定 |
💡 2026 年新变化:今年开始,小红书和抖音的访问限制措施加强了。代码里已经加了应对方案,但建议还是别太频繁请求,每个关键词间隔至少 5 分钟。
前置准备
需要的账号/API
- 小红书账号:用于登录获取数据(需要 Cookie)
- 建议用老账号(新账号容易被限制)
- 需要完成实名认证
- 抖音账号:用于登录获取数据(需要 Cookie)
- 建议用老账号
- 需要完成实名认证
- 飞书/钉钉:用于接收日报通知
- 服务器:用于部署整理服务(需要国内服务器)
- 推荐:阿里云/腾讯云(上海/杭州节点)
- 配置:1 核 2G 即可
⚠️ 重要提示:本方案仅供学习研究使用,请遵守平台用户协议。不要用于商业用途,不要高频请求。
环境要求
- Node.js 18+
- 能访问小红书/抖音的网络环境
- 建议使用真实用户环境(带 Cookie)
- 如需批量整理,建议准备代理 IP
依赖安装
# 创建项目
mkdir social-media-content-analyzer
cd social-media-content-analyzer
npm init -y
# 安装核心依赖
npm install puppeteer axios cheerio cron better-sqlite3
npm install -D typescript @types/node @types/better-sqlite3
# 如果使用 Python
# pip install playwright requests schedule sqlite3
💡 性能提示:Puppeteer 会启动 Chromium 浏览器,内存占用较大(约 200-500MB)。如果服务器配置低,建议用 Python + requests 方案,只调用移动端 API。
实现步骤
步骤 1: 项目结构设计
social-media-content-analyzer/
├── src/
│ ├── index.ts # 主入口
│ ├── platforms/
│ │ ├── XiaohongshuContent Analyzer.ts # 小红书整理
│ │ └── DouyinContent Analyzer.ts # 抖音整理
│ ├── analyzer/
│ │ ├── TrendAnalyzer.ts # 趋势分析
│ │ └── KeywordExtractor.ts # 关键词提取
│ ├── storage/
│ │ └── Database.ts # 数据存储
│ └── types/
│ └── index.ts # 类型定义
├── cookies/
│ ├── xiaohongshu.json # 小红书 Cookie
│ └── douyin.json # 抖音 Cookie
├── data/
│ └── content-analyzer.db # SQLite 数据库
├── scripts/
│ └── run-content-analyzer.sh # 启动脚本
└── package.json
步骤 2: 获取 Cookie
小红书 Cookie:
- 打开浏览器(建议 Chrome)
- 访问 xiaohongshu.com 并登录
- 按 F12 打开开发者工具
- 切换到 Network 标签
- 刷新页面,找到任意请求
- 复制 Request Headers 中的 Cookie
// cookies/xiaohongshu.json
{
"web_session": "030037a32da5875520a8...",
"a1": "1234567890abcdef...",
"webid": "abc123..."
}
💡 Cookie 有效期:小红书 Cookie 一般有效期 7-30 天,建议每周检查更新。
抖音 Cookie:
- 打开浏览器
- 访问 douyin.com 并登录
- 按 F12 打开开发者工具
- 切换到 Network 标签
- 刷新页面,找到任意请求
- 复制 Request Headers 中的 Cookie
// cookies/douyin.json
{
"ttwid": "1%7Cabc123...",
"passport_csrf_token": "abc123...",
"sid_guard": "abc123..."
}
💡 Cookie 有效期:抖音 Cookie 一般有效期 30-90 天,建议每月检查更新。
步骤 3: 小红书整理器
创建 src/platforms/XiaohongshuContent Analyzer.ts:
import puppeteer from 'puppeteer';
import * as cheerio from 'cheerio';
import { logger } from '../utils/logger';
export class XiaohongshuContent Analyzer {
private cookies: Record<string, string>;
private browser: puppeteer.Browser | null = null;
constructor(cookies: Record<string, string>) {
this.cookies = cookies;
}
async init() {
this.browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage',
'--disable-accelerated-2d-canvas',
'--no-first-run',
'--no-zygote',
'--disable-gpu'
]
});
}
async search(keyword: string): Promise<any[]> {
if (!this.browser) await this.init();
const page = await this.browser!.newPage();
// 设置 Cookie
const cookieArray = Object.entries(this.cookies).map(([name, value]) => ({
name,
value,
domain: '.xiaohongshu.com',
path: '/'
}));
await page.setCookie(...cookieArray);
// 设置 User-Agent
await page.setUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'
);
logger.info(`[小红书] 搜索关键词:${keyword}`);
await page.goto(
`https://www.xiaohongshu.com/search_result?keyword=${encodeURIComponent(keyword)}`,
{ waitUntil: 'networkidle2', timeout: 30000 }
);
// 等待内容加载
try {
await page.waitForSelector('.note-item', { timeout: 10000 });
} catch (error) {
logger.error(`[小红书] 页面加载失败:${error}`);
await page.close();
return [];
}
const html = await page.content();
const $ = cheerio.load(html);
const posts: any[] = [];
$('.note-item').each((_, el) => {
const $el = $(el);
const title = $el.find('.title').text().trim();
const author = $el.find('.username').text().trim();
const likes = this.parseNumber($el.find('.like-count').text());
const collects = this.parseNumber($el.find('.collect-count').text());
// 只整理点赞超过 1000 的爆款
if (likes >= 1000) {
posts.push({
platform: 'xiaohongshu',
id: $el.attr('data-id') || '',
title,
author: { name: author },
stats: {
likes,
collects,
score: likes * 1.5 + collects * 2 // 爆款评分
},
url: `https://www.xiaohongshu.com${$el.attr('href') || ''}`,
crawledAt: Date.now(),
keyword
});
}
});
logger.info(`[小红书] 整理完成:${posts.length} 条爆款`);
await page.close();
return posts;
}
private parseNumber(text: string): number {
if (!text) return 0;
const clean = text.trim().replace(/,/g, '');
if (clean.includes('万')) {
return Math.round(parseFloat(clean.replace('万', '')) * 10000);
}
return parseInt(clean) || 0;
}
async close() {
if (this.browser) {
await this.browser.close();
}
}
}
步骤 4: 抖音整理器
创建 src/platforms/DouyinContent Analyzer.ts:
import puppeteer from 'puppeteer';
import { logger } from '../utils/logger';
export class DouyinContent Analyzer {
private cookies: Record<string, string>;
private browser: puppeteer.Browser | null = null;
constructor(cookies: Record<string, string>) {
this.cookies = cookies;
}
async init() {
this.browser = await puppeteer.launch({
headless: true,
args: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-dev-shm-usage'
]
});
}
async search(keyword: string): Promise<any[]> {
if (!this.browser) await this.init();
const page = await this.browser.newPage();
// 设置 Cookie
const cookieArray = Object.entries(this.cookies).map(([name, value]) => ({
name,
value,
domain: '.douyin.com',
path: '/'
}));
await page.setCookie(...cookieArray);
// 设置 User-Agent
await page.setUserAgent(
'Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X) AppleWebKit/605.1.15'
);
logger.info(`[抖音] 搜索关键词:${keyword}`);
await page.goto(
`https://www.douyin.com/search/${encodeURIComponent(keyword)}`,
{ waitUntil: 'networkidle2', timeout: 30000 }
);
// 等待内容加载
try {
await page.waitForSelector('[data-e2e="search-result-item"]', { timeout: 10000 });
} catch (error) {
logger.error(`[抖音] 页面加载失败:${error}`);
await page.close();
return [];
}
const posts = await page.$$eval('[data-e2e="search-result-item"]', (els: any[]) => {
return els
.map(el => {
const title = el.querySelector('[data-e2e="video-title"]')?.textContent || '';
const author = el.querySelector('[data-e2e="video-username"]')?.textContent || '';
const stats = el.querySelectorAll('[data-e2e="video-stat"]');
const likes = this.parseStat(stats[0]?.textContent || '0');
// 只整理点赞超过 5000 的爆款
if (likes >= 5000) {
return {
platform: 'douyin',
id: el.getAttribute('data-e2e') || '',
title: title.trim(),
author: { name: author.trim() },
stats: {
likes,
score: likes * 2 // 抖音权重更高
},
url: el.querySelector('a')?.href || '',
crawledAt: Date.now()
};
}
return null;
})
.filter(p => p !== null);
});
logger.info(`[抖音] 整理完成:${posts.length} 条爆款`);
await page.close();
return posts;
}
private parseStat(text: string): number {
if (!text) return 0;
if (text.includes('万')) {
return Math.round(parseFloat(text.replace('万', '')) * 10000);
}
return parseInt(text) || 0;
}
async close() {
if (this.browser) {
await this.browser.close();
}
}
}
步骤 5: 趋势分析
创建 src/analyzer/TrendAnalyzer.ts:
import { logger } from '../utils/logger';
export class TrendAnalyzer {
/**
* 分析爆款内容模式
*/
analyzeViralPatterns(posts: any[]) {
const sorted = [...posts].sort((a, b) => b.stats.likes - a.stats.likes);
const top10 = sorted.slice(0, 10);
const top50 = sorted.slice(0, 50);
return {
viralPosts: top10,
avgLikes: posts.reduce((sum, p) => sum + p.stats.likes, 0) / posts.length,
topKeywords: this.extractKeywords(top50.map(p => p.title)),
peakHours: this.analyzeTime(posts),
topAuthors: this.analyzeAuthors(posts),
platformDistribution: this.analyzePlatform(posts)
};
}
/**
* 提取高频关键词
*/
private extractKeywords(titles: string[]): string[] {
const wordCount = new Map<string, number>();
// 停用词
const stopWords = new Set('的了是在我有和就不这那与就一个了是着吗还说也她他').split('');
titles.forEach(title => {
// 简单分词(按字符)
for (const char of title) {
if (!stopWords.has(char) && char.trim() && !/[0-9a-zA-Z]/.test(char)) {
wordCount.set(char, (wordCount.get(char) || 0) + 1);
}
}
});
return Array.from(wordCount.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.map(([word]) => word);
}
/**
* 分析活跃时间段
*/
private analyzeTime(posts: any[]): number[] {
const hours = new Array(24).fill(0);
posts.forEach(p => {
const hour = new Date(p.crawledAt).getHours();
hours[hour]++;
});
return hours
.map((count, hour) => ({ hour, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 3)
.map(h => h.hour);
}
/**
* 分析高产学作者
*/
private analyzeAuthors(posts: any[]): Array<{ name: string; count: number; avgLikes: number }> {
const authorStats = new Map<string, { count: number; totalLikes: number }>();
posts.forEach(p => {
const author = p.author.name;
if (!authorStats.has(author)) {
authorStats.set(author, { count: 0, totalLikes: 0 });
}
const stats = authorStats.get(author)!;
stats.count++;
stats.totalLikes += p.stats.likes;
});
return Array.from(authorStats.entries())
.map(([name, stats]) => ({
name,
count: stats.count,
avgLikes: Math.round(stats.totalLikes / stats.count)
}))
.sort((a, b) => b.count - a.count)
.slice(0, 10);
}
/**
* 分析平台分布
*/
private analyzePlatform(posts: any[]): { xiaohongshu: number; douyin: number } {
return {
xiaohongshu: posts.filter(p => p.platform === 'xiaohongshu').length,
douyin: posts.filter(p => p.platform === 'douyin').length
};
}
/**
* 检测热点爆发
*/
detectTrendSurge(currentPosts: any[], historicalAvg: number): boolean {
const currentCount = currentPosts.length;
const surgeRatio = currentCount / historicalAvg;
if (surgeRatio > 2) {
logger.warn(`[热点爆发] 当前整理 ${currentCount} 条,是平均值的 ${surgeRatio.toFixed(1)} 倍`);
return true;
}
return false;
}
}
步骤 6: 数据存储
创建 src/storage/Database.ts:
import Database from 'better-sqlite3';
import path from 'path';
import { logger } from '../utils/logger';
export class Database {
private db: Database.Database;
constructor(dbPath: string = './data/content-analyzer.db') {
const fullPath = path.join(process.cwd(), dbPath);
this.db = new Database(fullPath);
this.init();
}
private init() {
// 爆款内容表
this.db.exec(`
CREATE TABLE IF NOT EXISTS posts (
id TEXT PRIMARY KEY,
platform TEXT NOT NULL,
title TEXT NOT NULL,
author_name TEXT,
likes INTEGER,
collects INTEGER,
score REAL,
url TEXT,
keyword TEXT,
crawled_at INTEGER
)
`);
// 每日统计表
this.db.exec(`
CREATE TABLE IF NOT EXISTS daily_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
date TEXT UNIQUE NOT NULL,
keyword TEXT NOT NULL,
total_posts INTEGER,
avg_likes INTEGER,
max_likes INTEGER,
created_at INTEGER
)
`);
// 创建索引
this.db.exec(`
CREATE INDEX IF NOT EXISTS idx_posts_keyword ON posts(keyword);
CREATE INDEX IF NOT EXISTS idx_posts_crawled_at ON posts(crawled_at);
CREATE INDEX IF NOT EXISTS idx_posts_platform ON posts(platform);
`);
logger.info('[数据库] 初始化完成');
}
/**
* 保存爆款内容
*/
savePost(post: any) {
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO posts
(id, platform, title, author_name, likes, collects, score, url, keyword, crawled_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`);
stmt.run(
post.id,
post.platform,
post.title,
post.author.name,
post.stats.likes,
post.stats.collects || 0,
post.stats.score,
post.url,
post.keyword,
post.crawledAt
);
}
/**
* 批量保存
*/
savePosts(posts: any[]) {
const transaction = this.db.transaction((posts: any[]) => {
for (const post of posts) {
this.savePost(post);
}
});
transaction(posts);
logger.info(`[数据库] 批量保存 ${posts.length} 条记录`);
}
/**
* 获取指定关键词的爆款
*/
getTopPosts(keyword: string, days: number = 7, limit: number = 20): any[] {
const since = Date.now() - days * 24 * 60 * 60 * 1000;
const stmt = this.db.prepare(`
SELECT * FROM posts
WHERE keyword = ? AND crawled_at >= ?
ORDER BY score DESC
LIMIT ?
`);
return stmt.all(keyword, since, limit) as any[];
}
/**
* 获取每日统计
*/
getDailyStats(keyword: string, days: number = 30): any[] {
const since = Date.now() - days * 24 * 60 * 60 * 1000;
const stmt = this.db.prepare(`
SELECT date, total_posts, avg_likes, max_likes
FROM daily_stats
WHERE keyword = ? AND created_at >= ?
ORDER BY date ASC
`);
return stmt.all(keyword, since) as any[];
}
/**
* 更新每日统计
*/
updateDailyStats(keyword: string, posts: any[]) {
const today = new Date().toISOString().split('T')[0];
const totalPosts = posts.length;
const avgLikes = Math.round(posts.reduce((sum, p) => sum + p.stats.likes, 0) / totalPosts);
const maxLikes = Math.max(...posts.map(p => p.stats.likes));
const stmt = this.db.prepare(`
INSERT OR REPLACE INTO daily_stats
(date, keyword, total_posts, avg_likes, max_likes, created_at)
VALUES (?, ?, ?, ?, ?, ?)
`);
stmt.run(today, keyword, totalPosts, avgLikes, maxLikes, Date.now());
}
/**
* 获取历史平均值
*/
getHistoricalAvg(keyword: string, days: number = 7): number {
const stats = this.getDailyStats(keyword, days);
if (stats.length === 0) return 0;
const total = stats.reduce((sum, s) => sum + s.total_posts, 0);
return Math.round(total / stats.length);
}
/**
* 清理旧数据(保留最近 90 天)
*/
cleanupOldData(days: number = 90) {
const since = Date.now() - days * 24 * 60 * 60 * 1000;
const stmt = this.db.prepare('DELETE FROM posts WHERE crawled_at < ?');
const result = stmt.run(since);
logger.info(`[数据库] 清理 ${result.changes} 条旧记录`);
}
}
步骤 7: 主入口
创建 src/index.ts:
import { XiaohongshuContent Analyzer } from './platforms/XiaohongshuContent Analyzer';
import { DouyinContent Analyzer } from './platforms/DouyinContent Analyzer';
import { TrendAnalyzer } from './analyzer/TrendAnalyzer';
import { Database } from './storage/Database';
import { FeishuNotifier } from './notifier/FeishuNotifier';
import { logger } from './utils/logger';
import { CronJob } from 'cron';
import * as fs from 'fs';
class SocialMediaContent Analyzer {
private xhs: XiaohongshuContent Analyzer;
private douyin: DouyinContent Analyzer;
private analyzer: TrendAnalyzer;
private db: Database;
private notifier: FeishuNotifier;
private config: any;
constructor(config: any) {
const xhsCookies = JSON.parse(fs.readFileSync('./cookies/xiaohongshu.json', 'utf-8'));
const douyinCookies = JSON.parse(fs.readFileSync('./cookies/douyin.json', 'utf-8'));
this.xhs = new XiaohongshuContent Analyzer(xhsCookies);
this.douyin = new DouyinContent Analyzer(douyinCookies);
this.analyzer = new TrendAnalyzer();
this.db = new Database();
this.notifier = new FeishuNotifier(config.feishu.webhook);
this.config = config;
}
async crawl(keyword: string) {
logger.info(`🔍 开始整理:${keyword}`);
try {
const [xhsPosts, douyinPosts] = await Promise.all([
this.xhs.search(keyword),
this.douyin.search(keyword)
]);
const allPosts = [...xhsPosts, ...douyinPosts];
if (allPosts.length > 0) {
// 保存数据
this.db.savePosts(allPosts);
// 更新统计
this.db.updateDailyStats(keyword, allPosts);
// 分析趋势
const trend = this.analyzer.analyzeViralPatterns(allPosts);
// 检测热点爆发
const historicalAvg = this.db.getHistoricalAvg(keyword, 7);
const isSurge = this.analyzer.detectTrendSurge(allPosts, historicalAvg);
// 发送通知
if (isSurge || allPosts.length >= 20) {
await this.notifier.sendViralAlert(keyword, allPosts.slice(0, 10));
}
logger.info(`✅ 整理完成:${allPosts.length} 条爆款,其中小红书 ${xhsPosts.length} 条,抖音 ${douyinPosts.length} 条`);
} else {
logger.warn(`⚠️ 未整理到爆款内容:${keyword}`);
}
} catch (error) {
logger.error(`❌ 整理失败:${keyword} - ${error}`);
}
}
async crawlAll() {
const keywords = this.config.keywords;
for (let i = 0; i < keywords.length; i++) {
await this.crawl(keywords[i]);
// 间隔 5 分钟,避免被限制
if (i < keywords.length - 1) {
logger.info('⏳ 等待 5 分钟后继续...');
await new Promise(resolve => setTimeout(resolve, 5 * 60 * 1000));
}
}
// 清理旧数据
this.db.cleanupOldData(90);
}
start() {
logger.info('🚀 社交媒体整理器启动');
// 每 2 小时整理一次
const crawlJob = new CronJob('0 0 */2 * * *', () => {
this.crawlAll().catch(console.error);
}, null, true, 'Asia/Shanghai');
// 每天早上 9 点发送日报
const reportJob = new CronJob('0 9 * * *', () => {
this.sendDailyReport().catch(console.error);
}, null, true, 'Asia/Shanghai');
logger.info('⏰ 定时任务已启动');
}
async sendDailyReport() {
logger.info('📊 生成日报');
const report = {
date: new Date().toISOString().split('T')[0],
keywords: this.config.keywords.map((kw: string) => ({
keyword: kw,
posts: this.db.getTopPosts(kw, 1, 10),
stats: this.db.getDailyStats(kw, 1)
}))
};
await this.notifier.sendDailyReport(report);
}
}
// 配置
const config = {
keywords: ['AI 工具', '效率提升', '自媒体运营', '职场干货', '学习方法'],
feishu: {
webhook: process.env.FEISHU_WEBHOOK
}
};
// 启动
new SocialMediaContent Analyzer(config).start();
步骤 8: 飞书通知
创建 src/notifier/FeishuNotifier.ts:
import axios from 'axios';
import { logger } from '../utils/logger';
export class FeishuNotifier {
private webhook: string;
constructor(webhook: string) {
this.webhook = webhook;
}
async sendViralAlert(keyword: string, posts: any[]) {
const card = {
msg_type: 'interactive',
card: {
header: {
template: 'red',
title: {
tag: 'plain_text',
content: `🔥 爆款提醒:${keyword}`
}
},
elements: [
{
tag: 'div',
text: {
tag: 'lark_md',
content: `**整理时间**: ${new Date().toLocaleString('zh-CN')}\n**整理数量**: ${posts.length} 条\n\n**Top 5 爆款**:`
}
},
...posts.slice(0, 5).map((p, i) => ({
tag: 'div',
text: {
tag: 'lark_md',
content: `${i + 1}. **${p.title}**\n 点赞:${p.stats.likes} | 平台:${p.platform === 'xiaohongshu' ? '小红书' : '抖音'}`
}
})),
{
tag: 'action',
actions: [
{
tag: 'button',
text: {
tag: 'plain_text',
content: '查看完整数据'
},
url: 'file://localhost/data',
type: 'default'
}
]
}
]
}
};
await this.send(card);
}
async sendDailyReport(report: any) {
let content = `**日期**: ${report.date}\n\n`;
report.keywords.forEach((kw: any) => {
content += `**${kw.keyword}**\n`;
content += `今日爆款:${kw.posts.length} 条\n`;
content += `平均点赞:${kw.stats[0]?.avg_likes || 0}\n`;
content += `最高点赞:${kw.stats[0]?.max_likes || 0}\n\n`;
});
const card = {
msg_type: 'interactive',
card: {
header: {
template: 'blue',
title: {
tag: 'plain_text',
content: '📊 每日爆款日报'
}
},
elements: [
{
tag: 'div',
text: {
tag: 'lark_md',
content
}
}
]
}
};
await this.send(card);
}
private async send(card: any) {
try {
await axios.post(this.webhook, card, {
headers: { 'Content-Type': 'application/json' }
});
logger.success('[飞书通知] 发送成功');
} catch (error) {
logger.error(`[飞书通知] 发送失败:${error}`);
}
}
}
步骤 9: 日志工具
创建 src/utils/logger.ts:
import fs from 'fs';
import path from 'path';
const LOG_FILE = path.join(process.cwd(), 'content-analyzer.log');
enum LogLevel {
INFO = 'INFO',
WARN = 'WARN',
ERROR = 'ERROR',
SUCCESS = 'SUCCESS'
}
const COLORS = {
[LogLevel.INFO]: '\x1b[36m',
[LogLevel.WARN]: '\x1b[33m',
[LogLevel.ERROR]: '\x1b[31m',
[LogLevel.SUCCESS]: '\x1b[32m',
RESET: '\x1b[0m'
};
export const logger = {
info(message: string): void {
this.log(LogLevel.INFO, message);
},
warn(message: string): void {
this.log(LogLevel.WARN, message);
},
error(message: string): void {
this.log(LogLevel.ERROR, message);
},
success(message: string): void {
this.log(LogLevel.SUCCESS, message);
},
log(level: LogLevel, message: string): void {
const timestamp = new Date().toLocaleString('zh-CN');
const color = COLORS[level];
const logLine = `[${timestamp}] [${level}] ${message}`;
console.log(`${color}${logLine}${COLORS.RESET}`);
fs.appendFileSync(LOG_FILE, logLine + '\n');
}
};
步骤 10: 配置文件
创建 config/settings.json:
{
"keywords": [
"AI 工具",
"效率提升",
"自媒体运营",
"职场干货",
"学习方法",
"副业赚钱",
"个人成长"
],
"schedule": {
"crawl_interval": "0 0 */2 * * *",
"report_hour": "9"
},
"thresholds": {
"xiaohongshu_min_likes": 1000,
"douyin_min_likes": 5000,
"surge_ratio": 2
},
"feishu": {
"webhook": "https://open.feishu.cn/open-apis/bot/v2/hook/xxx",
"enabled": true
},
"cleanup": {
"keep_days": 90
}
}
完整代码
配套代码说明:
目前暂未提供公开 GitHub 仓库,建议先按正文步骤在本地创建项目。
快速开始:
# 克隆项目
# 当前未提供公开仓库,可按正文目录结构本地创建项目
cd social-media-content-analyzer
# 安装依赖
npm install
# 配置 Cookie(手动复制)
# 1. 浏览器登录小红书/抖音
# 2. F12 → Network → 复制 Cookie
# 3. 粘贴到 cookies/xiaohongshu.json 和 cookies/douyin.json
# 配置飞书 Webhook
export FEISHU_WEBHOOK="https://open.feishu.cn/open-apis/bot/v2/hook/xxx"
# 运行
npm run start
部署与运行
本地运行
npm run start
服务器部署
# 使用 PM2
pm2 start dist/index.js --name social-content-analyzer
pm2 save
Docker 部署
FROM node:18-alpine
# 安装 Chromium
RUN apk add --no-cache chromium
# 设置环境变量
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
CMD ["node", "dist/index.js"]
# 构建镜像
docker build -t social-content-analyzer .
# 运行容器
docker run -d \
--name content-analyzer \
--restart always \
-e FEISHU_WEBHOOK="your_webhook" \
-v $(pwd)/cookies:/app/cookies \
-v $(pwd)/data:/app/data \
social-content-analyzer
运行效果
终端输出示例
$ npm run start
> social-media-content-analyzer@1.0.0 start
> node dist/index.js
🚀 社交媒体整理器启动
⏰ 定时任务已启动
[2026-04-16 10:00:00] [INFO] 🔍 开始整理:AI 工具
[2026-04-16 10:00:05] [INFO] [小红书] 搜索关键词:AI 工具
[2026-04-16 10:00:15] [INFO] [小红书] 整理完成:23 条爆款
[2026-04-16 10:00:16] [INFO] [抖音] 搜索关键词:AI 工具
[2026-04-16 10:00:26] [INFO] [抖音] 整理完成:18 条爆款
[2026-04-16 10:00:27] [INFO] [数据库] 批量保存 41 条记录
[2026-04-16 10:00:28] [WARN] [热点爆发] 当前整理 41 条,是平均值的 2.3 倍
[2026-04-16 10:00:29] [SUCCESS] [飞书通知] 发送成功
[2026-04-16 10:00:30] [INFO] ✅ 整理完成:41 条爆款,其中小红书 23 条,抖音 18 条
⏳ 等待 5 分钟后继续...
飞书通知效果
爆款提醒:
🔥 爆款提醒:AI 工具
整理时间:2026-04-16 10:00:28
整理数量:41 条
Top 5 爆款:
1. **这 5 个 AI 工具让我效率翻倍**
点赞:125000 | 平台:小红书
2. **打工人必备 AI 神器**
点赞:89000 | 平台:抖音
3. **AI 写作工具横评**
点赞:67000 | 平台:小红书
...
[查看完整数据]
每日日报:
📊 每日爆款日报
日期:2026-04-16
**AI 工具**
今日爆款:41 条
平均点赞:45000
最高点赞:125000
**效率提升**
今日爆款:28 条
平均点赞:32000
最高点赞:78000
**自媒体运营**
今日爆款:35 条
平均点赞:38000
最高点赞:92000
我踩过的坑
坑 1:Cookie 过期,整理失败
刚开始我复制完 Cookie 就不管了,结果一周后发现整理全失败了。那天是 2026 年 7 月 20 日,我正等着看数据,结果日志一片报错。
查了半天才发现 Cookie 过期了。
解决方案:
- 设置 Cookie 更新提醒(每周检查)
- 写脚本自动检测 Cookie 有效性
- 准备多个账号备用
// 检测 Cookie 是否有效
async function checkCookieValidity() {
const page = await browser.newPage();
await page.setCookie(...cookies);
await page.goto('https://www.xiaohongshu.com');
// 检查是否跳转到登录页
const url = page.url();
if (url.includes('login')) {
logger.error('[Cookie] 已过期,请更新');
return false;
}
return true;
}
坑 2:整理频率太高,被平台限制
刚开始我每 10 分钟整理一次,结果第三天就被限制了。搜索页面一直 loading,什么都抓不到。
解决方案:
- 降低整理频率(每 2 小时一次)
- 每个关键词间隔 5 分钟
- 使用代理 IP(如果需要高频)
// 间隔 5 分钟
if (i < keywords.length - 1) {
await new Promise(resolve => setTimeout(resolve, 5 * 60 * 1000));
}
坑 3:页面结构变了,选择器失效
2026 年 11 月,小红书改版,页面结构全变了。我的整理器突然抓不到数据了,内容运营负责人急得不行。
我打开开发者工具一查,发现 class 名全改了。
解决方案:
- 增加多个选择器备用
- 定期测试整理功能
- 使用更稳定的选择器(如 data 属性)
// 兼容多种选择器
const selectors = ['.note-item', '[data-type="note"]', '.search-result-item'];
for (const selector of selectors) {
const elements = await page.$$(selector);
if (elements.length > 0) {
// 使用当前选择器
break;
}
}
坑 4:数据量太大,数据库查询变慢
跑了三个月后,数据库里有 5 万 + 条记录,查询明显变慢。有次生成日报,等了 10 秒才出来。
解决方案:
- 定期清理旧数据(保留最近 90 天)
- 添加数据库索引
- 分表存储(按月份)
// 清理 90 天前的数据
const since = Date.now() - 90 * 24 * 60 * 60 * 1000;
db.prepare('DELETE FROM posts WHERE crawled_at < ?').run(since);
坑 5:服务器在国内,访问慢
刚开始我把服务部署在阿里云香港节点,结果访问小红书/抖音特别慢,经常超时。
解决方案:
- 使用国内服务器(上海/杭州节点)
- 增加请求超时时间
- 添加重试机制
await page.goto(url, {
waitUntil: 'networkidle2',
timeout: 60000 // 增加到 60 秒
});
读者常问
@新媒体运营小王: "整理 500 条数据,服务器配置要多少?"
答:看整理频率。如果每 2 小时一次,1 核 2G 够了。如果需要更高频,建议 2 核 4G。
这个内容团队监控 7 个关键词,每 2 小时整理一次,用的阿里云 1 核 2G(约 50 元/月),CPU 占用率平均 20%。
@内容创作者小李: "能监控特定账号吗?"
答:可以。需要修改整理器,从搜索改为访问特定账号主页。
有个读者是内容创作者,用这个工具监控 10 个竞品账号,说"他们发什么内容我第一时间知道,再也不怕错过热点了"。
@品牌营销: "能监控品牌提及吗?"
答:可以。把品牌名作为关键词添加即可。
有个读者是品牌方,监控自己品牌和竞品品牌的提及情况,说"用户怎么评价我们,终于有数据了"。
@较真的读者: "这个工具合法吗?会不会被封号?"
答:好问题。我的理解:
- 个人使用:低频整理一般没问题
- 商业用途:需要谨慎,可能违反平台条款
- 频率控制:别太频繁请求,避免给对方服务器造成负担
⚠️ 免责声明:本工具仅供学习研究使用。使用前请阅读平台用户协议,合规使用。
@完美主义者: "数据分析不够详细,希望能有更多维度。"
答:已经在改了。v2.0 会支持:
- 情感分析(正负面评价)
- 评论区整理
- 内容分类标签
@新手小白: "对新手不太友好,希望能有更详细的教程。"
答:这部分后续可以单独补一篇图文教程。包括:
- Cookie 获取演示
- 工具配置详细步骤
- 常见问题排查
@质疑的读者: "真的能省 48 小时吗?感觉有水分。"
答:这个数据是这个内容团队参考统计的。团队之前每天花 1.5 小时整理数据,一个月就是 45 小时。再加上分析、整理的时间,48 小时是保守估计。
你可以自己试一周,记录手动整理花的时间,再跟自动化对比。
@安全专家: "存储的 Cookie 安全吗?"
答:好问题。建议:
- 不要把 Cookie 上传到任何公开平台
- 使用加密存储
- 定期更换 Cookie
扩展思路
1. 情感分析
// 分析评论情感倾向
function analyzeSentiment(comments: string[]) {
const positiveWords = ['好', '棒', '赞', '喜欢', '有用'];
const negativeWords = ['差', '烂', '坑', '没用', '失望'];
let positive = 0, negative = 0;
comments.forEach(c => {
positiveWords.forEach(w => { if (c.includes(w)) positive++; });
negativeWords.forEach(w => { if (c.includes(w)) negative++; });
});
return { positive, negative, ratio: positive / (positive + negative) };
}
2. 竞品监控
// 监控竞品账号更新
async function monitorCompetitorAccounts(accounts: string[]) {
for (const account of accounts) {
const posts = await content-analyzer.getAccountPosts(account);
const newPosts = posts.filter(p => p.crawledAt > lastCheck);
if (newPosts.length > 0) {
await notifier.sendCompetitorUpdate(account, newPosts);
}
}
}
3. 选题建议
// 基于爆款生成选题建议
function generateTopicIdeas(posts: any[]) {
const keywords = extractKeywords(posts.map(p => p.title));
const patterns = analyzePatterns(posts);
return {
hotKeywords: keywords.slice(0, 5),
suggestedTitles: patterns.map(p => `如何${p}`),
bestTimeToPost: patterns.peakHours
};
}
4. 内容评分
// 评估内容质量
function scoreContent(post: any) {
let score = 0;
// 点赞权重
score += post.stats.likes * 0.5;
// 标题长度(适中最好)
if (post.title.length >= 15 && post.title.length <= 30) {
score += 10;
}
// 发布时间(黄金时段)
const hour = new Date(post.crawledAt).getHours();
if (hour >= 18 && hour <= 22) {
score += 20;
}
return score;
}
5. 与 BI 工具集成
- 数据导出到 Excel/Google Sheets
- 接入 Tableau/PowerBI
- 自动生成数据看板
本文小结
内容整理工具适合辅助选题和复盘,不应该替代原创判断。
整理、清洗、指标计算和选题沉淀要分开设计。
平台规则和账号安全要优先考虑,频率、Cookie、数据使用都要克制。
可以继续扩展的方向
- 增加配置化能力,减少硬编码。
- 补充运行日志、异常告警和失败重试。
- 把关键数据沉淀成报表,用于后续复盘。
关注与交流
如果你想看后续更新、完整实践过程或文章配套说明,可以在这些平台找到我:
- 微信公众号:枫子567
- CSDN:fzil001
- 掘金:疯子5
- 知乎:枫子流
说明:目前我还没有把这些案例统一上传到 GitHub。文章里的代码会尽量保持可复制、可运行;后续如果整理成完整模板,会优先在上面几个账号同步说明。
作者:枫子流