在数字化时代,日记、小说、日志等文本记录承载着丰富的情感信息。如何量化这些文字的情绪倾向,直观呈现情感波动?本文将带你开发一个本地文本情感分析与可视化工具,无需依赖外部API,即可分析文本的情感趋势、生成词云与统计报告。
背景与需求
我们希望通过工具实现:
– 读取本地文本(日记、小说、日志等),按段落分割内容。
– 基于内置情感词典分析每个段落的情感得分(正面词+1、负面词-1)。
– 可视化输出:情感趋势折线图、正负词云、统计报告。
– 图形界面(GUI)支持文件选择、分析触发与结果展示。
技术思路拆解
工具的核心流程分为四部分:
1. 文件处理
- 读取本地文本,处理中文编码(UTF-8)。
- 按换行符(或自定义分隔符)分割段落。
2. 情感分析
- 内置情感词典(正面词+1、负面词-1)。
- 统计每个段落的正负词数量,计算情感得分(正面数 – 负面数)。
3. 数据可视化
- 折线图:用
matplotlib展示各段落情感得分趋势。 - 词云图:用
wordcloud生成正负词云(需解决中文字体显示)。
4. GUI交互
- 用
Tkinter实现文件选择、按钮事件、结果展示。
完整代码实现
以下是工具的Python实现(需安装matplotlib、wordcloud库):
import tkinter as tk
from tkinter import filedialog, messagebox
import matplotlib.pyplot as plt
from wordcloud import WordCloud
import os
# 配置matplotlib中文字体(解决中文显示乱码)
plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"]
plt.rcParams["axes.unicode_minus"] = False # 解决负号显示问题
# 内置情感词典(词: 情感值,1为正面,-1为负面)
emotion_dict = {
"开心": 1, "幸福": 1, "阳光": 1, "期待": 1, "喜欢": 1,
"美好": 1, "快乐": 1, "满意": 1, "温暖": 1, "成功": 1,
"难过": -1, "孤独": -1, "疲惫": -1, "悲伤": -1, "讨厌": -1,
"糟糕": -1, "痛苦": -1, "失望": -1, "寒冷": -1, "失败": -1
}
def read_and_split(file_path, delimiter="\n"):
"""读取文本文件并按分隔符分割段落"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# 按分隔符分割并过滤空段落
paragraphs = [p.strip() for p in content.split(delimiter) if p.strip()]
return paragraphs
except Exception as e:
messagebox.showerror("错误", f"文件处理失败:{str(e)}")
return []
def analyze_emotion(paragraphs):
"""分析段落情感,返回得分、总正负词数、正负词列表"""
paragraph_scores = []
positive_total = 0
negative_total = 0
positive_words = []
negative_words = []
for para in paragraphs:
pos = 0 # 正面词出现次数
neg = 0 # 负面词出现次数
for word, score in emotion_dict.items():
count = para.count(word)
if score == 1:
pos += count
positive_words.extend([word] * count)
else:
neg += count
negative_words.extend([word] * count)
para_score = pos - neg
paragraph_scores.append(para_score)
positive_total += pos
negative_total += neg
return paragraph_scores, positive_total, negative_total, positive_words, negative_words
def plot_emotion_trend(scores, parent):
"""绘制情感趋势折线图(嵌入Tkinter)"""
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(range(1, len(scores)+1), scores, marker='o', color='blue')
ax.set_xlabel('段落序号')
ax.set_ylabel('情感得分')
ax.set_title('情感得分趋势图')
ax.grid(True)
# 嵌入Tkinter窗口
canvas = plt.backends.backend_tkagg.FigureCanvasTkAgg(fig, master=parent)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=1)
def generate_wordcloud(words, is_positive, parent):
"""生成正负词云(嵌入Tkinter)"""
text = ' '.join(words)
if not text:
return
# 中文字体路径(Windows示例:C:/Windows/Fonts/simhei.ttf)
font_path = "simhei.ttf"
wc = WordCloud(
font_path=font_path,
background_color='white',
width=400,
height=300,
max_words=100
).generate(text)
fig, ax = plt.subplots(figsize=(4, 3))
ax.imshow(wc, interpolation='bilinear')
ax.axis('off')
ax.set_title('正面词云' if is_positive else '负面词云')
canvas = plt.backends.backend_tkagg.FigureCanvasTkAgg(fig, master=parent)
canvas.draw()
canvas.get_tk_widget().pack(fill=tk.BOTH, expand=1)
class EmotionAnalyzerApp:
def __init__(self, root):
self.root = root
self.root.title("文本情感分析与可视化工具")
self.root.geometry("1000x800")
# 变量初始化
self.file_path = tk.StringVar()
self.paragraphs = []
self.scores = []
self.positive_total = 0
self.negative_total = 0
self.positive_words = []
self.negative_words = []
# 界面布局:顶部文件栏
top_frame = tk.Frame(root)
top_frame.pack(fill=tk.X, padx=10, pady=10)
tk.Label(top_frame, text="文本文件:").pack(side=tk.LEFT)
tk.Entry(top_frame, textvariable=self.file_path, width=50).pack(side=tk.LEFT, padx=5)
tk.Button(top_frame, text="浏览...", command=self.browse_file).pack(side=tk.LEFT, padx=5)
tk.Button(top_frame, text="开始分析", command=self.analyze).pack(side=tk.LEFT, padx=5)
# 中间结果区:折线图+词云
middle_frame = tk.Frame(root)
middle_frame.pack(fill=tk.BOTH, expand=1, padx=10, pady=10)
# 折线图区域
self.trend_frame = tk.LabelFrame(middle_frame, text="情感趋势图")
self.trend_frame.pack(fill=tk.BOTH, expand=1, side=tk.LEFT, padx=5, pady=5)
# 词云区域
wordcloud_frame = tk.Frame(middle_frame)
wordcloud_frame.pack(fill=tk.BOTH, expand=1, side=tk.RIGHT, padx=5, pady=5)
self.positive_wc_frame = tk.LabelFrame(wordcloud_frame, text="正面词云")
self.positive_wc_frame.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
self.negative_wc_frame = tk.LabelFrame(wordcloud_frame, text="负面词云")
self.negative_wc_frame.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
# 底部报告区
report_frame = tk.LabelFrame(root, text="情感统计报告")
report_frame.pack(fill=tk.X, padx=10, pady=10)
self.report_text = tk.Text(report_frame, height=6, width=80)
self.report_text.pack(fill=tk.BOTH, expand=1, padx=5, pady=5)
def browse_file(self):
"""打开文件选择对话框"""
file_path = filedialog.askopenfilename(
filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")],
title="选择文本文件"
)
if file_path:
self.file_path.set(file_path)
def analyze(self):
"""执行情感分析与可视化"""
file_path = self.file_path.get()
if not os.path.exists(file_path):
messagebox.showerror("错误", "文件不存在!")
return
# 读取并分割文件
self.paragraphs = read_and_split(file_path)
if not self.paragraphs:
return
# 情感分析
self.scores, self.positive_total, self.negative_total, self.positive_words, self.negative_words = analyze_emotion(self.paragraphs)
# 展示结果
self.show_trend()
self.show_wordclouds()
self.show_report()
def show_trend(self):
"""展示情感趋势图"""
# 清空原有组件
for widget in self.trend_frame.winfo_children():
widget.destroy()
plot_emotion_trend(self.scores, self.trend_frame)
def show_wordclouds(self):
"""展示正负词云"""
# 清空正面词云区域
for widget in self.positive_wc_frame.winfo_children():
widget.destroy()
# 生成正面词云
if self.positive_words:
generate_wordcloud(self.positive_words, True, self.positive_wc_frame)
# 清空负面词云区域
for widget in self.negative_wc_frame.winfo_children():
widget.destroy()
# 生成负面词云
if self.negative_words:
generate_wordcloud(self.negative_words, False, self.negative_wc_frame)
def show_report(self):
"""展示统计报告"""
self.report_text.delete(1.0, tk.END)
if not self.scores:
return
avg_score = sum(self.scores) / len(self.scores)
max_score = max(self.scores)
max_para_idx = self.scores.index(max_score) + 1 # 段落序号从1开始
report = f"""情感统计报告:
总段落数:{len(self.paragraphs)}
总正面词数:{self.positive_total}
总负面词数:{self.negative_total}
平均情感得分:{avg_score:.2f}(正数表示整体偏积极)
情感最强烈段落:第{max_para_idx}段(得分{max_score:.2f})
"""
self.report_text.insert(tk.END, report)
if __name__ == "__main__":
root = tk.Tk()
app = EmotionAnalyzerApp(root)
root.mainloop()
代码解析
1. 文件处理(read_and_split)
- 用
open()读取文本,指定encoding='utf-8'处理中文。 - 按换行符(
\n)分割段落,过滤空行。
2. 情感分析(analyze_emotion)
- 遍历每个段落,统计正面词(+1)和负面词(-1)的出现次数。
- 情感得分 = 正面词数 – 负面词数。
- 收集所有正负词,用于生成词云。
3. 可视化
- 折线图:
matplotlib绘制情感得分趋势,嵌入Tkinter窗口。 - 词云:
wordcloud生成词云,需指定中文字体(如simhei.ttf)解决中文显示。
4. GUI交互(EmotionAnalyzerApp)
- 顶部:文件选择、分析按钮。
- 中间:折线图(左)、正负词云(右)。
- 底部:统计报告(总词数、平均得分、情感最强烈段落)。
运行与扩展
环境依赖
pip install matplotlib wordcloud
扩展方向
- 自定义词典:支持用户导入CSV/JSON格式的情感词典。
- 章节分割:用正则表达式(如
第\d+章)分割小说章节。 - 报告导出:将统计报告、图表导出为PDF/图片。
- 分词优化:引入
jieba分词,提升中文匹配准确性。
总结
本工具实现了本地文本情感分析与可视化,覆盖文件操作、词典匹配、数据可视化与GUI设计。通过分析日记、小说、日志等文本,我们可以直观理解文字背后的情绪脉络。代码结构清晰,可作为Python实战项目,帮助掌握多模块协作与情感分析基础逻辑。