{"id":3887,"date":"2025-09-13T08:50:16","date_gmt":"2025-09-13T00:50:16","guid":{"rendered":"http:\/\/viplao.com\/?p=3887"},"modified":"2025-09-13T08:50:19","modified_gmt":"2025-09-13T00:50:19","slug":"%e3%80%90python%e5%ae%9e%e8%b7%b5%e6%a1%88%e4%be%8b%e3%80%91%e7%94%b5%e5%95%86%e5%b9%b3%e5%8f%b0%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e5%92%8c%e6%8c%96%e6%8e%98-%e7%94%a8%e6%88%b7%e8%af%84%e8%ae%ba","status":"publish","type":"post","link":"http:\/\/viplao.com\/index.php\/2025\/09\/13\/%e3%80%90python%e5%ae%9e%e8%b7%b5%e6%a1%88%e4%be%8b%e3%80%91%e7%94%b5%e5%95%86%e5%b9%b3%e5%8f%b0%e6%95%b0%e6%8d%ae%e5%88%86%e6%9e%90%e5%92%8c%e6%8c%96%e6%8e%98-%e7%94%a8%e6%88%b7%e8%af%84%e8%ae%ba\/","title":{"rendered":"\u3010Python\u5b9e\u8df5\u6848\u4f8b\u3011\u7535\u5546\u5e73\u53f0\u6570\u636e\u5206\u6790\u548c\u6316\u6398 &#8211; \u7528\u6237\u8bc4\u8bba\u60c5\u611f\uff0c\u5e76\u751f\u6210\u7528\u6237\u53cd\u9988\u62a5\u544a"},"content":{"rendered":"\n<p><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u5f00\u53d1\u601d\u8def<\/h3>\n\n\n\n<ol>\n<li><strong>\u914d\u7f6e (<code># --- \u914d\u7f6e ---<\/code>)<\/strong>:\n<ul>\n<li><code>REVIEWS_FILE_PATH<\/code>: \u6307\u5b9a\u5305\u542b\u7528\u6237\u8bc4\u8bba\u7684\u6587\u672c\u6587\u4ef6\u8def\u5f84\u3002\u6bcf\u884c\u5e94\u4e3a\u4e00\u6761\u72ec\u7acb\u8bc4\u8bba\u3002<\/li>\n\n\n\n<li><code>STOPWORDS_FILE_PATH<\/code>: (\u53ef\u9009) \u6307\u5b9a\u505c\u7528\u8bcd\u6587\u4ef6\u8def\u5f84\u3002\u505c\u7528\u8bcd\u662f\u65e0\u5b9e\u9645\u610f\u4e49\u6216\u8fc7\u4e8e\u5e38\u89c1\u7684\u8bcd\uff08\u5982\u201c\u7684\u201d\u3001\u201c\u662f\u201d\u3001\u201c\u5728\u201d\uff09\uff0c\u5728\u5206\u6790\u4e2d\u901a\u5e38\u4f1a\u88ab\u8fc7\u6ee4\u6389\u3002\u5982\u679c\u6587\u4ef6\u4e0d\u5b58\u5728\uff0c\u5219\u4e0d\u8fdb\u884c\u8fc7\u6ee4\u3002<\/li>\n\n\n\n<li><code>REPORT_PREFIX<\/code>: \u751f\u6210\u7684\u62a5\u544a\u548c\u56fe\u8868\u6587\u4ef6\u540d\u7684\u524d\u7f00\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u8f85\u52a9\u51fd\u6570<\/strong>:\n<ul>\n<li><code>load_stopwords<\/code>: \u4ece\u6587\u4ef6\u52a0\u8f7d\u505c\u7528\u8bcd\u5230\u4e00\u4e2a\u96c6\u5408\u4e2d\u3002<\/li>\n\n\n\n<li><code>preprocess_text<\/code>: \u5bf9\u5355\u6761\u8bc4\u8bba\u8fdb\u884c\u5206\u8bcd (<code>jieba.lcut<\/code>) \u5e76\u79fb\u9664\u505c\u7528\u8bcd\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u5206\u6790\u51fd\u6570<\/strong>:\n<ul>\n<li><code>analyze_sentiment<\/code>:\n<ul>\n<li>\u904d\u5386\u6240\u6709\u8bc4\u8bba\uff0c\u4f7f\u7528&nbsp;<code>SnowNLP<\/code>&nbsp;\u8ba1\u7b97\u6bcf\u6761\u8bc4\u8bba\u7684\u60c5\u611f\u5f97\u5206 (0\u52301\u4e4b\u95f4\uff0c\u8d8a\u63a5\u8fd11\u8d8a\u79ef\u6781)\u3002<\/li>\n\n\n\n<li>\u6839\u636e\u5f97\u5206\u5c06\u8bc4\u8bba\u5206\u4e3a\u79ef\u6781 (&gt;0.6)\u3001\u4e2d\u6027 (0.4-0.6)\u3001\u6d88\u6781 (&lt;0.4) \u4e09\u7c7b\u3002<\/li>\n\n\n\n<li>\u6253\u5370\u5404\u7c7b\u522b\u6570\u91cf\uff0c\u5e76\u4f7f\u7528&nbsp;<code>matplotlib<\/code>&nbsp;\u7ed8\u5236\u60c5\u611f\u5206\u5e03\u7684\u997c\u56fe\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>extract_keywords<\/code>:\n<ul>\n<li>\u5bf9\u6240\u6709\u8bc4\u8bba\u8fdb\u884c\u9884\u5904\u7406\uff08\u5206\u8bcd\u3001\u53bb\u505c\u7528\u8bcd\uff09\u3002<\/li>\n\n\n\n<li>\u4f7f\u7528&nbsp;<code>collections.Counter<\/code>&nbsp;\u7edf\u8ba1\u6240\u6709\u8bcd\u8bed\u7684\u51fa\u73b0\u9891\u7387\u3002<\/li>\n\n\n\n<li>\u63d0\u53d6\u51fa\u73b0\u9891\u7387\u6700\u9ad8\u7684N\u4e2a\u8bcd\u4f5c\u4e3a\u5173\u952e\u8bcd\u3002<\/li>\n\n\n\n<li>\u4f7f\u7528&nbsp;<code>pandas<\/code>&nbsp;\u6253\u5370\u5173\u952e\u8bcd\u5217\u8868\uff0c\u5e76\u7528&nbsp;<code>matplotlib<\/code>&nbsp;\u7ed8\u5236\u67f1\u72b6\u56fe\u5c55\u793a\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>identify_issues_and_needs<\/code>:\n<ul>\n<li><strong>\u6f5c\u5728\u95ee\u9898<\/strong>: \u7b5b\u9009\u51fa\u6d88\u6781\u8bc4\u8bba (<code>sentiments &lt; 0.4<\/code>)\uff0c\u5bf9\u8fd9\u4e9b\u8bc4\u8bba\u63d0\u53d6\u5173\u952e\u8bcd\u3002\u4e3a\u4e86\u66f4\u805a\u7126\uff0c\u4f1a\u5c1d\u8bd5\u8fc7\u6ee4\u6389\u4e00\u4e9b\u5e38\u89c1\u7684\u3001\u4e0d\u5177\u4f53\u7684\u8d1f\u9762\u8bcd\uff08\u5982\u201c\u5dee\u201d\u3001\u201c\u4e0d\u597d\u201d\uff09\uff0c\u4ee5\u627e\u51fa\u66f4\u5177\u4f53\u7684\u201c\u7269\u6d41\u6162\u201d\u3001\u201c\u8d28\u91cf\u5dee\u201d\u7b49\u95ee\u9898\u3002<\/li>\n\n\n\n<li><strong>\u7528\u6237\u9700\u6c42<\/strong>: \u7b5b\u9009\u51fa\u79ef\u6781\u8bc4\u8bba (<code>sentiments &gt; 0.6<\/code>)\uff0c\u540c\u6837\u63d0\u53d6\u5173\u952e\u8bcd\uff0c\u5e76\u8fc7\u6ee4\u5e38\u89c1\u6b63\u9762\u8bcd\uff0c\u4ee5\u53d1\u73b0\u7528\u6237\u559c\u6b22\u7684\u201c\u7269\u6d41\u5feb\u201d\u3001\u201c\u8d28\u91cf\u597d\u201d\u3001\u201c\u6b3e\u5f0f\u65b0\u201d\u7b49\u7279\u6027\u3002<\/li>\n\n\n\n<li>\u4f7f\u7528&nbsp;<code>pandas<\/code>&nbsp;\u6253\u5370\u8bc6\u522b\u51fa\u7684\u95ee\u9898\u548c\u9700\u6c42\u5217\u8868\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u62a5\u544a\u751f\u6210 (<code>generate_report<\/code>)<\/strong>:\n<ul>\n<li>\u5c06\u6240\u6709\u5206\u6790\u7ed3\u679c\uff08\u60c5\u611f\u7edf\u8ba1\u3001\u5173\u952e\u8bcd\u3001\u6f5c\u5728\u95ee\u9898\u3001\u7528\u6237\u9700\u6c42\uff09\u4ee5\u53ca\u5bf9\u5e94\u7684\u56fe\u8868\u8def\u5f84\u6c47\u603b\u5230\u4e00\u4e2a&nbsp;<code>.txt<\/code>&nbsp;\u6587\u4ef6\u4e2d\u3002<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u4e3b\u51fd\u6570 (<code>main<\/code>)<\/strong>:\n<ul>\n<li>\u8d1f\u8d23\u534f\u8c03\u6574\u4e2a\u6d41\u7a0b\uff1a\u52a0\u8f7d\u8bc4\u8bba\u6570\u636e\u3001\u52a0\u8f7d\u505c\u7528\u8bcd\u3001\u8c03\u7528\u5206\u6790\u51fd\u6570\u3001\u751f\u6210\u62a5\u544a\u3002<\/li>\n\n\n\n<li>\u5982\u679c\u627e\u4e0d\u5230\u8bc4\u8bba\u6587\u4ef6\uff0c\u4f1a\u81ea\u52a8\u521b\u5efa\u4e00\u4e2a\u5305\u542b\u793a\u4f8b\u8bc4\u8bba\u7684\u6587\u4ef6\u7528\u4e8e\u6f14\u793a\u3002<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code>import jieba\nfrom snownlp import SnowNLP\nimport matplotlib.pyplot as plt\nimport pandas as pd\nfrom collections import Counter\nimport os\n\n# --- \u914d\u7f6e ---\n# \u8bc4\u8bba\u6570\u636e\u6587\u4ef6\u8def\u5f84\nREVIEWS_FILE_PATH = 'reviews.txt'\n# \u505c\u7528\u8bcd\u6587\u4ef6\u8def\u5f84 (\u53ef\u9009\uff0c\u7528\u4e8e\u8fc7\u6ee4\u65e0\u610f\u4e49\u8bcd\u6c47)\nSTOPWORDS_FILE_PATH = 'stopwords.txt'\n# \u751f\u6210\u62a5\u544a\u7684\u6587\u4ef6\u540d\u524d\u7f00\nREPORT_PREFIX = '\u7528\u6237\u53cd\u9988\u62a5\u544a'\n\n# --- \u8f85\u52a9\u51fd\u6570 ---\n\ndef load_stopwords(filepath):\n    \"\"\"\u52a0\u8f7d\u505c\u7528\u8bcd\u5217\u8868\"\"\"\n    stopwords = set()\n    if os.path.exists(filepath):\n        with open(filepath, 'r', encoding='utf-8') as f:\n            for line in f:\n                stopwords.add(line.strip())\n        print(f\"\u5df2\u52a0\u8f7d\u505c\u7528\u8bcd\u6587\u4ef6: {filepath}\")\n    else:\n        print(f\"\u672a\u627e\u5230\u505c\u7528\u8bcd\u6587\u4ef6: {filepath}\uff0c\u5c06\u4e0d\u4f7f\u7528\u505c\u7528\u8bcd\u8fc7\u6ee4\u3002\")\n    return stopwords\n\ndef preprocess_text(text, stopwords):\n    \"\"\"\u6587\u672c\u9884\u5904\u7406\uff1a\u5206\u8bcd\u5e76\u53bb\u9664\u505c\u7528\u8bcd\"\"\"\n    # \u4f7f\u7528\u7cbe\u786e\u6a21\u5f0f\u5206\u8bcd\n    words = jieba.lcut(text)\n    # \u53bb\u9664\u505c\u7528\u8bcd\u548c\u7a7a\u5b57\u7b26\u4e32\n    filtered_words = &#91;word for word in words if word.strip() and word not in stopwords]\n    return filtered_words\n\n# --- \u5206\u6790\u51fd\u6570 ---\n\ndef analyze_sentiment(reviews):\n    \"\"\"\u5206\u6790\u8bc4\u8bba\u60c5\u611f\"\"\"\n    print(\"--- \u5f00\u59cb\u60c5\u611f\u5206\u6790 ---\")\n    sentiments = &#91;]\n    for i, review in enumerate(reviews):\n        # \u4f7f\u7528SnowNLP\u8fdb\u884c\u60c5\u611f\u5206\u6790\uff0cscore\u8d8a\u63a5\u8fd11\u8868\u793a\u8d8a\u79ef\u6781\n        s = SnowNLP(review)\n        score = s.sentiments\n        sentiments.append(score)\n        # print(f\"\u8bc4\u8bba {i+1}: {review&#91;:30]}... -&gt; \u60c5\u611f\u5f97\u5206: {score:.4f}\") # \u53ef\u9009\uff1a\u6253\u5370\u6bcf\u6761\u8bc4\u8bba\u5f97\u5206\n    \n    # \u5206\u7c7b\u60c5\u611f\n    positive_count = sum(1 for s in sentiments if s &gt; 0.6)\n    neutral_count = sum(1 for s in sentiments if 0.4 &lt;= s &lt;= 0.6)\n    negative_count = sum(1 for s in sentiments if s &lt; .4)\n    \n    sentiment_labels = &#91;'\u79ef\u6781', '\u4e2d\u6027', '\u6d88\u6781']\n    sentiment_counts = &#91;positive_count, neutral_count, negative_count]\n    \n    print(f\"\u79ef\u6781\u8bc4\u8bba: {positive_count}\")\n    print(f\"\u4e2d\u6027\u8bc4\u8bba: {neutral_count}\")\n    print(f\"\u6d88\u6781\u8bc4\u8bba: {negative_count}\")\n    \n    # \u7ed8\u5236\u60c5\u611f\u5206\u5e03\u997c\u56fe\n    plt.figure(figsize=(8, 8))\n    colors = &#91;'green', 'gold', 'red']\n    plt.pie(sentiment_counts, labels=sentiment_labels, autopct='%1.1f%%', startangle=140, colors=colors)\n    plt.title('\u7528\u6237\u8bc4\u8bba\u60c5\u611f\u5206\u5e03')\n    sentiment_chart_path = f'{REPORT_PREFIX}_\u60c5\u611f\u5206\u5e03.png'\n    plt.savefig(sentiment_chart_path)\n    plt.close()\n    print(f\"\u60c5\u611f\u5206\u5e03\u56fe\u8868\u5df2\u4fdd\u5b58\u81f3: {sentiment_chart_path}\")\n    \n    return sentiments, sentiment_chart_path\n\ndef extract_keywords(reviews, stopwords, top_n=20):\n    \"\"\"\u63d0\u53d6\u8bc4\u8bba\u4e2d\u7684\u5173\u952e\u8bcd\"\"\"\n    print(\"\\n--- \u5f00\u59cb\u63d0\u53d6\u5173\u952e\u8bcd ---\")\n    all_words = &#91;]\n    for review in reviews:\n        words = preprocess_text(review, stopwords)\n        all_words.extend(words)\n    \n    # \u7edf\u8ba1\u8bcd\u9891\n    word_freq = Counter(all_words)\n    # \u83b7\u53d6\u6700\u5e38\u89c1\u7684N\u4e2a\u8bcd\n    most_common_words = word_freq.most_common(top_n)\n    \n    if not most_common_words:\n        print(\"\u672a\u63d0\u53d6\u5230\u5173\u952e\u8bcd\u3002\")\n        return &#91;], \"\"\n        \n    df_keywords = pd.DataFrame(most_common_words, columns=&#91;'\u5173\u952e\u8bcd', '\u9891\u7387'])\n    print(\"\u9ad8\u9891\u5173\u952e\u8bcd:\")\n    print(df_keywords.to_string(index=False))\n    \n    # \u7ed8\u5236\u5173\u952e\u8bcd\u8bcd\u4e91\u56fe\uff08\u7b80\u5316\u4e3a\u67f1\u72b6\u56fe\uff09\n    plt.figure(figsize=(12, 8))\n    words, counts = zip(*most_common_words) # \u89e3\u538b\u5143\u7ec4\u5217\u8868\n    bars = plt.bar(range(len(words)), counts, color='skyblue')\n    plt.xlabel('\u5173\u952e\u8bcd')\n    plt.ylabel('\u51fa\u73b0\u9891\u7387')\n    plt.title(f'TOP {top_n} \u9ad8\u9891\u5173\u952e\u8bcd')\n    plt.xticks(range(len(words)), words, rotation=45, ha='right')\n    \n    # \u5728\u67f1\u72b6\u56fe\u4e0a\u6dfb\u52a0\u6570\u503c\u6807\u7b7e\n    for bar in bars:\n        yval = bar.get_height()\n        plt.text(bar.get_x() + bar.get_width()\/2.0, yval, int(yval), ha='center', va='bottom')\n\n    plt.tight_layout()\n    keywords_chart_path = f'{REPORT_PREFIX}_\u9ad8\u9891\u5173\u952e\u8bcd.png'\n    plt.savefig(keywords_chart_path)\n    plt.close()\n    print(f\"\u9ad8\u9891\u5173\u952e\u8bcd\u56fe\u8868\u5df2\u4fdd\u5b58\u81f3: {keywords_chart_path}\")\n    \n    return df_keywords, keywords_chart_path\n\ndef identify_issues_and_needs(reviews, sentiments, stopwords):\n    \"\"\"\u6839\u636e\u60c5\u611f\u548c\u5173\u952e\u8bcd\u8bc6\u522b\u6f5c\u5728\u95ee\u9898\u4e0e\u9700\u6c42\"\"\"\n    print(\"\\n--- \u8bc6\u522b\u6f5c\u5728\u95ee\u9898\u4e0e\u9700\u6c42 ---\")\n    \n    # --- \u8bc6\u522b\u6f5c\u5728\u95ee\u9898 (\u5206\u6790\u6d88\u6781\u8bc4\u8bba\u4e2d\u7684\u5173\u952e\u8bcd) ---\n    negative_reviews = &#91;reviews&#91;i] for i, s in enumerate(sentiments) if s &lt; 0.4]\n    print(f\"\u5206\u6790 {len(negative_reviews)} \u6761\u6d88\u6781\u8bc4\u8bba\u4ee5\u8bc6\u522b\u6f5c\u5728\u95ee\u9898...\")\n    \n    negative_words = &#91;]\n    for review in negative_reviews:\n        words = preprocess_text(review, stopwords)\n        negative_words.extend(words)\n        \n    neg_word_freq = Counter(negative_words)\n    # \u8fc7\u6ee4\u6389\u4e00\u4e9b\u901a\u7528\u7684\u8d1f\u9762\u8bcd\uff0c\u805a\u7126\u4e8e\u5177\u4f53\u95ee\u9898\n    # \u8fd9\u91cc\u53ef\u4ee5\u52a0\u5165\u66f4\u590d\u6742\u7684\u903b\u8f91\uff0c\u6bd4\u5982\u7ed3\u5408\u6b63\u9762\u8bcd\u5bf9\u6bd4\n    common_neg_words = {'\u5dee', '\u4e0d\u597d', '\u6162', '\u8d35', '\u95ee\u9898', '\u5931\u671b', '\u5783\u573e'} # \u793a\u4f8b\u901a\u7528\u8d1f\u9762\u8bcd\n    potential_issues = &#91;(word, freq) for word, freq in neg_word_freq.most_common(20) if word not in common_neg_words and len(word) &gt; 1]\n    \n    df_issues = pd.DataFrame(potential_issues, columns=&#91;'\u6f5c\u5728\u95ee\u9898\u5173\u952e\u8bcd', '\u9891\u7387'])\n    print(\"\u6f5c\u5728\u95ee\u9898\u5173\u952e\u8bcd (\u57fa\u4e8e\u6d88\u6781\u8bc4\u8bba):\")\n    print(df_issues.to_string(index=False))\n    \n    # --- \u8bc6\u522b\u7528\u6237\u9700\u6c42 (\u5206\u6790\u79ef\u6781\u8bc4\u8bba\u4e2d\u7684\u5173\u952e\u8bcd) ---\n    positive_reviews = &#91;reviews&#91;i] for i, s in enumerate(sentiments) if s &gt; 0.6]\n    print(f\"\\n\u5206\u6790 {len(positive_reviews)} \u6761\u79ef\u6781\u8bc4\u8bba\u4ee5\u8bc6\u522b\u7528\u6237\u9700\u6c42\/\u559c\u597d...\")\n    \n    positive_words = &#91;]\n    for review in positive_reviews:\n        words = preprocess_text(review, stopwords)\n        positive_words.extend(words)\n        \n    pos_word_freq = Counter(positive_words)\n     # \u8fc7\u6ee4\u6389\u4e00\u4e9b\u901a\u7528\u7684\u6b63\u9762\u8bcd\uff0c\u805a\u7126\u4e8e\u5177\u4f53\u9700\u6c42\/\u559c\u597d\n    common_pos_words = {'\u597d', '\u68d2', '\u559c\u6b22', '\u4e0d\u9519', '\u63a8\u8350', '\u6ee1\u610f', '\u5feb'} # \u793a\u4f8b\u901a\u7528\u6b63\u9762\u8bcd\n    user_needs = &#91;(word, freq) for word, freq in pos_word_freq.most_common(20) if word not in common_pos_words and len(word) &gt; 1]\n    \n    df_needs = pd.DataFrame(user_needs, columns=&#91;'\u7528\u6237\u9700\u6c42\/\u559c\u597d\u5173\u952e\u8bcd', '\u9891\u7387'])\n    print(\"\u7528\u6237\u9700\u6c42\/\u559c\u597d\u5173\u952e\u8bcd (\u57fa\u4e8e\u79ef\u6781\u8bc4\u8bba):\")\n    print(df_needs.to_string(index=False))\n    \n    return df_issues, df_needs\n\ndef generate_report(sentiments, sentiment_chart, df_keywords, keywords_chart, df_issues, df_needs):\n    \"\"\"\u751f\u6210\u6700\u7ec8\u7684\u6587\u672c\u62a5\u544a\"\"\"\n    from datetime import datetime\n    report_filename = f\"{REPORT_PREFIX}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.txt\"\n    \n    with open(report_filename, 'w', encoding='utf-8') as f:\n        f.write(\"=\" * 40 + \"\\n\")\n        f.write(\"        \u7535\u5546\u5e73\u53f0\u7528\u6237\u53cd\u9988\u5206\u6790\u62a5\u544a\\n\")\n        f.write(f\"        \u751f\u6210\u65f6\u95f4: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\\n\")\n        f.write(\"=\" * 40 + \"\\n\\n\")\n\n        f.write(\"--- 1. \u6574\u4f53\u60c5\u611f\u5206\u6790 ---\\n\")\n        positive_count = sum(1 for s in sentiments if s &gt; 0.6)\n        neutral_count = sum(1 for s in sentiments if 0.4 &lt;= s &lt;= 0.6)\n        negative_count = sum(1 for s in sentiments if s &lt; 0.4)\n        total_count = len(sentiments)\n        \n        f.write(f\"\u603b\u8bc4\u8bba\u6570: {total_count}\\n\")\n        f.write(f\"\u79ef\u6781\u8bc4\u8bba: {positive_count} ({positive_count\/total_count*100:.1f}%)\\n\")\n        f.write(f\"\u4e2d\u6027\u8bc4\u8bba: {neutral_count} ({neutral_count\/total_count*100:.1f}%)\\n\")\n        f.write(f\"\u6d88\u6781\u8bc4\u8bba: {negative_count} ({negative_count\/total_count*100:.1f}%)\\n\")\n        f.write(f\"\u56fe\u8868: {sentiment_chart}\\n\\n\")\n\n        f.write(\"--- 2. \u5173\u952e\u8bcd\u5206\u6790 ---\\n\")\n        if df_keywords is not None and not df_keywords.empty:\n            f.write(df_keywords.to_string(index=False))\n            f.write(f\"\\n\u56fe\u8868: {keywords_chart}\\n\\n\")\n        else:\n            f.write(\"\u65e0\u5173\u952e\u8bcd\u6570\u636e\u3002\\n\\n\")\n\n        f.write(\"--- 3. \u6f5c\u5728\u95ee\u9898\u8bc6\u522b (\u57fa\u4e8e\u6d88\u6781\u8bc4\u8bba) ---\\n\")\n        if df_issues is not None and not df_issues.empty:\n            f.write(df_issues.to_string(index=False))\n            f.write(\"\\n\u5efa\u8bae\uff1a\u5173\u6ce8\u4e0a\u8ff0\u9ad8\u9891\u8d1f\u9762\u5173\u952e\u8bcd\uff0c\u6df1\u5165\u8c03\u67e5\u5177\u4f53\u539f\u56e0\u3002\\n\\n\")\n        else:\n            f.write(\"\u672a\u8bc6\u522b\u51fa\u663e\u8457\u7684\u6f5c\u5728\u95ee\u9898\u5173\u952e\u8bcd\u3002\\n\\n\")\n\n        f.write(\"--- 4. \u7528\u6237\u9700\u6c42\u4e0e\u559c\u597d (\u57fa\u4e8e\u79ef\u6781\u8bc4\u8bba) ---\\n\")\n        if df_needs is not None and not df_needs.empty:\n            f.write(df_needs.to_string(index=False))\n            f.write(\"\\n\u5efa\u8bae\uff1a\u8003\u8651\u52a0\u5f3a\u6216\u6269\u5c55\u4e0e\u4e0a\u8ff0\u5173\u952e\u8bcd\u76f8\u5173\u7684\u529f\u80fd\u6216\u4ea7\u54c1\u3002\\n\\n\")\n        else:\n            f.write(\"\u672a\u8bc6\u522b\u51fa\u663e\u8457\u7684\u7528\u6237\u9700\u6c42\u5173\u952e\u8bcd\u3002\\n\\n\")\n\n        f.write(\"=\" * 40 + \"\\n\")\n        f.write(\"              \u62a5\u544a\u7ed3\u675f\\n\")\n        f.write(\"=\" * 40 + \"\\n\")\n\n    print(f\"\\n\u5206\u6790\u62a5\u544a\u5df2\u751f\u6210: {report_filename}\")\n\n# --- \u4e3b\u51fd\u6570 ---\n\ndef main():\n    \"\"\"\u4e3b\u51fd\u6570\"\"\"\n    # 1. \u52a0\u8f7d\u6570\u636e\n    if not os.path.exists(REVIEWS_FILE_PATH):\n        print(f\"\u9519\u8bef: \u672a\u627e\u5230\u8bc4\u8bba\u6587\u4ef6 {REVIEWS_FILE_PATH}\")\n        # \u521b\u5efa\u4e00\u4e2a\u793a\u4f8b\u6587\u4ef6\u7528\u4e8e\u6f14\u793a\n        sample_reviews = &#91;\n            \"\u8fd9\u4e2a\u4ea7\u54c1\u8d28\u91cf\u5f88\u597d\uff0c\u6211\u5f88\u559c\u6b22\uff0c\u4f1a\u63a8\u8350\u7ed9\u670b\u53cb\u3002\",\n            \"\u7269\u6d41\u901f\u5ea6\u975e\u5e38\u5feb\uff0c\u5305\u88c5\u4e5f\u5f88\u4ed4\u7ec6\uff0c\u4e94\u661f\u597d\u8bc4\uff01\",\n            \"\u4ef7\u683c\u6709\u70b9\u8d35\uff0c\u4f46\u662f\u4e1c\u897f\u8fd8\u4e0d\u9519\u3002\",\n            \"\u5ba2\u670d\u6001\u5ea6\u5f88\u597d\uff0c\u89e3\u51b3\u95ee\u9898\u5f88\u53ca\u65f6\u3002\",\n            \"\u5546\u54c1\u548c\u63cf\u8ff0\u4e0d\u7b26\uff0c\u6709\u70b9\u5931\u671b\u3002\",\n            \"\u7269\u6d41\u592a\u6162\u4e86\uff0c\u7b49\u4e86\u597d\u4e45\u624d\u6536\u5230\u3002\",\n            \"\u8d28\u91cf\u5f88\u5dee\uff0c\u7528\u4e86\u4e00\u6b21\u5c31\u574f\u4e86\uff0c\u975e\u5e38\u4e0d\u6ee1\u610f\u3002\",\n            \"\u6b3e\u5f0f\u5f88\u65b0\u9896\uff0c\u7a7f\u7740\u5f88\u8212\u670d\uff0c\u8d5e\u4e00\u4e2a\uff01\",\n            \"\u53d1\u8d27\u901f\u5ea6\u4e00\u822c\uff0c\u5e0c\u671b\u53ef\u4ee5\u66f4\u5feb\u4e00\u70b9\u3002\",\n            \"\u6027\u4ef7\u6bd4\u5f88\u9ad8\uff0c\u7269\u8d85\u6240\u503c\uff0c\u4e0b\u6b21\u8fd8\u4f1a\u518d\u4e70\u3002\"\n        ]\n        with open(REVIEWS_FILE_PATH, 'w', encoding='utf-8') as f:\n            for review in sample_reviews:\n                f.write(review + '\\n')\n        print(f\"\u5df2\u521b\u5efa\u793a\u4f8b\u8bc4\u8bba\u6587\u4ef6: {REVIEWS_FILE_PATH}\")\n        \n    \n    reviews = &#91;]\n    try:\n        with open(REVIEWS_FILE_PATH, 'r', encoding='utf-8') as f:\n            for line in f:\n                review = line.strip()\n                if review: # \u5ffd\u7565\u7a7a\u884c\n                    reviews.append(review)\n        print(f\"\u6210\u529f\u52a0\u8f7d {len(reviews)} \u6761\u8bc4\u8bba\u3002\")\n    except Exception as e:\n        print(f\"\u8bfb\u53d6\u8bc4\u8bba\u6587\u4ef6\u65f6\u51fa\u9519: {e}\")\n        return\n\n    if not reviews:\n        print(\"\u8bc4\u8bba\u6587\u4ef6\u4e3a\u7a7a\u6216\u8bfb\u53d6\u5931\u8d25\u3002\")\n        return\n\n    # 2. \u52a0\u8f7d\u505c\u7528\u8bcd\n    stopwords = load_stopwords(STOPWORDS_FILE_PATH)\n\n    # 3. \u6267\u884c\u5206\u6790\n    sentiments, sentiment_chart = analyze_sentiment(reviews)\n    df_keywords, keywords_chart = extract_keywords(reviews, stopwords)\n    df_issues, df_needs = identify_issues_and_needs(reviews, sentiments, stopwords)\n\n    # 4. \u751f\u6210\u62a5\u544a\n    generate_report(sentiments, sentiment_chart, df_keywords, keywords_chart, df_issues, df_needs)\n\n    print(\"\\n\u5206\u6790\u5b8c\u6210\u3002\")\n\nif __name__ == \"__main__\":\n    main()<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>\u5f00\u53d1\u601d\u8def<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[28],"views":370,"_links":{"self":[{"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/posts\/3887"}],"collection":[{"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/comments?post=3887"}],"version-history":[{"count":3,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/posts\/3887\/revisions"}],"predecessor-version":[{"id":3910,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/posts\/3887\/revisions\/3910"}],"wp:attachment":[{"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/media?parent=3887"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/categories?post=3887"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/viplao.com\/index.php\/wp-json\/wp\/v2\/tags?post=3887"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}