Files
Ubuntu 32b43f85bd a
2026-07-03 16:58:28 +08:00

580 lines
24 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
小红书数据分析模块
分析发布效果,优化内容策略
"""
import os
import json
import sqlite3
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import numpy as np
from collections import Counter
class XiaohongshuDataAnalyzer:
"""小红书数据分析器"""
def __init__(self):
self.publish_db = "/home/ubuntu/.nanobot/workspace/xiaohongshu_publish_records"
self.monitor_db = "/home/ubuntu/.nanobot/workspace/xiaohongshu_monitor.db"
self.ab_test_db = "/home/ubuntu/.nanobot/workspace/xiaohongshu_ab_test.db"
self.output_dir = "/home/ubuntu/.nanobot/workspace/xiaohongshu_analysis"
os.makedirs(self.output_dir, exist_ok=True)
def analyze_publish_patterns(self, days=30):
"""分析发布模式"""
print(f"📊 分析最近{days}天发布模式...")
# 收集发布记录
publish_files = []
for filename in os.listdir(self.publish_db):
if filename.endswith('.json'):
filepath = os.path.join(self.publish_db, filename)
mtime = os.path.getmtime(filepath)
file_age_days = (time.time() - mtime) / (24 * 3600)
if file_age_days <= days:
publish_files.append((filepath, mtime))
if not publish_files:
print(f"ℹ️ 最近{days}天无发布记录")
return None
# 分析数据
publish_dates = []
sources = []
titles = []
for filepath, mtime in publish_files:
try:
with open(filepath, 'r', encoding='utf-8') as f:
record = json.load(f)
publish_time = record.get('publish_time')
if publish_time:
publish_dates.append(datetime.fromisoformat(publish_time))
article = record.get('article', {})
sources.append(article.get('source', '未知'))
titles.append(article.get('title', ''))
except Exception as e:
print(f"❌ 读取文件失败 {filepath}: {e}")
# 生成分析报告
analysis = {
'total_publishes': len(publish_files),
'date_range': {
'start': min(publish_dates).strftime('%Y-%m-%d') if publish_dates else '无',
'end': max(publish_dates).strftime('%Y-%m-%d') if publish_dates else '无'
},
'source_distribution': dict(Counter(sources)),
'avg_publishes_per_day': len(publish_files) / days if days > 0 else 0,
'title_length_stats': {
'avg': np.mean([len(t) for t in titles]) if titles else 0,
'min': min([len(t) for t in titles]) if titles else 0,
'max': max([len(t) for t in titles]) if titles else 0
}
}
# 生成可视化
self._create_publish_visualization(publish_dates, sources, days)
return analysis
def _create_publish_visualization(self, publish_dates, sources, days):
"""创建发布可视化"""
try:
# 按日期统计
date_counts = {}
for date in publish_dates:
date_str = date.strftime('%Y-%m-%d')
date_counts[date_str] = date_counts.get(date_str, 0) + 1
# 按来源统计
source_counts = Counter(sources)
# 创建图表
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# 1. 发布时间分布
if date_counts:
dates = list(date_counts.keys())
counts = list(date_counts.values())
axes[0, 0].bar(dates, counts)
axes[0, 0].set_title('每日发布数量')
axes[0, 0].set_xlabel('日期')
axes[0, 0].set_ylabel('发布数量')
axes[0, 0].tick_params(axis='x', rotation=45)
# 2. 来源分布
if source_counts:
sources_list = list(source_counts.keys())
source_values = list(source_counts.values())
axes[0, 1].pie(source_values, labels=sources_list, autopct='%1.1f%%')
axes[0, 1].set_title('文章来源分布')
# 3. 发布时间点分布
if publish_dates:
hours = [d.hour for d in publish_dates]
hour_counts = Counter(hours)
hour_keys = list(hour_counts.keys())
hour_values = [hour_counts.get(h, 0) for h in range(24)]
axes[1, 0].bar(range(24), hour_values)
axes[1, 0].set_title('发布时间点分布')
axes[1, 0].set_xlabel('小时')
axes[1, 0].set_ylabel('发布数量')
axes[1, 0].set_xticks(range(0, 24, 2))
# 4. 发布趋势
if date_counts:
# 按日期排序
sorted_dates = sorted(date_counts.items())
trend_dates = [d[0] for d in sorted_dates]
trend_counts = [d[1] for d in sorted_dates]
axes[1, 1].plot(trend_dates, trend_counts, marker='o')
axes[1, 1].set_title('发布趋势')
axes[1, 1].set_xlabel('日期')
axes[1, 1].set_ylabel('发布数量')
axes[1, 1].tick_params(axis='x', rotation=45)
plt.tight_layout()
# 保存图表
chart_file = os.path.join(self.output_dir, f'publish_analysis_{days}d.png')
plt.savefig(chart_file, dpi=150, bbox_inches='tight')
plt.close()
print(f"📈 可视化图表已保存: {chart_file}")
except Exception as e:
print(f"❌ 创建可视化失败: {e}")
def analyze_cookie_health(self, days=30):
"""分析Cookie健康状况"""
print(f"🔍 分析最近{days}天Cookie健康状况...")
try:
conn = sqlite3.connect(self.monitor_db)
cursor = conn.cursor()
# 查询最近数据
cursor.execute('''
SELECT
DATE(check_time) as check_date,
COUNT(*) as total_checks,
SUM(CASE WHEN cookie_valid THEN 1 ELSE 0 END) as valid_checks
FROM cookie_monitor
WHERE check_time >= datetime('now', ?)
GROUP BY DATE(check_time)
ORDER BY check_date DESC
''', (f'-{days} days',))
daily_stats = cursor.fetchall()
# 计算总体统计
cursor.execute('''
SELECT
COUNT(*) as total_checks,
SUM(CASE WHEN cookie_valid THEN 1 ELSE 0 END) as valid_checks
FROM cookie_monitor
WHERE check_time >= datetime('now', ?)
''', (f'-{days} days',))
overall_stats = cursor.fetchone()
conn.close()
if not overall_stats or overall_stats[0] == 0:
print(f"ℹ️ 最近{days}天无Cookie检查记录")
return None
total_checks, valid_checks = overall_stats
success_rate = (valid_checks / total_checks) * 100 if total_checks > 0 else 0
analysis = {
'total_checks': total_checks,
'valid_checks': valid_checks,
'success_rate': success_rate,
'daily_stats': []
}
for check_date, daily_total, daily_valid in daily_stats:
daily_rate = (daily_valid / daily_total) * 100 if daily_total > 0 else 0
analysis['daily_stats'].append({
'date': check_date,
'total': daily_total,
'valid': daily_valid,
'rate': daily_rate
})
# 生成健康报告
health_status = "良好"
if success_rate < 80:
health_status = "一般"
if success_rate < 50:
health_status = "较差"
if success_rate < 20:
health_status = "危险"
analysis['health_status'] = health_status
analysis['recommendation'] = self._get_cookie_recommendation(success_rate)
return analysis
except Exception as e:
print(f"❌ 分析Cookie健康失败: {e}")
return None
def _get_cookie_recommendation(self, success_rate):
"""获取Cookie建议"""
if success_rate >= 90:
return "Cookie状态优秀,无需操作"
elif success_rate >= 80:
return "Cookie状态良好,建议定期检查"
elif success_rate >= 60:
return "Cookie状态一般,建议近期检查更新"
elif success_rate >= 40:
return "Cookie状态较差,建议尽快更新"
else:
return "Cookie状态危险,需要立即更新"
def analyze_ab_test_results(self, days=30):
"""分析A/B测试结果"""
print(f"🎯 分析最近{days}天A/B测试结果...")
try:
conn = sqlite3.connect(self.ab_test_db)
cursor = conn.cursor()
# 查询测试数据
cursor.execute('''
SELECT
style_used,
COUNT(*) as test_count,
AVG(engagement_score) as avg_score,
MAX(engagement_score) as max_score,
SUM(likes) as total_likes,
SUM(collects) as total_collects,
SUM(comments) as total_comments,
SUM(shares) as total_shares
FROM ab_tests
WHERE test_date >= datetime('now', ?)
AND engagement_score > 0
GROUP BY style_used
ORDER BY avg_score DESC
''', (f'-{days} days',))
style_results = cursor.fetchall()
# 查询最佳表现
cursor.execute('''
SELECT
article_title,
style_used,
engagement_score,
likes,
collects,
comments,
shares
FROM ab_tests
WHERE test_date >= datetime('now', ?)
AND engagement_score > 0
ORDER BY engagement_score DESC
LIMIT 5
''', (f'-{days} days',))
top_performers = cursor.fetchall()
conn.close()
if not style_results:
print(f"ℹ️ 最近{days}天无A/B测试数据")
return None
analysis = {
'total_tests': sum(r[1] for r in style_results),
'style_performance': [],
'top_performers': [],
'recommendations': []
}
# 分析风格表现
for style, count, avg_score, max_score, likes, collects, comments, shares in style_results:
analysis['style_performance'].append({
'style': style,
'test_count': count,
'avg_score': avg_score,
'max_score': max_score,
'total_likes': likes or 0,
'total_collects': collects or 0,
'total_comments': comments or 0,
'total_shares': shares or 0
})
# 分析最佳表现
for title, style, score, likes, collects, comments, shares in top_performers:
analysis['top_performers'].append({
'title': title[:50] + '...' if len(title) > 50 else title,
'style': style,
'score': score,
'likes': likes or 0,
'collects': collects or 0,
'comments': comments or 0,
'shares': shares or 0
})
# 生成推荐
best_style = max(analysis['style_performance'], key=lambda x: x['avg_score'])
worst_style = min(analysis['style_performance'], key=lambda x: x['avg_score'])
analysis['recommendations'].append({
'type': '最佳风格',
'style': best_style['style'],
'reason': f"平均互动分数最高: {best_style['avg_score']:.2f}",
'confidence': min(best_style['test_count'] / 5, 1.0)
})
analysis['recommendations'].append({
'type': '避免风格',
'style': worst_style['style'],
'reason': f"平均互动分数最低: {worst_style['avg_score']:.2f}",
'suggestion': '减少使用或优化该风格'
})
# 互动类型分析
total_likes = sum(s['total_likes'] for s in analysis['style_performance'])
total_collects = sum(s['total_collects'] for s in analysis['style_performance'])
total_comments = sum(s['total_comments'] for s in analysis['style_performance'])
total_shares = sum(s['total_shares'] for s in analysis['style_performance'])
total_engagement = total_likes + total_collects + total_comments + total_shares
if total_engagement > 0:
engagement_distribution = {
'likes': total_likes / total_engagement,
'collects': total_collects / total_engagement,
'comments': total_comments / total_engagement,
'shares': total_shares / total_engagement
}
# 分析哪种互动最多
max_engagement = max(engagement_distribution.items(), key=lambda x: x[1])
analysis['engagement_insight'] = {
'distribution': engagement_distribution,
'most_common': max_engagement[0],
'percentage': max_engagement[1] * 100
}
return analysis
except Exception as e:
print(f"❌ 分析A/B测试失败: {e}")
return None
def generate_comprehensive_report(self, days=30):
"""生成综合分析报告"""
print(f"📋 生成{days}天综合分析报告...")
report = f"📊 小红书自动化系统综合分析报告\n"
report += "=" * 60 + "\n\n"
report += f"📅 报告周期: 最近{days}天\n"
report += f"⏰ 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
# 1. 发布模式分析
publish_analysis = self.analyze_publish_patterns(days)
if publish_analysis:
report += "1. 📱 发布模式分析\n"
report += f" 总发布数量: {publish_analysis['total_publishes']} 篇\n"
report += f" 时间范围: {publish_analysis['date_range']['start']} 至 {publish_analysis['date_range']['end']}\n"
report += f" 日均发布: {publish_analysis['avg_publishes_per_day']:.2f} 篇\n\n"
report += " 文章来源分布:\n"
for source, count in publish_analysis['source_distribution'].items():
percentage = (count / publish_analysis['total_publishes']) * 100
report += f" {source}: {count} 篇 ({percentage:.1f}%)\n"
report += f"\n 标题长度统计:\n"
report += f" 平均: {publish_analysis['title_length_stats']['avg']:.1f} 字符\n"
report += f" 最短: {publish_analysis['title_length_stats']['min']} 字符\n"
report += f" 最长: {publish_analysis['title_length_stats']['max']} 字符\n\n"
else:
report += "1. 📱 发布模式分析: 无数据\n\n"
# 2. Cookie健康分析
cookie_analysis = self.analyze_cookie_health(days)
if cookie_analysis:
report += "2. 🔐 Cookie健康分析\n"
report += f" 总检查次数: {cookie_analysis['total_checks']} 次\n"
report += f" 有效检查: {cookie_analysis['valid_checks']} 次\n"
report += f" 成功率: {cookie_analysis['success_rate']:.1f}%\n"
report += f" 健康状态: {cookie_analysis['health_status']}\n"
report += f" 建议: {cookie_analysis['recommendation']}\n\n"
# 显示最近7天数据
recent_days = min(7, len(cookie_analysis['daily_stats']))
if recent_days > 0:
report += f" 最近{recent_days}天成功率:\n"
for i in range(recent_days):
daily = cookie_analysis['daily_stats'][i]
report += f" {daily['date']}: {daily['rate']:.1f}% ({daily['valid']}/{daily['total']})\n"
report += "\n"
else:
report += "2. 🔐 Cookie健康分析: 无数据\n\n"
# 3. A/B测试分析
ab_test_analysis = self.analyze_ab_test_results(days)
if ab_test_analysis:
report += "3. 🎯 A/B测试分析\n"
report += f" 总测试数量: {ab_test_analysis['total_tests']} 次\n\n"
report += " 风格表现排名:\n"
for i, style in enumerate(ab_test_analysis['style_performance'], 1):
report += f" {i}. {style['style']}: {style['avg_score']:.2f} 分 (测试{style['test_count']}次)\n"
report += "\n"
report += " 最佳表现文章:\n"
for performer in ab_test_analysis['top_performers'][:3]:
report += f" 📝 {performer['title']}\n"
report += f" 风格: {performer['style']}, 分数: {performer['score']:.2f}\n"
report += f" 互动: 👍{performer['likes']} ⭐{performer['collects']} 💬{performer['comments']} 📤{performer['shares']}\n"
report += "\n"
report += " 优化建议:\n"
for rec in ab_test_analysis['recommendations']:
report += f" {rec['type']}: {rec['style']}\n"
report += f" 理由: {rec['reason']}\n"
if 'confidence' in rec:
report += f" 置信度: {rec['confidence']:.1%}\n"
if 'suggestion' in rec:
report += f" 建议: {rec['suggestion']}\n"
report += "\n"
if 'engagement_insight' in ab_test_analysis:
insight = ab_test_analysis['engagement_insight']
report += " 互动洞察:\n"
report += f" 最多互动类型: {insight['most_common']} ({insight['percentage']:.1f}%)\n"
report += f" 点赞占比: {insight['distribution']['likes']*100:.1f}%\n"
report += f" 收藏占比: {insight['distribution']['collects']*100:.1f}%\n"
report += f" 评论占比: {insight['distribution']['comments']*100:.1f}%\n"
report += f" 分享占比: {insight['distribution']['shares']*100:.1f}%\n\n"
else:
report += "3. 🎯 A/B测试分析: 无数据\n\n"
# 4. 系统建议
report += "4. 💡 系统优化建议\n"
suggestions = []
# 基于发布模式建议
if publish_analysis:
if publish_analysis['avg_publishes_per_day'] < 0.5:
suggestions.append("增加发布频率,建议每天至少发布1篇")
# 检查来源多样性
source_count = len(publish_analysis['source_distribution'])
if source_count < 3:
suggestions.append(f"增加文章来源多样性,当前只有{source_count}个来源")
# 基于Cookie健康建议
if cookie_analysis:
if cookie_analysis['success_rate'] < 80:
suggestions.append("Cookie成功率较低,建议检查更新")
# 基于A/B测试建议
if ab_test_analysis:
if ab_test_analysis['total_tests'] < 10:
suggestions.append("A/B测试数据不足,建议继续收集数据")
if suggestions:
for i, suggestion in enumerate(suggestions, 1):
report += f" {i}. {suggestion}\n"
else:
report += " 系统运行良好,暂无优化建议\n"
report += "\n" + "=" * 60 + "\n"
report += "📁 相关文件:\n"
report += f" 发布记录: {self.publish_db}/\n"
report += f" 监控数据库: {self.monitor_db}\n"
report += f" A/B测试数据库: {self.ab_test_db}\n"
report += f" 分析输出: {self.output_dir}/\n"
report += "\n⚡ 快速命令:\n"
report += " 查看报告: python3 data_analyzer.py report --days 30\n"
report += " 分析发布: python3 data_analyzer.py publish --days 30\n"
report += " 分析Cookie: python3 data_analyzer.py cookie --days 30\n"
report += " 分析A/B测试: python3 data_analyzer.py abtest --days 30\n"
# 保存报告到文件
report_file = os.path.join(self.output_dir, f'comprehensive_report_{days}d_{datetime.now().strftime("%Y%m%d_%H%M%S")}.txt')
with open(report_file, 'w', encoding='utf-8') as f:
f.write(report)
print(f"✅ 综合分析报告已保存: {report_file}")
return report
def main():
"""主函数"""
import argparse
import time
parser = argparse.ArgumentParser(description='小红书数据分析器')
subparsers = parser.add_subparsers(dest='command', help='命令')
# report命令
report_parser = subparsers.add_parser('report', help='生成综合分析报告')
report_parser.add_argument('--days', type=int, default=30, help='分析天数')
# publish命令
publish_parser = subparsers.add_parser('publish', help='分析发布模式')
publish_parser.add_argument('--days', type=int, default=30, help='分析天数')
# cookie命令
cookie_parser = subparsers.add_parser('cookie', help='分析Cookie健康')
cookie_parser.add_argument('--days', type=int, default=30, help='分析天数')
# abtest命令
abtest_parser = subparsers.add_parser('abtest', help='分析A/B测试')
abtest_parser.add_argument('--days', type=int, default=30, help='分析天数')
args = parser.parse_args()
analyzer = XiaohongshuDataAnalyzer()
if args.command == 'report':
report = analyzer.generate_comprehensive_report(args.days)
print(report)
elif args.command == 'publish':
analysis = analyzer.analyze_publish_patterns(args.days)
if analysis:
print(json.dumps(analysis, indent=2, ensure_ascii=False))
else:
print(f"最近{args.days}天无发布数据")
elif args.command == 'cookie':
analysis = analyzer.analyze_cookie_health(args.days)
if analysis:
print(json.dumps(analysis, indent=2, ensure_ascii=False))
else:
print(f"最近{args.days}天无Cookie数据")
elif args.command == 'abtest':
analysis = analyzer.analyze_ab_test_results(args.days)
if analysis:
print(json.dumps(analysis, indent=2, ensure_ascii=False))
else:
print(f"最近{args.days}天无A/B测试数据")
else:
parser.print_help()
if __name__ == "__main__":
main()