Python中文文本分析实战:从分词到情感分析的完整流程
简介这份Python中文文本分析期末大作业压缩包面向高校学生及文本挖掘初学者以《三国演义》为分析对象完整演示了中文分词、词频统计、词云绘制、人物出场排序、社交网络关系建模与章回字数统计等典型任务。包内共13个文件涵盖Python源码、可执行程序、可视化图表、报告文档及原始文本其中main.py与main.exe方便直接运行html与png展示词云、关系网络等结果docx和pdf则为课程报告与说明材料整体约406.57MB结构清晰、开箱可用。资源已有6645人学习内容兼顾编程实践与数据分析思路适合期末参考、课设复现或个人自学。通过研读源码和报告可掌握jieba分词、wordcloud词云、networkx关系图等关键技术并理解从文本清洗到可视化输出的完整流程是一份能直接启发中文文本分析项目设计的综合案例。1. Python中文文本分析期末大作业先别急着找代码说清要交什么再说“Python中文文本分析”这类期末大作业几乎每个和数据处理沾边的专业都会碰到一次。表面看是让你写一个程序把一批中文文本做分词、词频统计、情感分析和可视化最后提交代码、数据、报告和一份能运行的环境清单。实际上一眼看出差距的地方往往是“文件打不开、中文乱码、词云一片方块”这些最草根的问题。这篇笔记按一个能答辩的方案去拆讲清楚选型理由和具体参数再把踩坑记录一并列出来。无论是零基础第一次做文本分析还是想给已有课程设计补分这套流程都能直接对齐。2. 中文文本分析为什么不能照搬英文方案分词、编码与停用词三座山文本分析如果只处理英文一个字符串的 split() 就能基本解决问题。中文不一样一句话几十个字连在一起没有空格作为天然边界同一个词在不同语境里还可能被切成不同长度。所以中文文本分析的第一个技术决策不是模型而是分词器怎么选。分词、编码、停用词我习惯把这称为三座山任何一座没处理好后面的词频、词云和情感分析全是在垃圾数据上做验收。2.1 没有天然空格jieba 分词模式与三个关键参数最常用的开源分词工具是 jieba。它不是靠固定词典硬切而是在词典匹配的基础上用 HMM 模型识别词典之外的新词。日常文本分析的精度够用安装简单API 稳定所以课程作业和快速原型基本都是它。先用一句话区分三种模式再看表格。模式调用方式返回特点适合场景精确模式jieba.lcut(text)把句子切成一串最小且不重叠的词词频统计、关键词提取作业默认选它全模式jieba.lcut(text, cut_allTrue)把所有可能词都扫一遍产生重叠调试词典覆盖不适合直接统计搜索引擎模式jieba.lcut_for_search(text)在全模式基础上补齐长词搜索场景作业里偶尔用于对比展示import jieba text Python中文文本分析期末大作业 words jieba.lcut(text) print(words) all_mode jieba.lcut(text, cut_allTrue) print(all_mode) search_mode jieba.lcut_for_search(text) print(search_mode)这三个打印结果放在一起看能直观理解精确模式为什么是默认选择。lcut 返回的是 list比原来的 cut 返回生成器更方便后续直接接 Counter 做统计也不会搞错对象。cut_all 参数默认是 False也就是精确模式HMM 参数一般不需要手动改保持默认即可。实际作业里记住精确模式就够。全模式跑出来的词频统计往往把同一个内容重复算好几遍反而让结果失真答辩时老师一问就露馅。2.2 字符编码黑匣子一样的 GBK 和 UTF-8 混战中文文本分析的第二个坑发生在数据读入之前。编码不是 Python 的问题但足够让 Python 程序“莫名”崩溃。常见场景是 Windows 下用记事本保存文件可能被存成 ANSI/GBK从网页 API 或 Linux 服务器拿到的文件通常是 UTF-8。两者混用就会遇到“明明数据没问题一读就读不出来”的情况。import chardet # 读前 10000 字节去猜编码猜错也比瞎赌好 with open(data.txt, rb) as f: raw f.read(10000) encoding chardet.detect(raw)[encoding] print(encoding)chardet 返回的结果不一定精确尤其对短文本所以它更适合用来做初步判断。更稳的做法是拿到文件后先统一转成 UTF-8 再进 pandas我实际用得最多的还是指定 encoding 参数去读而不是依赖系统默认。提示Windows 记事本另存为时“UTF-8”和“UTF-8 BOM”是两回事。pandas 用 encodingutf-8 读带 BOM 的文件第一列经常出现一个看不见的 BOM 字符处理时要用 encodingutf-8-sig 读取。2.3 停用词表词频排行第一的永远是“的”这不是分析结果中文里有一批高频但没有信息量的词比如“的、了、是、在、和、与”。如果不剔除它们会占满高频词前二十真正的主题词被挤到后面整个词频统计失去解释力。公开的中文停用词表有不少常见的是哈工大停用词表和百度停用词表大小从几百词到两千词不等。但领域词必须自己补。新闻语料里要加“记者、编辑、来源”评论语料里要加“哈哈、真的、感觉、觉得”。这份 stopwords.txt 建议自己动手维护同一份文件对多批语料反复使用这也是期末作业里特别能体现细节的地方。后面所有词频统计都默认先过停用词。处理顺序一般是正则清洗、分词、过滤停用词、统计顺序不要颠倒。先统计再过滤会导致计数多算一次而且某些词会被标点和前后缀污染清洗效果大打折扣。3. 搭一套能交差的流程从语料到词云的最小可执行方案原理部分讲完下面进入能跑的阶段。我倾向于按“环境、数据、分词、可视化”四步走每一步的产物都是下一步的输入。这套流程同样适用于自建项目区别只是数据源不同。对期末大作业来说能把这一步跑通就已经拿到了大部分基础分。3.1 环境准备用 venv 和这 6 个库避免版本翻车第一个建议是不要把包直接装进系统 Python。零基础入门教程通常推荐 Anaconda但做期末大作业我反而建议用 venv 或 conda 建独立环境。原因很实际交作业时通常要写依赖清单独立环境能精确导出 requirements.txt不会把系统里无关的包带进去。另一个常见翻车点是版本。Python 3.13 发布后部分依赖编译的库还没有对应 wheel建议锁定 3.9 到 3.12其中 3.10、3.11 最稳。python -m venv nlp_homework # Windows 激活 nlp_homework\Scripts\activate # macOS / Linux 激活 source nlp_homework/bin/activate pip install jieba pandas wordcloud matplotlib snownlp chardetpython 安装教程里常说要加镜像源如果 pip 下载慢可以在命令末尾加-i https://pypi.tuna.tsinghua.edu.cn/simple这不影响最终代码运行。激活虚拟环境后命令行里的 python 指向的是项目内解释器和全局环境隔离。如果用 VSCode 写代码装完依赖后还要在解释器选择器里指到nlp_homework\Scripts\python.exe否则会出现“命令行激活了、编辑器里还是全局解释器”的诡异情况。这个是 python 环境配置里最常见的跳坑点我见过不止一个人在这上面浪费一晚上。3.2 数据准备CSV 还是 TXT先解决一列文本的清洗问题语料怎么来取决于作业给的是什么。常见的是新闻标题、商品评论、影评、问卷开放性题目。有一个干净的短文本列表后面流程会顺很多。如果作业不限定数据源我一般建议把数据存成 CSV至少两列一列 id一列 text一列可选的 label。这个结构够用也能支持后面做类别对比分析。import pandas as pd # 读 CSV如果报 UnicodeDecodeError先试着改成 encodinggbk df pd.read_csv(data.csv, encodingutf-8) df df.dropna(subset[text]) print(df.head()) # 没有现成文件时用这个最小语料也能跑通全流程 sample_texts [ 期末考试临近大家都在准备Python数据分析与可视化作业。, 中文文本分析需要用分词工具最常用的是jieba。, 停用词表对词频统计影响很大不过滤全是虚词。, 词云图需要指定中文字体否则看到的都是方框。, 情感分析可以告诉老师这条评论偏正面还是负面。 ] texts df[text].tolist() if text in df else sample_textsdropna(subset[text]) 会把正文为空的行直接丢掉避免后面分词时对 NaN 操作报错。text in df是判断列名是否存在如果字段名不对会自动走示例语料不至于一运行就中断。真实作业里建议把这段改成明确的列名判断省得示例数据混进正式结果。3.3 分词与停用词过滤把句子切碎并且去掉噪音分词函数是整个流程里最需要稳定的部分。思路是先只保留中文、英文、数字再用精确模式切词最后过滤停用词和单字。正则里的[^\w\u4e00-\u9fa5]在 Python3 下已经是“保留单词字符和中文”的写法因为 \w 本身会匹配中文多写 \u4e00-\u9fa5 只是把意图写得更明确。import jieba import re # 自定义词典一行一个词格式词 词频 词性 # jieba.load_userdict(userdict.txt) stopwords set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: word line.strip() if word: stopwords.add(word) def clean_and_cut(text): text re.sub(r[^\w\u4e00-\u9fa5], , str(text)) words jieba.lcut(text) result [] for w in words: if w.strip() and w not in stopwords and len(w.strip()) 1: result.append(w) return result all_words [] for t in texts[:2000]: all_words.extend(clean_and_cut(t))开发阶段先用texts[:2000]切片跑通逻辑没问题再放开全量这是一个避免反复浪费时间的原则。停用词表用 set 而不是 list成员判断 O(1)几万条文本跑下来差距明显。len(w.strip()) 1会把单字直接过滤因为词频统计里单字通常只是噪音除非你是做汉字级别的分析。停用词表里没有的词比如“作业”这种领域词如果不想进停用词也不用管。真正要处理的是希望被识别成整体但被拆开的词那个用 load_userdict 或 add_word 解决后面避坑章节会专门讲。3.4 词频统计与可视化词云加柱状图成果图就有了词频统计用 Counter 一行就能排好。柱状图展示 Top30词云展示 Top100两张图放进报告代码量并不大但视觉效果立刻完整。注意 matplotlib 默认字体不含中文不设置字体的话坐标轴标签全是方块。from collections import Counter counter Counter(all_words) top30 counter.most_common(30) print(top30[:10]) import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] plt.rcParams[axes.unicode_minus] False words [w for w, _ in top30] freqs [f for _, f in top30] fig, ax plt.subplots(figsize(12, 6)) ax.bar(range(len(words)), freqs, color#4C72B0) ax.set_xticks(range(len(words))) ax.set_xticklabels(words, rotation45, haright) ax.set_ylabel(词频) plt.tight_layout() plt.savefig(top30.png, dpi150) plt.show()plt.rcParams 两行是全局设置把默认字体切到黑体同时把负号显示修好否则图表里出现减号会变方块。rotation45 和 haright 是让中文标签斜着排避免横轴重叠。tight_layout 自动收缩留白savefig 的 dpi150 足够放进报告打印。词云部分同样要注意字体WordCloud 默认的字体对中文不友好必须手动指定 font_path。from wordcloud import WordCloud wc WordCloud( font_pathC:/Windows/Fonts/simsun.ttc, width800, height600, max_words100, background_colorwhite, colormapviridis, max_font_size120, random_state42 ) wc.generate_from_frequencies(dict(counter.most_common(100))) plt.figure(figsize(10, 6)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(wordcloud.png, dpi150, bbox_inchestight) plt.show()font_path 指向系统里的一个中文字体文件Windows 常见宋体是 simsun.ttc黑体是 simhei.ttfmacOS 放在 /System/Library/Fonts/PingFang.ttc。max_words 控制词云里最多显示多少词词太多会糊成一片。colormap 控制配色viridis 是默认里不容易翻车的方案。random_state42 固定随机种子保证每次运行生成的词云形状一致写报告和答辩演示时不跳变。4. 期末大作业常见问题排查六个跑不通的坑和对应解法能跑的流程已经有了下面这些坑是我见过最多的几乎每个小组作业都会踩中至少一条。每条按现象、原因、解决列出来方便对照排查。4.1 读 CSV 报 UnicodeDecodeError 乱码现象pd.read_csv(data.csv)一运行就报错提示gbk codec cant decode byte 0x...。原因文件是 UTF-8 编码但 Windows 下 pandas 默认用 GBK 去解码或者反过来文件是 GBK 而代码写了 utf-8。解决先确定文件编码再指定。try: df pd.read_csv(data.csv, encodingutf-8) except UnicodeDecodeError: df pd.read_csv(data.csv, encodinggbk)这个 try except 拦截是实用做法但不完美如果两种解码都失败说明文件本身混入了特殊字节。这个时候用记事本打开文件另存为 UTF-8 再跑一次比在代码里绕来绕去更省时间。4.2 词云全是方块或者直接报字体异常现象词云生成了但每个字都是一个空心小方块或者运行时报cannot load font。原因WordCloud 默认的字体不包含中文字形系统里找不到匹配的字体文件就变成占位符。解决在 WordCloud 里显式指定中文字体路径。Windows 用C:/Windows/Fonts/simhei.ttfmacOS 用/System/Library/Fonts/PingFang.ttcLinux 可以检查 /usr/share/fonts 下有没有中文字体。代码里先判断文件存在再传进去比直接写死路径更稳。4.3 分词结果和预期差一大截比如“文本分析”被切开现象高频词结果里有“文本”也有“分析”但就是没有“文本分析”这个整体词。原因jieba 的默认词典里对这个词的词频权重不够或者中英混排让切分走了另一条路。解决自定义干预。import jieba jieba.add_word(文本分析, freq50, tagn) words jieba.lcut(中文文本分析方法) print(words)add_word 适合临时加一个词。如果词表很长用 load_userdict 加载文本文件更合适文件每行一个词格式是“词 词频 词性”。注意自定义词必须在 lcut 之前加入切分是即时计算的结果。4.4 全量跑几万条文本卡到怀疑人生现象代码在分词阶段跑了很久没反应内存占用持续上涨。原因中文分词本身是逐句处理不像矩阵运算可以批量加速正则加集合过滤在高数据量下也会堆积耗时。解决开发阶段先用texts[:2000]跑通确认效果后再全量运行。全量跑时可以在循环里每 5000 条打印一次进度不然只能干瞪眼。for i, t in enumerate(texts): all_words.extend(clean_and_cut(t)) if (i 1) % 5000 0: print(f处理进度: {i 1}/{len(texts)})词云和柱状图不必每次都从全量数据重跑先用抽样结果把图调好看最后跑一次全量做正式数据这是很实用的节奏。4.5 报告只有一张词云老师说不够深入现象代码全对图也美但报告交上去被打回评语写着“分析性不足”。原因词云只是词频统计的可视化缺少问题定义和分析结论。老师要看到的是从问题出发、用数据回答问题的过程不是程序运行截图。解决至少补三类内容高频词排序柱状图、按类别或时间对比词频、情感分布结果。情感和关键词扩展在下一章展开。5. 从合格到优秀的三个加分方向情感分析、关键词提取与报告结构词频和词云是基础分要把档次提上去最快的是加情感分布和关键词权重分析。这两项代码量不大但能明显让报告有“分析”的骨架。更重要的是答辩时老师问“你的分析说明什么”你手里有具体数字可以回答。5.1 用 SnowNLP 做情感分布如果语料带主观性比如微博、评论、影评情感分析是最好实现的加分项。SnowNLP 是基于朴素贝叶斯的中文情感分析库调用很简单。但要知道训练语料偏电商购物评论对新闻类文本的结果会比较偏中性使用时要在报告里说明。from snownlp import SnowNLP texts_sample texts[:500] scores [] for t in texts_sample: s SnowNLP(t) scores.append(s.sentiments) pos sum(1 for s in scores if s 0.6) neg sum(1 for s in scores if s 0.4) mid len(scores) - pos - neg print(f正面 {pos}中性 {mid}负面 {neg}) labels [positive, neutral, negative] values [pos, mid, neg] plt.pie(values, labelslabels, autopct%1.1f%%) plt.savefig(sentiment.png, dpi150) plt.show()sentiments 取值在 0 到 1 之间越接近 1 越正面。阈值 0.6 和 0.4 是根据语料情况调整的经验值在报告中写明“本文以 0.6 和 0.4 为阈值划分三分类并经过抽样校验”这句话本身就是加分项。如果语料是新闻标题正负占比通常会集中在中性附近这很正常说明数据特性要单独讨论不要硬套结论。5.2 用 TF-IDF 关键词提取比词频统计更有说服力词频统计体现的是“出现得多”TF-IDF 体现的是“在这份语料里相对重要”。一个词如果每篇文章都出现它的 IDF 会很低只有集中在部分文章中出现的词权重才会凸显。用 jieba 的 analyse 模块可以直接算。import jieba.analyse full_text .join(texts[:500]) keywords_tfidf jieba.analyse.extract_tags( full_text, topK20, withWeightTrue ) for word, weight in keywords_tfidf: print(word, weight) keywords_tr jieba.analyse.textrank( full_text, topK20, withWeightTrue, allowPOS(ns, n, vn, v) ) for word, weight in keywords_tr: print(word, weight)extract_tags 默认算法就是 TF-IDF。topK 控制返回数量withWeightTrue 会同时输出权重值这是画对比表的基础。textrank 是另一种算法基于词图网络计算重要性allowPOS 限制了参与计算的词性通常只保留名词和动词效果会比全词性更干净。作业里把词频榜、TF-IDF 榜、TextRank 榜放到一个表格里做对比并说明为什么“出现次数多”和“重要”不是一回事这就是很扎实的分析内容。5.3 报告结构参考背景、数据、方法、结果、不足五段期末大作业报告不需要长篇大论但要有完整逻辑链。我见过很多报告把大量篇幅花在介绍 Python 是什么上这没有意义。真正该写的是下面这五块。章节内容要点常见错误背景想解决什么问题为什么用文本分析从 Python 发展史写起数据来源、条数、预处理方案不写编码处理和清洗方法分词器选型、停用词表、统计方案只写库名不给参数结果词云、柱状图、情感分布、对比表只有一张词云图不足数据量、模型偏差、人工标注缺失不写不足怕暴露问题答辩问题通常不在代码本身而在“你这个词频说明了什么”这类问题上。情感分布给了你一个相对客观的结论TF-IDF 给了你一组可以解释的权重两者都能支撑起一段像样的讨论。这也是我强烈建议把扩展分析写进报告的原因。6. 给词云换一张形状掩膜图片的完整步骤与参数6.1 用 PIL 读取一张黑白剪影作为词云轮廓词云默认是一个矩形但如果想让视觉呈现更有记忆点比如把词云做成爱心、头像、项目 Logo 的形状只需要准备一张黑白剪影图在生成时把图片传给 mask 参数。这个技巧对最终展示效果提升非常明显操作也不复杂。from PIL import Image import numpy as np mask np.array(Image.open(mask.png)) # 处理掉可能存在的灰度保留纯黑和纯白两个值 mask np.where(mask 128, 255, 0).astype(np.uint8) wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, maskmask, width800, height600, background_colorwhite, contour_width2, contour_colorsteelblue, max_words80, random_state42 ) wc.generate_from_frequencies(dict(counter.most_common(80))) plt.figure(figsize(10, 8)) plt.imshow(wc) plt.axis(off) plt.savefig(wordcloud_mask.png, dpi150) plt.show()mask 里白色部分代表留白黑色部分是词云填充的区域。如果发现文字跑到背景里说明图片的颜色反了用mask 255 - mask反转一次就能解决。contour_width 和 contour_color 会给形状轮廓加一条描边视觉上更干净。生成的剪影图用常见图形软件导出 PNG 即可注意尺寸不要太小建议不低于 800x600不然词云里的文字会糊。最后说一个我自己踩过的细节。期末展示那次我的词云背景是灰扑扑的色块老师随口问了一句“是不是字体加载失败了”。当时我检查了很久才发现是掩膜图片本身带了反锯齿边缘有一圈半透明灰度程序处理时没有把它二值化干净。后来在代码里加了np.where(mask 128, 255, 0)这一步问题一次性解决。这个习惯沿用到现在凡是涉及图像生成的环节先确认数据只有黑和白两个值再去调字体和配色。希望帮到你。本文还有配套的精品资源点击获取