基于自然语言处理的
舆情分析实验指南
本指南包含四个小实验,帮助同学们逐步完成舆情数据分析。从评论筛选到情感分类,所有代码均基于 Python 与常见机器学习库。
实验环境准备
安装依赖与数据准备
操作方式说明:本指南中的每个代码块都需要按从上到下的顺序逐个复制运行,不能跳步。每个代码块都是一段独立的 Python 代码,你需要:
① 点击代码块右上角的「复制」按钮,将代码复制到剪贴板
② 粘贴到 Spyder 编辑器中(左侧代码编辑区)
③ 选中刚粘贴的代码,按 F9 运行选中的代码(或按 F5 运行整个文件)
④ 在右下角的 IPython 控制台中观察输出结果,确认无误后再继续下一个代码块
重要提醒:四个小实验之间有先后依赖关系——实验二、三、四都需要用到实验一筛选出的 relevant_comments 变量,因此必须先完成实验一,再依次做后续实验,不可跳步。
实验室电脑已安装 Anaconda 和 Spyder,无需额外安装。打开 Spyder 后,在下方 IPython 控制台中输入以下命令,安装本实验所需的第三方库:
pip install jieba scikit-learn pandas numpy matplotlib seaborn snownlp
如果安装速度慢,可使用国内镜像源:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple jieba scikit-learn pandas numpy matplotlib seaborn snownlp
在电脑上新建一个文件夹(如 舆情分析实验),将 comments.txt 评论数据文件放入该文件夹。后续所有代码生成的文件也会保存在这里。
打开 Spyder,点击菜单栏 文件 → 另存为,将脚本保存到刚才的文件夹中(如 舆情分析实验/experiment.py)。脚本文件必须和 comments.txt 在同一文件夹下,否则代码会找不到数据文件。
也可以在 Spyder 菜单栏点击 运行 → 配置,将"工作目录"设置为 comments.txt 所在的文件夹,这样脚本就能正确找到数据文件。
以下代码是本实验的"公共基础",必须第一个运行,后续所有实验都依赖它。将此代码粘贴到 Spyder 编辑器最顶部,选中后按 F9 运行:
import re import jieba import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity # 设置 matplotlib 中文显示 plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'Arial Unicode MS'] plt.rcParams['axes.unicode_minus'] = False
小实验一:筛选相关评论
关键词匹配过滤无关评论
目标:从混合评论中筛选出与"校园流浪狗伤人事件"相关的评论,排除无关闲聊。
操作提示:本实验的代码块需按步骤 1→2→3→4 的顺序逐个运行。每个步骤的代码都依赖前一步产生的变量,请勿跳步或打乱顺序。
将下方代码复制到 Spyder 编辑器中,选中后按 F9 运行。运行成功后会在 IPython 控制台输出评论总数。注意:确保 comments.txt 文件与脚本在同一文件夹下,否则会报文件找不到的错误。
with open('comments.txt', 'r', encoding='utf-8') as f: comments = [line.strip() for line in f if line.strip()] print(f"总评论数:{len(comments)}")
根据事件主题设定关键词:
keywords = ['流浪狗', '流浪猫', '咬人', '伤人', '学校', '物业', '保安', '管理', '安全', '受伤', '赔偿', '责任', '捕杀', '驱赶', '动保', '收容', '回旋镖', '开除', '道歉', '处理', '校园']
一条评论只要包含任意关键词即视为相关(可根据需要调整为"包含至少2个关键词"以提高精度):
def is_relevant(comment, keywords): for kw in keywords: if kw in comment: return True return False relevant_comments = [c for c in comments if is_relevant(c, keywords)] irrelevant_comments = [c for c in comments if not is_relevant(c, keywords)] print(f"相关评论数:{len(relevant_comments)}") print(f"无关评论数:{len(irrelevant_comments)}")
with open('relevant_comments.txt', 'w', encoding='utf-8') as f: for c in relevant_comments: f.write(c + '\n')
pattern = '|'.join(keywords) relevant_comments2 = [c for c in comments if re.search(pattern, c)] print(f"正则匹配数量:{len(relevant_comments2)}")
小实验二:提取关键信息
实体识别与关键事件提取
目标:从相关评论中识别出实体(如责任主体、处理方式、情绪对象)和关键事件信息。
操作提示:本实验依赖实验一的结果变量 relevant_comments,请确保实验一已全部运行完毕。本实验的代码块同样需要从上到下逐个运行。
| 类别 | 实体词 |
|---|---|
| 责任主体 | 学校 物业 保安 校领导 街道办 教育局 学生 |
| 处理方式 | 赔偿 道歉 捕杀 驱赶 开除 收容 打疫苗 绝育 |
| 评价词 | 太糟糕 不可接受 支持 反对 活该 可怜 理性 讽刺 |
entities = {
'责任主体': ['学校', '物业', '保安', '校领导', '街道办', '教育局', '学生'],
'处理方式': ['赔偿', '道歉', '捕杀', '驱赶', '开除', '收容', '打疫苗', '绝育'],
'评价词': ['太糟糕', '不可接受', '支持', '反对', '活该', '可怜', '理性', '讽刺']
}
def extract_entities(comment):
found = {k: [] for k in entities.keys()}
for cat, words in entities.items():
for w in words:
if w in comment:
found[cat].append(w)
return found
# 示例:提取前10条相关评论的实体
for i, com in enumerate(relevant_comments[:10]):
print(f"{i+1}. {com}")
print(f" 实体:{extract_entities(com)}\n")pip install LAC
from LAC import LAC lac = LAC(mode='lac') # 词性标注+实体识别 text = "学校应该加强安全管理,流浪狗应该及时处理!" result = lac.run(text) print(result) # 输出分词及标签(ORG机构名、PER人名等)
本实验因数据规模不大,使用词典匹配即可达到教学目的。
backlash = [c for c in relevant_comments if '回旋镖' in c or '保安' in c] print(f"涉及保安/回旋镖的评论数:{len(backlash)}") for b in backlash[:5]: print(b)
小实验三:案例对比分析
TF-IDF 相似度计算与评论聚类
目标:使用 TF-IDF 计算评论之间的相似度,找出观点相似的评论,并与"原型评论"对比。
操作提示:本实验依赖实验一的 relevant_comments 变量,请确保实验一已运行完毕。按步骤 1→2→3 的顺序逐个运行代码块,最后的"聚类"和"热力图"为可选扩展。
prototype = "学校当初为了舆论开除保安,现在流浪狗伤人了,真是回旋镖。"# 构建语料:原型 + 所有相关评论 corpus = [prototype] + relevant_comments # 中文分词函数 def chinese_cut(text): return ' '.join(jieba.cut(text)) # 对所有文本进行分词 cut_corpus = [chinese_cut(doc) for doc in corpus] # TF-IDF 向量化 vectorizer = TfidfVectorizer(token_pattern=r'(?u)\b\w+\b') tfidf_matrix = vectorizer.fit_transform(cut_corpus)
# 计算原型与每一条评论的相似度 prototype_vec = tfidf_matrix[0:1] others_vec = tfidf_matrix[1:] similarities = cosine_similarity(prototype_vec, others_vec).flatten() # 输出最相似的5条评论 top_indices = similarities.argsort()[-5:][::-1] print("与原型最相似的评论:") for idx in top_indices: print(f"相似度 {similarities[idx]:.3f}: {relevant_comments[idx]}")
# 计算所有评论之间的相似度矩阵 sim_matrix = cosine_similarity(tfidf_matrix[1:]) # 去掉原型 # 可设置阈值,找出高相似度对,实现观点分组
运行下方代码前,需要先运行上方的"评论间聚类"代码块生成 sim_matrix 变量,否则会报错。运行后会弹出热力图窗口。
import matplotlib.pyplot as plt import seaborn as sns sns.heatmap(sim_matrix[:20, :20], cmap='YlOrRd', xticklabels=False, yticklabels=False) plt.title("前20条评论相似度热力图") plt.show()
小实验四:舆情情感分析
情感倾向分类与分布统计
目标:对评论进行情感倾向分类(积极、消极、中性),并统计舆情情绪分布。
操作提示:本实验依赖实验一的 relevant_comments 变量。推荐使用"方法一 SnowNLP",从上到下逐个运行代码块即可。"方法二 简易词典"为替代方案,二者选其一即可。
from snownlp import SnowNLP def sentiment_snownlp(text): s = SnowNLP(text) prob = s.sentiments # 越接近1越正面,0越负面 if prob > 0.6: return 'positive' elif prob < 0.4: return 'negative' else: return 'neutral' # 对相关评论进行情感分析 sentiments = [sentiment_snownlp(com) for com in relevant_comments] df_sent = pd.DataFrame({'comment': relevant_comments, 'sentiment': sentiments}) print(df_sent['sentiment'].value_counts())
pos_words = set(['康复', '合理', '维护', '支持', '早日', '好消息', '肯定', '值得', '有效', '安全']) neg_words = set(['糟糕', '不可接受', '严重问题', '太差', '不作为', '荒谬', '恶心', '心寒', '愤怒', '讽刺', '活该']) def simple_sentiment(text): pos_cnt = sum(1 for w in pos_words if w in text) neg_cnt = sum(1 for w in neg_words if w in text) if pos_cnt > neg_cnt: return 'positive' elif neg_cnt > pos_cnt: return 'negative' else: return 'neutral'
sent_counts = df_sent['sentiment'].value_counts() plt.bar(sent_counts.index, sent_counts.values, color=['green', 'red', 'gray']) plt.title('舆情情感倾向分布') plt.xlabel('情感类别') plt.ylabel('评论数量') for i, v in enumerate(sent_counts.values): plt.text(i, v+1, str(v), ha='center') plt.show()
# 针对"保安开除"相关评论的情感分析 guard_comments = [c for c in relevant_comments if '保安' in c] guard_sent = [sentiment_snownlp(c) for c in guard_comments] print(pd.Series(guard_sent).value_counts())
df_sent.to_csv('sentiment_result.csv', index=False, encoding='utf-8-sig') print("情感分析结果已保存至 sentiment_result.csv")
实验报告要求
围绕以下五个维度撰写实验报告
- 1 数据概况 — 总评论数、筛选出的相关评论比例
- 2 关键信息提取结果 — 出现最多的责任主体、处理方式等
- 3 相似度分析结论 — 哪些观点与原型评论最相似?评论整体可分为几类?
- 4 情感分布 — 积极、消极、中性的比例,并解释为什么消极评论占主导(结合事件背景)
- 5 综合讨论 — 对比"小实验一"筛选出的评论与小实验四的情感分析结果,验证"回旋镖"言论是否更偏向消极讽刺
注意事项
实验过程中的关键提醒
自定义词典 — 中文分词效果受词典影响,可考虑添加自定义词典:jieba.add_word('回旋镖')
情感分析局限 — SnowNLP 基于通用语料训练,对网络阴阳怪气评论判断可能不够准确,可手动调整阈值。