《文本分析》实操指南
卢圣华,2025年3月2日
本指南详细介绍如何在公共管理研究中运用 Python 进行常用的数据处理与分析方法,包括中文文本的分词与词云可视化、情感分析(关键词匹配和 SnowNLP)、文本分类(监督学习的朴素贝叶斯分类和无监督学习的 LDA 主题建模),以及关键信息提取(正则表达式和 spaCy 实体识别)。我们将使用 Python + Jupyter Notebook 来编写与演示这些过程。代码力求简洁易懂,并配有详细注释,方便同学理解代码逻辑,并可对参数进行自由调节。
目录:
0. 教程概述与配套文件说明
本教程《文本分析实战》旨在为大家介绍文本分析方法在公共管理领域的具体应用,涵盖中文分词、词云生成、情感分析、文本分类以及关键信息提取等内容。通过本教程,你将掌握如何使用 Python 及相关库来实现以上任务。
为了帮助你更好地实践学习,我已准备了一套完整的配套文件:
文件名 | 文件说明 |
training.csv | 用于训练文本分类模型的示例数据 |
full.csv | 用于预测或主题建模的完整示例数据 |
stopwords.txt | 中文停用词列表,用于过滤无意义的词语 |
SimSun.ttf | 中文字体文件,用于词云生成时避免中文乱码 |
ntusd-positive.txt | 中文情感分析的正面情绪词典 |
ntusd-negative.txt | 中文情感分析的负面情绪词典 |
ChnSentiCorp_htl_all.csv | 常用的中文酒店评论情感分析数据集 |
wordcloud.png | 示例词云图片 |
text_analysis.ipynb | 配套的Jupyter Notebook代码示例 |
如何下载和使用配套文件
为了方便大家获取和使用上述文件,我已经将所有文件打包整理并上传到Notion。你只需点击下方链接,进入页面后即可预览文件内容并进行下载:
注意事项:
- 下载文件并解压后,请确保将所有文件保存在同一个工作目录下,以便顺利运行教程中的代码示例。
- 推荐使用Jupyter Notebook打开并运行代码文件(
text_analysis.ipynb
)。
接下来,你可以进入教程的第一章,开始中文分词及词云的学习和实践 🎉
1. 中文分词及词云
背景与目标: 中文文本分析的第一步通常是中文分词。由于中文没有空格来分隔单词,我们需要借助分词工具将句子切分成有意义的词语。常用的中文分词库有 jieba
等。完成分词后,我们可以通过词频统计来了解文本的关键词分布,并使用词云(Word Cloud)将高频词进行可视化,从直观上展示文本中的重要词汇。
本节将介绍如何使用 jieba
对中文文本进行分词,并基于分词结果绘制词云图。我们将使用一段示例中文文本自行演示整个过程。
1.1 中文分词(jieba)
要解决的问题: 将一段中文文本切分为单独的词语序列,以便后续进行统计和分析。
实现思路: 使用 jieba
库的默认分词模式对文本分词。jieba
库具有简单易用的接口,我们可以通过 jieba.cut()
将文本切分成生成器,然后转成列表或用空格拼接。分词的精细程度可以通过调整模式(精确模式、全模式、搜索引擎模式)或自定义词典来控制。
代码示例: 我们将对一段示例文本进行分词,并展示分词结果列表:
# 导入jieba库
import jieba
# 示例中文文本
text = "在公共管理领域,数据驱动决策变得越来越重要。这需要掌握各种研究方法,包括文本分析和数据挖掘方法。"
# 使用jieba进行中文分词
words = jieba.lcut(text) # 使用精确模式进行分词,返回列表
print("分词结果:", words)
代码详解:
jieba.lcut(text)
:对文本进行精确模式分词,直接返回列表。也可以使用jieba.cut(text)
得到生成器,再用list()
转换。默认精确模式适合大多数应用,使切分结果尽量合理。
- 我们可以根据需要调整分词模式:
- 全模式:
jieba.lcut(text, cut_all=True)
会把句子中所有可能的词语都扫描出来,存在冗余,适合需要找出所有词的情况。
- 搜索引擎模式:
jieba.lcut_for_search(text)
对长词再细分,适合搜索引擎建立索引。
- 全模式:
- 如果需要添加自定义词汇(如领域专有名词),可以用
jieba.add_word('自定义词')
来避免被错分。
运行上面的代码,将输出一个列表形式的分词结果。例如:
分词结果: ['在', '公共管理', '领域', ',', '数据', '驱动', '决策', '变得', '越来越', '重要', '。', '这', '需要', '掌握', '各种', '研究', '方法', ',', '包括', '文本', '分析', '和', '数据', '挖掘', '方法', '。']
可以看到句子被成功切分成了词语和标点符号。分词的结果将作为后续词频统计和词云生成的基础。
1.2 绘制词云
要解决的问题: 将文本中的高频词直观地展示出来,突出重要词语。词云是一种将词频用字体大小表示的可视化方法,频率越高的词显示得越大。
实现思路: 使用 wordcloud
库根据分词结果生成词云图。首先需要统计词频或直接将所有词拼接成字符串,然后传给 WordCloud
对象。为了确保词云更加准确,我们需要去除停用词(如“的”“是”“和”等常见但无意义的词),然后再进行可视化。我们可以设置词云的一些参数,如字体、背景颜色、最大词数等,然后生成并显示图像。
代码示例: 下面演示如何基于上一小节得到的分词结果来绘制词云,并考虑停用词的处理(假设 stopwords.txt
已放在工作目录下)。
# 安装并导入词云库(如果尚未安装,需要使用 pip 安装 wordcloud)
# !pip install wordcloud
from wordcloud import WordCloud
# 读取停用词表(假设 stopwords.txt 放在工作目录下)
with open("stopwords.txt", "r", encoding="utf-8") as f:
stopwords = set(f.read().splitlines())
# 过滤停用词
filtered_words = [word for word in words if word not in stopwords and len(word) > 1] # 去掉单字词(通常无意义)
# 将分词结果列表拼接成空格分隔的字符串
text_for_wc = " ".join(filtered_words)
# 配置词云参数并生成词云对象
wc = WordCloud(font_path=None, # 字体路径,中文必须指定中文字体路径,否则会乱码。这里使用默认字体时可能无法正确显示中文
background_color="white", # 背景颜色白色
max_words=100, # 显示的最大词数
width=800, height=400, # 图片尺寸
collocations=False # 是否避免重复单词,这里不需要启用以免影响中文显示
)
wordcloud = wc.generate(text_for_wc) # 根据文本生成词云
# 将词云保存为图片文件(在Jupyter中也可以直接显示)
wordcloud.to_file("wordcloud.png")
print("词云已生成并保存为 wordcloud.png 文件。")
代码详解:
- 停用词处理:
stopwords.txt
需要提前准备,并存放在工作目录下,每行一个停用词(如“的”“是”“和”等)。
set(f.read().splitlines())
读取停用词文件并转换为set
结构,加快查找速度。
filtered_words = [word for word in words if word not in stopwords and len(word) > 1]
:- 去除停用词,使词云更能突出关键内容;
- 去除单字词(如“的”“是”),因这些词通常无意义,仅影响视觉效果。
- 词云生成:
text_for_wc = " ".join(filtered_words)
:将处理后的分词结果转换为WordCloud
可用的格式(以空格分隔)。
WordCloud(...)
:创建词云对象:font_path
:指定字体路径。生成中文词云时需要指定支持中文的字体文件路径(如SimHei.ttf
),否则中文会乱码。
background_color="white"
:设置词云背景为白色。
max_words=100
:最多显示100个词语。
width, height
:指定生成的词云图片尺寸。
collocations=False
:避免相关联词汇重复显示,对于中文分词结果,将其设为False
以防止英文情况下的重复词问题。
- 词云可视化:
wc.generate(text_for_wc)
计算词频并生成词云数据。
wordcloud.to_file("wordcloud.png")
将词云结果保存为图片文件,方便在 Notebook 之外查看。
- (提示:在 Jupyter Notebook 中,可以直接使用
%matplotlib inline
并调用plt.imshow(wordcloud)
来显示词云。)
词云结果: 经过停用词处理后,生成的词云将更加清晰地突出核心关键词。例如,如果使用上面的示例文本,词云可能会突出显示“数据”“方法”“公共管理”等重要词汇,而不会被“的”“是”“在”等无意义词干扰。
(注意:在实际运行时,请确保提供中文字体路径以正确显示中文。在本手册环境中,我们将代码展示为主要参考,运行效果请在本地 Jupyter Notebook 中执行观察。)
2. 情感分析
背景与目标: 情感分析旨在判断文本所表达的主观情绪倾向,例如评价一句话是正面(积极)还是负面(消极)。在公共管理研究中,情感分析可用于分析公众对政策的态度、社交媒体上的民意倾向等。
情感分析的方法主要分为两类:
- 基于情感词典/关键词的规则方法:预先定义正面和负面情感词,根据文本中出现的情感词来评估情感倾向。
- 基于机器学习或深度学习的模型方法:通过训练模型自动判断情感。SnowNLP 是一个针对中文的库,内部使用了一定的机器学习模型,能对中文句子进行情感判断。
本节将分别介绍这两种方法:首先构建一个简单的关键词匹配情感分析器,然后演示使用 SnowNLP 进行情感分析。
2.1 基于关键词的情感计算
要解决的问题: 不借助复杂模型,如何利用预先准备的情感关键词来判断文本情感?
实现思路: 先准备两份情感关键词词典:一份包含常见的正面情绪词(如“好”、“满意”、“赞赏”等),另一份包含负面情绪词(如“差”、“失望”、“愤怒”等)。对于给定文本,我们统计其中出现的正面和负面词数量,或者根据匹配情况打分。简单的方法:计算情感分值 = 正面词计数 - 负面词计数,分值大于0可判为正面,反之负面(或根据具体需求设定阈值)。
代码示例: 下面我们构造简易的正负面词典,并对两条示例句子进行情感判断:
# 定义简单的情感关键词字典
positive_words = ["好", "满意", "赞", "喜欢", "高兴"] # 正面情感词列表(可以根据需要扩充)
negative_words = ["差", "不好", "失望", "愤怒", "讨厌"] # 负面情感词列表
# 情感分析函数:计算情感分值(正面词+1,负面词-1)
def sentiment_score(text):
score = 0
for word in positive_words:
if word in text:
score += 1
for word in negative_words:
if word in text:
score -= 1
return score
# 示例文本
text1 = "这次服务质量很好,我很满意!"
text2 = "产品问题太多了,让人非常失望。"
# 计算情感分值
score1 = sentiment_score(text1)
score2 = sentiment_score(text2)
print(f"文本1: {text1} -> 情感分值: {score1}")
print(f"文本2: {text2} -> 情感分值: {score2}")
# 根据分值判断情感极性
def sentiment_label(score):
if score > 0:
return "正面"
elif score < 0:
return "负面"
else:
return "中性或无法判断"
print(f"文本1情感极性判断: {sentiment_label(score1)}")
print(f"文本2情感极性判断: {sentiment_label(score2)}")
代码详解:
positive_words
和negative_words
列表:列出我们认为具有强烈情感倾向的词。实际应用中可以使用更全面的情感词典。
sentiment_score(text)
函数:遍历正面词和负面词列表,判断每个情感词是否出现在待分析文本中。如果出现正面词则score += 1
,出现负面词则score -= 1
。这种方法将每个匹配到的情感词都简单地累加或减去分值,假设每个词贡献相同的权重。- 注意:这里用了最简单的“子串判断”(
if word in text
)来确定情感词是否出现。对于中文,这样做能粗略找到关键词,但如果要更精确地匹配单词(避免部分匹配误判),可以先对文本分词,然后逐个词对比。
- 注意:这里用了最简单的“子串判断”(
- 示例文本
text1
和text2
分别表示明显正面和明显负面的句子。
- 计算得到的
score1
和score2
:- 对于
text1
= "这次服务质量很好,我很满意!" 来说,包含了“好”和“满意”各一个,因此分值应为 +2,判断为正面。
- 对于
text2
= "产品问题太多了,让人非常失望。" 来说,包含了“失望”一个负面词,分值为 -1,判断为负面。
- 对于
sentiment_label(score)
函数:将情感分值映射为情感极性标签。这里简单地以0为分界,大于0算正面,小于0算负面,等于0算中性/无法判断。如果想提高准确度,可以设置一个正面和负面的阈值范围,或者引入不同权重。
运行上述代码,可得到类似输出:
文本1: 这次服务质量很好,我很满意! -> 情感分值: 2
文本2: 产品问题太多了,让人非常失望。 -> 情感分值: -1
文本1情感极性判断: 正面
文本2情感极性判断: 负面
优缺点讨论: 基于关键词的情感分析实现简单直观,但也有局限:
- 优点:无需训练数据,易于快速实现;可以根据领域自定义词典。
- 缺点:对词典的依赖很强,覆盖不全会导致漏判或错判;无法理解复杂句型,比如双重否定或讽刺;无法处理不同词的不同权重。此外,此方法没有考虑词频(一个词出现多次对情感的影响)以及程度副词(如“非常好”和“有点好”情感强度不同)。
在实际应用中,简单的关键词匹配可用于快速粗略分析,但如果需要更高的准确率,通常会引入机器学习模型或更复杂的规则。下面我们将介绍使用机器学习库 SnowNLP 对中文文本进行情感分析。
2.2 使用 SnowNLP 进行情感分析
要解决的问题: 利用现有的中文情感分析工具包,对文本的情感倾向进行自动判断,减少手工构建规则的工作。
实现思路: SnowNLP 是一个专门针对中文的自然语言处理库,支持中文分词、关键词提取、情感分析等功能。SnowNLP 内置了一个中文情感分析模型(据称基于大量带标注的点评数据训练),可以直接对一句话给出一个情感倾向评分。这个评分通常是0到1的浮点数,接近1表示偏向正面,接近0表示偏向负面。
使用 SnowNLP 进行情感分析非常简单:将文本传入 SnowNLP
对象,然后调用其 .sentiments
属性即可得到情感概率值。
代码示例: 演示 SnowNLP 对正面和负面句子的情感判断:
# 安装并导入 SnowNLP 库(如果未安装,需要使用 pip 安装 snownlp)
# !pip install snownlp
from snownlp import SnowNLP
# 示例文本(一个正面,一个负面)
text_pos = "这个政策真是太好了,帮助了很多人。"
text_neg = "这个决策糟透了,引起了广泛的不满。"
# 使用 SnowNLP 进行情感分析
s1 = SnowNLP(text_pos)
s2 = SnowNLP(text_neg)
sentiment_score1 = s1.sentiments # 返回情感倾向概率,接近1表示正面
sentiment_score2 = s2.sentiments
print(f"文本: '{text_pos}' -> 情感倾向评分: {sentiment_score1:.3f}")
print(f"文本: '{text_neg}' -> 情感倾向评分: {sentiment_score2:.3f}")
# 根据阈值判断正负面
threshold = 0.5
label1 = "正面" if sentiment_score1 >= threshold else "负面"
label2 = "正面" if sentiment_score2 >= threshold else "负面"
print(f"文本1判断为: {label1}, 文本2判断为: {label2}")
代码详解:
SnowNLP(text)
:创建 SnowNLP 对象,自动对文本进行处理。
s.sentiments
:获得情感分析结果,是一个 0~1 的浮点数。通常大于等于0.5可视为正面,小于0.5视为负面。这个阈值也可以根据实际情况调整,比如0.6和0.4作为更保守的边界。
- 示例中:
text_pos
是一句正面的评价性语句,预计s1.sentiments
会得到一个接近1的值,比如0.8以上。
text_neg
是一句负面的批评性语句,预计s2.sentiments
会得到一个接近0的值,比如0.2以下。
- 通过简单地与0.5比较,我们将评分转换为了“正面”或“负面”标签。同时我们格式化输出评分到小数点后三位,以便观察。
运行结果举例:
文本: '这个政策真是太好了,帮助了很多人。' -> 情感倾向评分: 0.928
文本: '这个决策糟透了,引起了广泛的不满。' -> 情感倾向评分: 0.134
文本1判断为: 正面, 文本2判断为: 负面
(具体输出可能因 SnowNLP 内部模型而略有不同,但总的来说第一句评分会远高于0.5,第二句会远低于0.5。)
SnowNLP 方法的特点:
- 使用方便,无需自行训练模型,对于常见的评价性语句有较高准确率。
- 适合快速对中文文本做情感分析。不过SnowNLP的模型训练数据偏向于一般评价场景(如网络评论),对于专业领域的文本(例如政策文件)可能效果一般。如果应用在特定领域,需考虑定制训练自己的情感模型。
通过对比可以看到,SnowNLP 这种模型方法相较关键词匹配更加智能,能够综合考虑句中的各种信息(如否定词、程度词等)得出情感倾向,且使用更加简单。但需要注意模型可能存在偏见,需要评估其对你要处理的数据集是否适用。
3. 文本分类
背景与目标: 文本分类是将文本归入预先定义的类别中的过程。在公共管理研究中,文本分类可用于很多场景,比如将政策文件按领域分类(教育、医疗、环保等),或者将市民意见分类为不同类型的诉求。文本分类方法分为监督学习(有标签的数据训练模型)和无监督学习(无标签情况下自动聚类主题)。
本节我们介绍两种方法:
- 监督学习:使用朴素贝叶斯(Naive Bayes)分类器对文本进行分类。
- 无监督学习:使用 LDA (Latent Dirichlet Allocation) 主题模型来发现文本的潜在主题分类。
我们将基于一些实际数据(假设已存放于工作目录中)演示如何使用这两种方法实现文本分类任务,并展示如何训练模型和解释结果。
3.1 监督学习:朴素贝叶斯分类
要解决的问题: 给定一些已经标注好类别的文本数据,如何训练一个分类模型,对新文本进行自动分类?
朴素贝叶斯方法简介: 朴素贝叶斯是一种基于概率的简单高效的分类算法,适用于文本分类等任务。它假设特征(词语)之间相互独立(“朴素”的由来),依据训练数据计算先验概率和似然,然后对新样本使用贝叶斯定理计算后验概率,选择概率最大的类别作为预测结果。尽管独立性假设较强,朴素贝叶斯在很多文本分类场景下表现良好,尤其是当特征数很多、每个特征影响较小的情况。
实现思路: 使用 sklearn
提供的 MultinomialNB
(多项式朴素贝叶斯)分类器。具体流程如下:
- 导入数据:从文件中读取已标注的文本数据。
- 中文分词:使用
jieba
对文本数据进行分词处理。
- 特征提取:使用
TfidfVectorizer
将分词后的文本转为数值特征向量。
- 训练模型:用标注好的数据训练朴素贝叶斯模型。
- 预测分类:对新数据进行类别预测并输出预测结果。
代码示例:
import os
import pandas as pd
import jieba
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import Pipeline
# 设置工作目录
work_dir = r"E:\OneDrive\PhD Candidate\Python Project\Jupyter Notebook\课程教学\文本分析实战"
os.chdir(work_dir)
# 读取数据
train_df = pd.read_csv("training.csv")
full_df = pd.read_csv("full.csv")
# 中文分词函数
def tokenize(text):
return " ".join(jieba.cut(text))
# 对数据进行分词处理
train_df["processed_text"] = train_df["新闻标题"].apply(tokenize)
full_df["processed_text"] = full_df["新闻标题"].apply(tokenize)
# 构建分类模型(朴素贝叶斯)
nb_pipeline = Pipeline([
('vect', TfidfVectorizer()),
('clf', MultinomialNB()),
])
# 使用训练数据训练模型
nb_pipeline.fit(train_df["processed_text"], train_df["分类"])
# 对完整数据集进行分类预测
full_df["predicted_category"] = nb_pipeline.predict(full_df["processed_text"])
# 输出部分分类预测结果
print(full_df[["新闻标题", "predicted_category"]].head(10))
代码详解:
- 数据文件
training.csv
和full.csv
中包含“新闻标题”和对应类别(如教育、经济、环境等)。
- 使用
jieba
分词函数将中文标题转换成空格分隔的字符串,便于向量化处理。
- 使用
TfidfVectorizer
提取特征,生成文本的TF-IDF向量表示。
- 使用
MultinomialNB
训练分类模型,并通过管道Pipeline
简化训练预测流程。
- 模型训练完成后,自动对新数据进行分类预测,并展示预测结果。
运行结果示例:
新闻标题 predicted_category
0 高校图书馆尝试夜间自助借还服务 教育
1 沿海湿地锐减引发候鸟栖息危机 教育
2 民营企业融资环境转暖,投资者信心回升 经济
3 大学教授倡导混合式教学改革 教育
4 城市污水处理厂排放监测结果不容乐观 环境
...
3.2 无监督学习:LDA 主题建模
要解决的问题: 当我们没有预先的标签时,如何让模型自动地发现文本集合中潜在的主题分类?这就是主题模型要解决的任务。
LDA 方法简介: LDA (Latent Dirichlet Allocation) 是一种常用的主题模型,它假设每篇文档由若干个主题混合生成,而每个主题是若干词的概率分布。简单来说,LDA 能够根据词语的共现关系,将文档集聚类成若干主题,每个主题对应一组高概率的关键词。与聚类不同的是,一篇文档可以同时属于多个主题(只是比例不同)。
实现思路: 使用 sklearn
的 LatentDirichletAllocation
来进行 LDA。基本步骤:
- 数据预处理:对文本数据进行中文分词处理。
- 特征表示:使用
TfidfVectorizer
将文本数据转化为特征向量。
- 训练 LDA 模型:设置合适的主题数量并训练模型。
- 展示主题关键词:输出每个主题对应的关键词。
- 查看文档主题分布:了解每篇文档所属的主要主题。
代码示例:
from sklearn.decomposition import LatentDirichletAllocation
# 特征向量化
vectorizer = TfidfVectorizer()
X_full = vectorizer.fit_transform(full_df["processed_text"])
# 构建并训练LDA模型
lda = LatentDirichletAllocation(n_components=3, random_state=42)
lda.fit(X_full)
# 显示每个主题的关键词
def display_topics(model, feature_names, no_top_words):
for topic_idx, topic in enumerate(model.components_):
print(f"主题 {topic_idx+1}: ", " ".join([feature_names[i] for i in topic.argsort()[:-no_top_words - 1:-1]]))
display_topics(lda, vectorizer.get_feature_names_out(), 10)
# 预测每篇文档的主要主题
topic_results = lda.transform(X_full)
full_df["LDA_Topic"] = topic_results.argmax(axis=1)
# 输出部分LDA主题预测结果
print(full_df[["新闻标题", "LDA_Topic"]].head(10))
运行结果示例:
主题 1: 升级 生态 带动 监测 项目 海洋 行业 试点 转型 国内
主题 2: 提升 导致 高校 质量 减少 融资 保护 企业 引发 带动
主题 3: 市场 全球 加速 高校 乡村 改革 上升 模式 显示 学生
新闻标题 LDA_Topic
0 高校图书馆尝试夜间自助借还服务 2
1 沿海湿地锐减引发候鸟栖息危机 1
2 民营企业融资环境转暖,投资者信心回升 1
3 大学教授倡导混合式教学改革 1
4 城市污水处理厂排放监测结果不容乐观 0
...
LDA模型的劣势与注意事项:
上述运行结果反映出LDA的典型缺点——主题的含义并不总是十分明确。从结果中我们可以看到:
- 一些关键词在不同主题之间存在重复或重叠,例如“高校”同时出现在主题2和主题3中,使得主题之间的界限模糊。
- 个别主题内部的关键词含义比较发散(如主题2同时涉及“高校”“融资”“企业”“保护”等多个领域),难以形成清晰的主题解释。
- 当数据集中包含过多不同主题的文本时,设定的主题数量不足会导致主题混杂,而增加主题数量又可能使某些主题变得过于琐碎或缺乏解释力。
这些问题在LDA建模中较为常见。为提升主题建模效果,我们通常需要:
- 合理调整主题数量(如尝试不同的
n_components
)。
- 优化文本预处理(如移除停用词、选择更有区分度的特征提取方法)。
- 结合领域知识进行人工干预,帮助更好地解释和定义主题含义。
小结:
监督学习(朴素贝叶斯)适合已有标注数据时进行精确分类;无监督学习(LDA)适合在没有预定义类别时探索潜在的主题结构。但LDA无法完全自动地生成非常明确、易解释的主题,往往需要进一步人工干预或结合其他模型提升效果。在实际应用中,通常会先用LDA进行初步的主题探索,再结合领域专家意见人工明确分类标准,然后再使用监督学习方法进行更准确的分类预测。
4. 关键信息提取
背景与目标: 在公共管理研究或实际办公场景中,我们经常需要从文本中提取特定信息。例如,从政策公告中提取涉及的机构名称、从合同文本中提取金额和日期、从投诉信中提取公司名称或人名等。这类任务可以看作信息抽取的一部分。
常用的关键信息提取方法包括:
- 使用正则表达式(Regular Expression)根据文本模式匹配来提取结构化的信息(如特定格式的日期、数字、邮箱等)。
- 使用命名实体识别(Named Entity Recognition, NER)模型,自动识别人名、地名、组织名、时间、金额等实体。
本节我们将介绍上述两种方法的简要应用:首先通过正则表达式提取公司名称和金额等简单模式的信息,然后演示如何使用 spaCy 库的实体识别功能来提取更一般的实体。
4.1 使用正则表达式提取信息
要解决的问题: 根据事先定义的格式,从非结构化文本中抽取出符合该格式的字符串。
实现思路: Python 内置的 re
模块提供了正则表达式匹配功能。我们需要根据待抽取的信息设计相应的正则模式。例如:
- 公司名称:中文环境下公司名称通常以“公司”或“有限公司”结尾,前面是若干汉字或字母(可能还有“股份”“集团”等关键词)。简单起见,我们以匹配“某某公司”形式的名字为例,模式可用
r"[\u4e00-\u9fa5A-Za-z0-9]+公司"
来匹配由中英文或数字组成并以“公司”结尾的字符串。
- 金额:假设我们关注以“元”或“万元”为单位的金额数字。一个模式可以是
r"\d+\.?\d*万?元"
,表示一串数字(可选小数点及小数部分)后面跟“元”或“万元”。这会匹配诸如“100元”“1000万元”“3.5万元”等。
- 类似地可以为日期、电话等设计模式(例如日期
\d{4}年\d{1,2}月\d{1,2}日
,邮箱[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}
等)。
代码示例: 使用正则从一段文本中提取公司名称和金额:
import re
# 示例文本,其中包含公司名称和金额信息
text = "根据合同,ABC有限公司将在2023年投资1000万元用于社区服务提升项目。另有XYZ科技公司预计投入500万。"
# 正则模式定义
company_pattern = r"[\u4e00-\u9fa5A-Za-z0-9]+公司" # 匹配简易公司名称
money_pattern = r"\d+\.?\d*万?元" # 匹配金额(元或万元)
# 查找所有匹配项
companies = re.findall(company_pattern, text)
moneys = re.findall(money_pattern, text)
print("提取的公司名称列表:", companies)
print("提取的金额列表:", moneys)
代码详解:
- 示例
text
包含两家公司的名字“ABC有限公司”、“XYZ科技公司”,以及金额“1000万元”、“500万”(注意后者缺了“元”字,我们的模式目前要求有“元”,所以可能匹配不到“500万”,稍后会讨论)。
company_pattern
使用正则字符集合:[\u4e00-\u9fa5A-Za-z0-9]
匹配一个中文汉字、英文字母或数字。这个集合带+
号,表示匹配一个或多个上述字符序列。
- 接着
公司
两个字,表示匹配以“公司”结尾的字符串。
- 这个模式会匹配如“ABC有限公司”(包含英文字母和“有限公司”)以及“XYZ科技公司”。它也会匹配比如“123公司”等不太合理的情况,但目前为了演示简单。
- 可以改进模式使其更精确,比如限定必须以“公司”或“有限公司”结尾分别匹配,或者包含“科技”、“股份”等常见公司类型词汇,但那样模式会很长。在不知道具体公司名称格式时,用宽松的模式先提取出来再人工过滤是常见做法。
money_pattern
:\d+
匹配一位或多位数字。
\.?\d*
可选的小数点和小数部分(使我们可以匹配整数和小数金额)。
万?
可选的“万”字,表示金额可能是以万为单位。
元
匹配固定的“元”字。
- 合起来,如:
- "1000万元":\d+ 匹配1000,万?匹配"万",元匹配"元"。
- "3.5万元":\d+匹配3,.?\d*匹配".5",万匹配"万",元匹配"元"。
- "50元":\d+匹配50,万?不匹配任何字符(可为空),元匹配"元"。
- 但对于"500万",因为缺少“元”,我们的模式将不匹配。如果希望匹配没有写元的情况,可以把“元”设为可选:
万?元?
,但要小心这会匹配到一些不完整的东西。视需求调整。
re.findall(pattern, text)
:返回所有非重叠匹配。我们分别找出了公司和金额列表。
- 打印提取结果。
运行结果:
提取的公司名称列表: ['ABC有限公司', 'XYZ科技公司']
提取的金额列表: ['1000万元']
注意输出中只有“1000万元”,因为“500万”没有跟“元”,未被匹配。若我们想包括“500万”,可以将模式改为 r"\d+\.?\d*万?元?"
使“元”成为可选。这里演示的是严格匹配带“元”的金额形式。
正则方法优缺点:
- 优点:对于格式规范的信息(如固定的单词或标点模式),正则表达式非常有效且快速。例如身份证号、电话号码、邮箱这些有明确格式的信息提取,正则几乎是首选。
- 缺点:对自由文本中更灵活的信息(如一般的公司名称、人名)很难写出完全覆盖的正则;正则模式过于复杂时可读性和维护性也差。此外,正则无法“理解”上下文,比如无法准确识别出句子中的组织名是否真的代表公司还是别的东西。
因此,当我们要提取的信息类型模糊或多样时,可以借助机器学习的实体识别来完成。这就是下一小节要介绍的内容。
4.2 使用 spaCy 进行实体识别
要解决的问题: 自动识别文本中的命名实体,如人名、地名、组织机构名、时间日期、货币金额等,而不需要为每种类型手写规则。
实现思路: spaCy
是一个强大的自然语言处理库,内置了预训练的命名实体识别模型(NER)。spaCy对多种语言有支持,包括英文和中文等。使用spaCy进行NER的步骤如下:
- 加载语言模型:对于中文,需要加载中文的模型(如
zh_core_web_sm
)。对于英文,则加载英文模型(如en_core_web_sm
)。
- 处理文本:用模型对文本进行分析,得到一个文档对象,其中包含各种NLP分析结果,包括实体。
- 提取实体:遍历文档中的实体列表,获得每个实体的文本及其类型标签(如PER人名,ORG组织,GPE地名,DATE日期,MONEY金钱等)。
先决条件: 需要安装spaCy及对应语言模型。例如:
pip install spacy
python -m spacy download zh_core_web_sm
模型安装后,可以通过 spacy.load("zh_core_web_sm")
加载。模型大小较大,初次使用需要下载,本手册不直接执行下载。
代码示例: 演示 spaCy 实体识别(假设已安装中文版模型)。我们将对上一小节的示例句子用spaCy来识别其中的实体:
import spacy
# 加载spaCy的中文模型(小型)
nlp = spacy.load("zh_core_web_sm")
# 待处理文本(与前述正则例子相同)
text = "根据合同,ABC有限公司将在2023年投资1000万元用于社区服务提升项目。另有XYZ科技公司预计投入500万。"
doc = nlp(text) # 分析文本
# 提取并打印实体及其类型
for ent in doc.ents:
print(ent.text, ent.label_)
代码详解:
nlp = spacy.load("zh_core_web_sm")
:加载spaCy的简体中文小模型。这一步可能会比较耗时,因为会加载模型的数据(包括分词、POS、NER模型等)。如果没安装会报错,需要先安装模型。
doc = nlp(text)
:对文本进行处理。spaCy会自动进行分词、词性标注、依存分析和实体识别等操作。得到的doc
对象类似于一个包含丰富注解的文本容器。
doc.ents
:文档中的命名实体列表。每个实体对象有属性text
(实体文本)和label_
(实体类别标签)。
- 我们遍历
doc.ents
并打印实体文本和标签。中文模型的实体类型标签可能包括:- ORG: 组织机构
- PERSON: 人名
- GPE: 地名(国家/城市等政治实体)
- DATE: 日期
- MONEY: 金额
- 等等。
预期输出:
运行对上述 text
的实体识别,输出结果类似于:
ABC有限公司 ORG
2023年 DATE
1000万元 MONEY
500万 CARDINAL
解释:
- "ABC有限公司" 被识别为 ORG(组织机构)实体。
- "2023年" 被识别为 DATE(日期)。
- "1000万元" 被识别为 MONEY(金钱)。
- 其他词语如“合同”、“社区服务提升项目”没有被标注为特定实体类型。
可以看到,相比我们手写的正则,spaCy自动提取出了我们关注的公司名和金额,并且甚至识别了日期。这很大程度上减轻了人工规则的负担。不过需要注意:
- 实体识别模型可能会有误识别或漏识别,比如不认识的缩略词、特殊格式内容等。
- spaCy的中文模型对短文本可能不如对长文本有效,因为模型是对大量通用语料训练的,对于特定领域的文本(如政府公文)可能识别准确率一般。另外,小模型的准确率也不如大型模型或者专门的模型。
- NER模型效果取决于训练数据的覆盖面。比如某些新兴词汇、领域特有名称可能无法识别。如果需要高精度,可以考虑对模型进行微调训练,或者结合规则补充。
总结:
- 正则表达式在结构化信息提取上简单高效,但需要对目标信息格式非常了解。
- NER模型能应对更灵活的语言模式,自动化程度高,但受限于模型训练数据,可能需要调整或校正输出。
实际应用中,两者经常结合使用:先用NER模型找出可能的实体,再用正则核验格式,或者在NER未覆盖的特殊格式上用正则弥补。
5. 扩展讨论与总结
通过上述内容,我们实践了中文文本分析中的几个重要方法,从基础的分词到高级的主题模型和实体识别。在公共管理研究中,这些方法可以组合应用于政策分析、舆情监控、文本数据挖掘等场景,为数据驱动决策提供支持。
本手册小结:
- 中文分词:是中文 NLP 的基础步骤,
jieba
提供了便捷的分词功能。掌握分词后,我们可以进行词频统计和词云可视化,快速了解文本关键词分布。
- 情感分析:介绍了基于关键词词典的简单情感判断方法和使用 SnowNLP 的机器学习模型方法。前者实现容易但精度有限,后者更智能但需注意模型适用性。情感分析有助于了解文本(如民众评论、意见)的倾向性,为决策参考提供定性依据。
- 文本分类:展示了监督学习中的朴素贝叶斯分类器如何对简短文本进行分类,以及无监督学习的 LDA 如何发掘隐藏主题。朴素贝叶斯作为入门分类算法速度快效果尚可,而 LDA 则提供了一种在无标签数据上进行探索的途径。
- 关键信息提取:通过正则表达式和 spaCy NER 两种方式提取文本中的结构化信息。正则适合明确格式的信息,NER适合更复杂的语言识别。提取出的关键数据能进一步用于统计分析或关联研究,例如从大量文件中抓取日期和金额以分析投资趋势等。
拓展思考:
上述每个主题都只是相关领域的起步:
- 在分词方面,可以探索自定义用户词典、新词发现、分词歧义消解等提高分词准确率。
- 情感分析可以引入更强大的预训练模型(如BERT情感分析)或训练适合特定领域的模型,构建更加细致的情感类别(如愤怒、喜悦、悲伤等分类)。
- 主题模型除了LDA,还有其他模型(如HDP、NMF)等,可对比不同模型的结果。此外,如何确定最佳主题数、如何命名主题都是值得思考的问题。
- 实体识别在spaCy之外,还有专注于中文的工具(如HanLP、FudanNLP等)或者深度学习方法(如BERT+CRF序列标注)。在信息抽取任务中,还可进一步抽取实体间关系、构建知识图谱,这都是更加高级的内容。
使用建议: 同学们可以基于本手册内容,选取自己的文本数据(比如政府工作报告、新闻评论等)进行尝试。在实践中调整参数、观察结果,将有助于加深对各方法的理解。以下是一些有趣的 GitHub 开源数据集,可供同学在文本分析实践中使用:
- 中文新闻分类数据集:包含大量中文新闻文本,适用于文本分类任务。
- 中文情感分析数据集:包含多种中文情感分析数据,适用于情感分类任务。
此外,以下社区资源也可帮助同学更深入地了解相关工具和方法:
- jieba 分词的 GitHub 主页和使用教程:提供 jieba 分词工具的源码和详细使用说明。
- SnowNLP 项目说明:介绍 SnowNLP 的功能和使用方法,适用于中文文本处理。
- scikit-learn 文档(朴素贝叶斯、LDA 部分):提供 scikit-learn 库中朴素贝叶斯和 LDA 模型的详细文档和示例。
- spaCy 官方文档(尤其是中文模型的注意事项):介绍 spaCy 的使用方法和中文模型的相关注意事项。
一个是工作目录设定,改成自己的;一个是需要先安装对应的小程序,进入dos环境用pip安装,这里涉及到好几个,如jieba、snownlp、pandas等等,import调用的几个程序如果出现问题,一般都是没有安装,pip安装就好;加载spacy中文模型这里,zh_core_web_sm,需要先在dos环境这里下载,C:\Users\Administrator>python -m spacy download zh_core_web_sm