NLP 实验指南

基于自然语言处理的
舆情分析实验指南

本指南包含四个小实验,帮助同学们逐步完成舆情数据分析。从评论筛选到情感分类,所有代码均基于 Python 与常见机器学习库。

4 核心实验
6+ Python 库
7 实验章节
筛选相关评论
关键词匹配过滤
提取关键信息
实体识别与分类
案例对比分析
TF-IDF 相似度
舆情情感分析
情感倾向分类

实验环境准备

安装依赖与数据准备

操作方式说明:本指南中的每个代码块都需要按从上到下的顺序逐个复制运行,不能跳步。每个代码块都是一段独立的 Python 代码,你需要:

① 点击代码块右上角的「复制」按钮,将代码复制到剪贴板

② 粘贴到 Spyder 编辑器中(左侧代码编辑区)

③ 选中刚粘贴的代码,按 F9 运行选中的代码(或按 F5 运行整个文件)

④ 在右下角的 IPython 控制台中观察输出结果,确认无误后再继续下一个代码块

重要提醒:四个小实验之间有先后依赖关系——实验二、三、四都需要用到实验一筛选出的 relevant_comments 变量,因此必须先完成实验一,再依次做后续实验,不可跳步。

1 安装所需 Python 库

实验室电脑已安装 Anaconda 和 Spyder,无需额外安装。打开 Spyder 后,在下方 IPython 控制台中输入以下命令,安装本实验所需的第三方库:

bash
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

2 准备数据文件与工作目录

在电脑上新建一个文件夹(如 舆情分析实验),将 comments.txt 评论数据文件放入该文件夹。后续所有代码生成的文件也会保存在这里。

打开 Spyder,点击菜单栏 文件 → 另存为,将脚本保存到刚才的文件夹中(如 舆情分析实验/experiment.py)。脚本文件必须和 comments.txt 在同一文件夹下,否则代码会找不到数据文件。

也可以在 Spyder 菜单栏点击 运行 → 配置,将"工作目录"设置为 comments.txt 所在的文件夹,这样脚本就能正确找到数据文件。

3 导入通用库

以下代码是本实验的"公共基础",必须第一个运行,后续所有实验都依赖它。将此代码粘贴到 Spyder 编辑器最顶部,选中后按 F9 运行:

python
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 的顺序逐个运行。每个步骤的代码都依赖前一步产生的变量,请勿跳步或打乱顺序。

1 读取数据

将下方代码复制到 Spyder 编辑器中,选中后按 F9 运行。运行成功后会在 IPython 控制台输出评论总数。注意:确保 comments.txt 文件与脚本在同一文件夹下,否则会报文件找不到的错误。

python
with open('comments.txt', 'r', encoding='utf-8') as f:
    comments = [line.strip() for line in f if line.strip()]

print(f"总评论数:{len(comments)}")
2 定义关键词词典

根据事件主题设定关键词:

流浪狗 流浪猫 咬人 伤人 学校 物业 保安 管理 安全 受伤 赔偿 责任 捕杀 驱赶 动保 收容 回旋镖 开除 道歉 处理 校园
python
keywords = ['流浪狗', '流浪猫', '咬人', '伤人', '学校', '物业',
            '保安', '管理', '安全', '受伤', '赔偿', '责任',
            '捕杀', '驱赶', '动保', '收容', '回旋镖', '开除',
            '道歉', '处理', '校园']
3 筛选函数

一条评论只要包含任意关键词即视为相关(可根据需要调整为"包含至少2个关键词"以提高精度):

python
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)}")
4 结果输出
python
with open('relevant_comments.txt', 'w', encoding='utf-8') as f:
    for c in relevant_comments:
        f.write(c + '\n')
扩展:使用正则表达式模糊匹配
python
pattern = '|'.join(keywords)
relevant_comments2 = [c for c in comments if re.search(pattern, c)]
print(f"正则匹配数量:{len(relevant_comments2)}")

小实验二:提取关键信息

实体识别与关键事件提取

目标:从相关评论中识别出实体(如责任主体、处理方式、情绪对象)和关键事件信息。

操作提示:本实验依赖实验一的结果变量 relevant_comments,请确保实验一已全部运行完毕。本实验的代码块同样需要从上到下逐个运行。

实体词典定义
类别实体词
责任主体 学校 物业 保安 校领导 街道办 教育局 学生
处理方式 赔偿 道歉 捕杀 驱赶 开除 收容 打疫苗 绝育
评价词 太糟糕 不可接受 支持 反对 活该 可怜 理性 讽刺
基于字典匹配的实体提取
python
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")
使用中文命名实体识别
bash
pip install LAC
python
from LAC import LAC

lac = LAC(mode='lac')  # 词性标注+实体识别

text = "学校应该加强安全管理,流浪狗应该及时处理!"
result = lac.run(text)
print(result)  # 输出分词及标签(ORG机构名、PER人名等)

本实验因数据规模不大,使用词典匹配即可达到教学目的。

提取含"回旋镖"或"保安开除"的评论
python
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 的顺序逐个运行代码块,最后的"聚类"和"热力图"为可选扩展。

1 准备语料 — 选取原型评论
python
prototype = "学校当初为了舆论开除保安,现在流浪狗伤人了,真是回旋镖。"
2 中文分词 + TF-IDF 向量化
python
# 构建语料:原型 + 所有相关评论
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)
3 计算余弦相似度
python
# 计算原型与每一条评论的相似度
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]}")
评论间聚类(可选)
python
# 计算所有评论之间的相似度矩阵
sim_matrix = cosine_similarity(tfidf_matrix[1:])  # 去掉原型
# 可设置阈值,找出高相似度对,实现观点分组
案例对比可视化(热力图)

运行下方代码前,需要先运行上方的"评论间聚类"代码块生成 sim_matrix 变量,否则会报错。运行后会弹出热力图窗口。

python
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",从上到下逐个运行代码块即可。"方法二 简易词典"为替代方案,二者选其一即可。

方法一:使用 SnowNLP(推荐)
python
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())
方法二:自建简易情感词典
python
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'
情感分布可视化
python
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()
针对特定话题的情感分析
python
# 针对"保安开除"相关评论的情感分析
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())
输出带情感标签的完整结果
python
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 基于通用语料训练,对网络阴阳怪气评论判断可能不够准确,可手动调整阈值。