开发思路

  1. 配置 (# --- 配置 ---):
    • REVIEWS_FILE_PATH: 指定包含用户评论的文本文件路径。每行应为一条独立评论。
    • STOPWORDS_FILE_PATH: (可选) 指定停用词文件路径。停用词是无实际意义或过于常见的词(如“的”、“是”、“在”),在分析中通常会被过滤掉。如果文件不存在,则不进行过滤。
    • REPORT_PREFIX: 生成的报告和图表文件名的前缀。
  2. 辅助函数:
    • load_stopwords: 从文件加载停用词到一个集合中。
    • preprocess_text: 对单条评论进行分词 (jieba.lcut) 并移除停用词。
  3. 分析函数:
    • analyze_sentiment:
      • 遍历所有评论,使用 SnowNLP 计算每条评论的情感得分 (0到1之间,越接近1越积极)。
      • 根据得分将评论分为积极 (>0.6)、中性 (0.4-0.6)、消极 (<0.4) 三类。
      • 打印各类别数量,并使用 matplotlib 绘制情感分布的饼图。
    • extract_keywords:
      • 对所有评论进行预处理(分词、去停用词)。
      • 使用 collections.Counter 统计所有词语的出现频率。
      • 提取出现频率最高的N个词作为关键词。
      • 使用 pandas 打印关键词列表,并用 matplotlib 绘制柱状图展示。
    • identify_issues_and_needs:
      • 潜在问题: 筛选出消极评论 (sentiments < 0.4),对这些评论提取关键词。为了更聚焦,会尝试过滤掉一些常见的、不具体的负面词(如“差”、“不好”),以找出更具体的“物流慢”、“质量差”等问题。
      • 用户需求: 筛选出积极评论 (sentiments > 0.6),同样提取关键词,并过滤常见正面词,以发现用户喜欢的“物流快”、“质量好”、“款式新”等特性。
      • 使用 pandas 打印识别出的问题和需求列表。
  4. 报告生成 (generate_report):
    • 将所有分析结果(情感统计、关键词、潜在问题、用户需求)以及对应的图表路径汇总到一个 .txt 文件中。
  5. 主函数 (main):
    • 负责协调整个流程:加载评论数据、加载停用词、调用分析函数、生成报告。
    • 如果找不到评论文件,会自动创建一个包含示例评论的文件用于演示。
import jieba
from snownlp import SnowNLP
import matplotlib.pyplot as plt
import pandas as pd
from collections import Counter
import os

# --- 配置 ---
# 评论数据文件路径
REVIEWS_FILE_PATH = 'reviews.txt'
# 停用词文件路径 (可选,用于过滤无意义词汇)
STOPWORDS_FILE_PATH = 'stopwords.txt'
# 生成报告的文件名前缀
REPORT_PREFIX = '用户反馈报告'

# --- 辅助函数 ---

def load_stopwords(filepath):
    """加载停用词列表"""
    stopwords = set()
    if os.path.exists(filepath):
        with open(filepath, 'r', encoding='utf-8') as f:
            for line in f:
                stopwords.add(line.strip())
        print(f"已加载停用词文件: {filepath}")
    else:
        print(f"未找到停用词文件: {filepath},将不使用停用词过滤。")
    return stopwords

def preprocess_text(text, stopwords):
    """文本预处理:分词并去除停用词"""
    # 使用精确模式分词
    words = jieba.lcut(text)
    # 去除停用词和空字符串
    filtered_words = [word for word in words if word.strip() and word not in stopwords]
    return filtered_words

# --- 分析函数 ---

def analyze_sentiment(reviews):
    """分析评论情感"""
    print("--- 开始情感分析 ---")
    sentiments = []
    for i, review in enumerate(reviews):
        # 使用SnowNLP进行情感分析,score越接近1表示越积极
        s = SnowNLP(review)
        score = s.sentiments
        sentiments.append(score)
        # print(f"评论 {i+1}: {review[:30]}... -> 情感得分: {score:.4f}") # 可选:打印每条评论得分
    
    # 分类情感
    positive_count = sum(1 for s in sentiments if s > 0.6)
    neutral_count = sum(1 for s in sentiments if 0.4 <= s <= 0.6)
    negative_count = sum(1 for s in sentiments if s < .4)
    
    sentiment_labels = ['积极', '中性', '消极']
    sentiment_counts = [positive_count, neutral_count, negative_count]
    
    print(f"积极评论: {positive_count}")
    print(f"中性评论: {neutral_count}")
    print(f"消极评论: {negative_count}")
    
    # 绘制情感分布饼图
    plt.figure(figsize=(8, 8))
    colors = ['green', 'gold', 'red']
    plt.pie(sentiment_counts, labels=sentiment_labels, autopct='%1.1f%%', startangle=140, colors=colors)
    plt.title('用户评论情感分布')
    sentiment_chart_path = f'{REPORT_PREFIX}_情感分布.png'
    plt.savefig(sentiment_chart_path)
    plt.close()
    print(f"情感分布图表已保存至: {sentiment_chart_path}")
    
    return sentiments, sentiment_chart_path

def extract_keywords(reviews, stopwords, top_n=20):
    """提取评论中的关键词"""
    print("\n--- 开始提取关键词 ---")
    all_words = []
    for review in reviews:
        words = preprocess_text(review, stopwords)
        all_words.extend(words)
    
    # 统计词频
    word_freq = Counter(all_words)
    # 获取最常见的N个词
    most_common_words = word_freq.most_common(top_n)
    
    if not most_common_words:
        print("未提取到关键词。")
        return [], ""
        
    df_keywords = pd.DataFrame(most_common_words, columns=['关键词', '频率'])
    print("高频关键词:")
    print(df_keywords.to_string(index=False))
    
    # 绘制关键词词云图(简化为柱状图)
    plt.figure(figsize=(12, 8))
    words, counts = zip(*most_common_words) # 解压元组列表
    bars = plt.bar(range(len(words)), counts, color='skyblue')
    plt.xlabel('关键词')
    plt.ylabel('出现频率')
    plt.title(f'TOP {top_n} 高频关键词')
    plt.xticks(range(len(words)), words, rotation=45, ha='right')
    
    # 在柱状图上添加数值标签
    for bar in bars:
        yval = bar.get_height()
        plt.text(bar.get_x() + bar.get_width()/2.0, yval, int(yval), ha='center', va='bottom')

    plt.tight_layout()
    keywords_chart_path = f'{REPORT_PREFIX}_高频关键词.png'
    plt.savefig(keywords_chart_path)
    plt.close()
    print(f"高频关键词图表已保存至: {keywords_chart_path}")
    
    return df_keywords, keywords_chart_path

def identify_issues_and_needs(reviews, sentiments, stopwords):
    """根据情感和关键词识别潜在问题与需求"""
    print("\n--- 识别潜在问题与需求 ---")
    
    # --- 识别潜在问题 (分析消极评论中的关键词) ---
    negative_reviews = [reviews[i] for i, s in enumerate(sentiments) if s < 0.4]
    print(f"分析 {len(negative_reviews)} 条消极评论以识别潜在问题...")
    
    negative_words = []
    for review in negative_reviews:
        words = preprocess_text(review, stopwords)
        negative_words.extend(words)
        
    neg_word_freq = Counter(negative_words)
    # 过滤掉一些通用的负面词,聚焦于具体问题
    # 这里可以加入更复杂的逻辑,比如结合正面词对比
    common_neg_words = {'差', '不好', '慢', '贵', '问题', '失望', '垃圾'} # 示例通用负面词
    potential_issues = [(word, freq) for word, freq in neg_word_freq.most_common(20) if word not in common_neg_words and len(word) > 1]
    
    df_issues = pd.DataFrame(potential_issues, columns=['潜在问题关键词', '频率'])
    print("潜在问题关键词 (基于消极评论):")
    print(df_issues.to_string(index=False))
    
    # --- 识别用户需求 (分析积极评论中的关键词) ---
    positive_reviews = [reviews[i] for i, s in enumerate(sentiments) if s > 0.6]
    print(f"\n分析 {len(positive_reviews)} 条积极评论以识别用户需求/喜好...")
    
    positive_words = []
    for review in positive_reviews:
        words = preprocess_text(review, stopwords)
        positive_words.extend(words)
        
    pos_word_freq = Counter(positive_words)
     # 过滤掉一些通用的正面词,聚焦于具体需求/喜好
    common_pos_words = {'好', '棒', '喜欢', '不错', '推荐', '满意', '快'} # 示例通用正面词
    user_needs = [(word, freq) for word, freq in pos_word_freq.most_common(20) if word not in common_pos_words and len(word) > 1]
    
    df_needs = pd.DataFrame(user_needs, columns=['用户需求/喜好关键词', '频率'])
    print("用户需求/喜好关键词 (基于积极评论):")
    print(df_needs.to_string(index=False))
    
    return df_issues, df_needs

def generate_report(sentiments, sentiment_chart, df_keywords, keywords_chart, df_issues, df_needs):
    """生成最终的文本报告"""
    from datetime import datetime
    report_filename = f"{REPORT_PREFIX}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt"
    
    with open(report_filename, 'w', encoding='utf-8') as f:
        f.write("=" * 40 + "\n")
        f.write("        电商平台用户反馈分析报告\n")
        f.write(f"        生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
        f.write("=" * 40 + "\n\n")

        f.write("--- 1. 整体情感分析 ---\n")
        positive_count = sum(1 for s in sentiments if s > 0.6)
        neutral_count = sum(1 for s in sentiments if 0.4 <= s <= 0.6)
        negative_count = sum(1 for s in sentiments if s < 0.4)
        total_count = len(sentiments)
        
        f.write(f"总评论数: {total_count}\n")
        f.write(f"积极评论: {positive_count} ({positive_count/total_count*100:.1f}%)\n")
        f.write(f"中性评论: {neutral_count} ({neutral_count/total_count*100:.1f}%)\n")
        f.write(f"消极评论: {negative_count} ({negative_count/total_count*100:.1f}%)\n")
        f.write(f"图表: {sentiment_chart}\n\n")

        f.write("--- 2. 关键词分析 ---\n")
        if df_keywords is not None and not df_keywords.empty:
            f.write(df_keywords.to_string(index=False))
            f.write(f"\n图表: {keywords_chart}\n\n")
        else:
            f.write("无关键词数据。\n\n")

        f.write("--- 3. 潜在问题识别 (基于消极评论) ---\n")
        if df_issues is not None and not df_issues.empty:
            f.write(df_issues.to_string(index=False))
            f.write("\n建议:关注上述高频负面关键词,深入调查具体原因。\n\n")
        else:
            f.write("未识别出显著的潜在问题关键词。\n\n")

        f.write("--- 4. 用户需求与喜好 (基于积极评论) ---\n")
        if df_needs is not None and not df_needs.empty:
            f.write(df_needs.to_string(index=False))
            f.write("\n建议:考虑加强或扩展与上述关键词相关的功能或产品。\n\n")
        else:
            f.write("未识别出显著的用户需求关键词。\n\n")

        f.write("=" * 40 + "\n")
        f.write("              报告结束\n")
        f.write("=" * 40 + "\n")

    print(f"\n分析报告已生成: {report_filename}")

# --- 主函数 ---

def main():
    """主函数"""
    # 1. 加载数据
    if not os.path.exists(REVIEWS_FILE_PATH):
        print(f"错误: 未找到评论文件 {REVIEWS_FILE_PATH}")
        # 创建一个示例文件用于演示
        sample_reviews = [
            "这个产品质量很好,我很喜欢,会推荐给朋友。",
            "物流速度非常快,包装也很仔细,五星好评!",
            "价格有点贵,但是东西还不错。",
            "客服态度很好,解决问题很及时。",
            "商品和描述不符,有点失望。",
            "物流太慢了,等了好久才收到。",
            "质量很差,用了一次就坏了,非常不满意。",
            "款式很新颖,穿着很舒服,赞一个!",
            "发货速度一般,希望可以更快一点。",
            "性价比很高,物超所值,下次还会再买。"
        ]
        with open(REVIEWS_FILE_PATH, 'w', encoding='utf-8') as f:
            for review in sample_reviews:
                f.write(review + '\n')
        print(f"已创建示例评论文件: {REVIEWS_FILE_PATH}")
        
    
    reviews = []
    try:
        with open(REVIEWS_FILE_PATH, 'r', encoding='utf-8') as f:
            for line in f:
                review = line.strip()
                if review: # 忽略空行
                    reviews.append(review)
        print(f"成功加载 {len(reviews)} 条评论。")
    except Exception as e:
        print(f"读取评论文件时出错: {e}")
        return

    if not reviews:
        print("评论文件为空或读取失败。")
        return

    # 2. 加载停用词
    stopwords = load_stopwords(STOPWORDS_FILE_PATH)

    # 3. 执行分析
    sentiments, sentiment_chart = analyze_sentiment(reviews)
    df_keywords, keywords_chart = extract_keywords(reviews, stopwords)
    df_issues, df_needs = identify_issues_and_needs(reviews, sentiments, stopwords)

    # 4. 生成报告
    generate_report(sentiments, sentiment_chart, df_keywords, keywords_chart, df_issues, df_needs)

    print("\n分析完成。")

if __name__ == "__main__":
    main()