
简介面向需要完成数据挖掘实践任务的在校生这是一套以东野圭吾小说集为分析对象的Python文本挖掘项目涵盖期末大作业、课程设计与毕业设计等典型应用场景可直接作为高分项目参考。项目包含完整源码与文档说明代码附有详细注释新手也能快速上手部署系统功能完善、界面直观且经过严格调试确保可运行具有较高的实际应用价值。压缩包整体约25.78MB以源码和说明文档为核心便于直接下载使用。目前已有518人学习/下载得到导师认可的高分评价可帮助读者快速搭建文本挖掘分析流程理解分词、词频统计、可视化等关键环节并可直接作为课程作业或毕设的基础框架加以扩展有效节省从零搭建的时间。1. 东野圭吾文本挖掘大作业从语料到报告的一次完整闭环每年数据挖掘课程结课的时候总有一批人卡在同一个问题上数据集是现成的模型是调包的但报告交上去老师一句分析深度不够就打回来了。东野圭吾小说集文本挖掘这个题目之所以常被选作大作业是因为它自带三个天然优势语料容易获取、文本长度足够做统计、作者风格有明显变化可以挖出故事。但绝大多数人把作业做成了一次词频统计——画几张词云、贴一个 TF-IDF 热点词表就收工这撑不起高分。这篇笔记要讲的是把文本挖掘从交差做到能讲出道理的完整落地路径从语料清洗、分词调参到主题建模和情感走向每一步都给出可直接复制的代码和参数也把容易翻车的地方提前指出来。适合正在做数据挖掘大作业、期末项目或者想把手头一堆小说文本真正挖出点东西的读者。2. 语料准备与清洗构建能复现的东野圭吾文本文档集2.1 语料从哪来公开来源、自备文件和版权边界做中文文本挖掘大作业第一步不是写代码是先解决有没有干净语料的问题。常见做法有三种按优先级排序使用课程提供的样例语料或教材附带数据能省掉大量清洗时间但缺点是作品不全做风格对比时样本不够。从公开文本库或 GitHub 上的语料仓库获取这类语料往往是别人整理过的格式比较规整但要留意编码是不是 UTF-8很多老仓库给的是 GBK。自备电子书自行抽取这是自由度最高的方式可以按自己需求选作品、定篇章范围但清洗工作量也最大需要处理目录、注释、空白页等噪声。版权方面提醒一句大作业是学习用途语料不建议大规模分发也不要把爬虫写得像在扫站控制请求频率只取自己需要的少量作品。课程答辩时老师看的是方法和结论不是语料库多大。2.2 建立统一语料目录按作品存储的目录约定不管语料从哪来我一般先统一落成这样的目录结构后面每一步都不用来回改路径corpus/ ├── 白夜行.txt ├── 嫌疑人X的献身.txt ├── 解忧杂货店.txt ├── 放学后.txt └── 恶意.txt每部作品一个 TXT 文件文件名即作品名。选 5 到 8 部作品比较合适太少做不了对比太多清洗和标注的时间会失控。大作业的时间预算下6 部左右是最舒服的。2.3 清洗脚本去掉目录、空行和乱码字符拿到 TXT 后第一件事就是清洗。很多网上下载的小说文本带有章节目录、页眉页脚、全角空格和不可见字符这些残留会直接污染后续的分词统计尤其是高频词表里出现一堆第一章目录之类的噪声词答辩时很尴尬。清洗脚本按规则逐行过滤import re def clean_novel(text): # 删除常见垃圾行目录、章节标记、纯数字页眉页脚 lines text.split(\n) cleaned [] for line in lines: line line.strip() if not line: continue # 跳过目录/章节行 if re.match(r^[(]?第[0-9一二三四五六七八九十百千][章节回卷].*, line): continue if re.match(r^\d$, line): # 纯数字页码 continue if len(line) 2: # 单字行大概率是噪声 continue cleaned.append(line) return \n.join(cleaned) with open(corpus/白夜行.txt, r, encodingutf-8) as f: raw f.read() cleaned clean_novel(raw) with open(corpus_clean/白夜行.txt, w, encodingutf-8) as f: f.write(cleaned)这段逻辑的关键在正则那一行第[0-9一二三四五六七八九十百千][章节回卷]能覆盖大部分中文小说的章节标题格式。注意有些电子书的目录不是第X章格式而是直接用XX事件XX人物做章名那种情况可以改用长度阈值加人工抽查。编码统一用 UTF-8 保存后面读入时不用再猜编码。清洗后人工抽查 5 个文件的开头 50 行确认没有整段丢失即可。3. 中文分词与特征提取jieba 加 TF-IDF 的默认搭配3.1 jieba 分词参数精确模式、自定义词典和停用词三层设置中文文本挖掘的第一步是分词。没有分词后面的词频统计、主题模型全部无法进行。常用方案是 jieba速度快、安装简单、中文支持度好对作业项目足够用。import jieba # 加载自定义词典提高人名、专有名词的切分准确率 jieba.load_userdict(dict/novel_dict.txt) # 精确模式分词返回 list words jieba.lcut(cleaned_text)自定义词典是这里最值得花时间的环节。以东野圭吾作品为例如果不加词典汤川学很可能被切成汤川/学加贺恭一郎会被切得乱七八糟。词典格式很简单每行一个词可以带词频和词性汤川学 10 nr 加贺恭一郎 10 nr 东野圭吾 10 nr 白夜行 10 n 解忧杂货店 10 n词频数字建议填 10 左右太高会导致这个词被强制保留反而干扰统计词性标注可写可不写大作业层面写了没坏处。词典内容根据你的语料范围来定选的作品不同人物名词表也不同。一个省事的做法是把所有作品里出现超过 20 次的专有名词列出来人工筛选后加入词典。分词之后还差一步——去停用词。停用词表直接决定高频词的质量。的了是在这类虚词如果不删词频统计前十全是它们词云和图谱都没有意义。建议使用通用的中文停用词表再针对小说文本手动增加一些词比如说道看见知道时候因为所以这类叙事高频但无分析价值的词。stopwords set() with open(dict/stopwords.txt, r, encodingutf-8) as f: for line in f: stopwords.add(line.strip()) # 保留长度 1 的词过滤停用词 final_words [w for w in words if len(w) 1 and w not in stopwords]3.2 TF-IDF 特征提取用 sklearn 做作品间对比分词完成了下一步是特征化。大作业里最常被要求的特征提取方法是 TF-IDF它解决的问题很直接某些词在单篇作品里出现频率高但在全部作品里只有这篇出现说明它是这部作品的标识性词汇。TF-IDF 计算综合了词频和逆文档频率适合拿来做作品风格对比和关键词提取。from sklearn.feature_extraction.text import TfidfVectorizer # 语料列表每个元素是一部作品清洗分词后的文本空格连接 corpus_texts [ .join(tokenize_one_file(corpus_clean/白夜行.txt)), .join(tokenize_one_file(corpus_clean/嫌疑人X的献身.txt)), # ... 依此类推 ] vectorizer TfidfVectorizer(max_features3000, ngram_range(1, 2)) tfidf_matrix vectorizer.fit_transform(corpus_texts) feature_names vectorizer.get_feature_names_out()max_features3000控制只保留 TF-IDF 值最高的 3000 个特征避免维度爆炸ngram_range(1, 2)同时保留单词和二元词组像不在场证明本格推理这类双字词组成的短语可以保留下来。需要注意tokenize_one_file需要自己封装内部做读取、清洗、分词、去停用词四步。拿到 TF-IDF 矩阵后可以打印每部作品里 TF-IDF 值最高的 15 个词这就是答辩时的关键素材import numpy as np for i, title in enumerate(titles): row np.asarray(tfidf_matrix[i].todense()).flatten() top_idx row.argsort()[-15:][::-1] top_words [feature_names[j] for j in top_idx] print(f{title}: {, .join(top_words)})这段输出的价值在于你会明显看到《白夜行》和《解忧杂货店》的关键词几乎不重叠——前者偏暗色调词汇后者偏温暖治愈类词汇。用 TF-IDF 数值作为风格指纹做对比是文本挖掘大作业里最稳的一个亮点。3.3 词云制作让中文不显示成方块的必要设置词云几乎是每份大作业的刚需可视化但中文词云有个必经的坑默认字体不支持中文。直接用 wordcloud 包默认字体会得到一堆方块需要显式指定中文字体路径。from wordcloud import WordCloud import matplotlib.pyplot as plt font_path C:/Windows/Fonts/simhei.ttf # Windows 黑体Mac 用 /System/Library/Fonts/PingFang.ttc wc WordCloud( font_pathfont_path, width1600, height900, background_colorwhite, max_words200, colormapDark2, random_state42 # 固定随机种子保证结果可复现 ) wc.generate( .join(final_words)) wc.to_file(output/wordcloud.png)max_words200控制词云里显示前多少个高频词想过图干净就调到 150 以下。random_state42必须设不设的话每次运行词云布局不同报告里的图没法复现答辩时如果老师问怎么保证结果一致性这就是一个减分项。词云本身信息量不大建议放置在报告的分析章节开头作为视觉概览重点分析放在 TF-IDF 和后面的主题模型上。4. 主题建模与情感分析拉开分数差距的两个分析4.1 LDA 主题建模从困惑度到落地解释的完整做法词频统计和 TF-IDF 只能回答每部作品里什么词多但回答不了作品在讲什么主题。主题建模是对整个语料库进行主题推断的常用手段LDA 是最经典的一种。它的基本假设是每篇文档是多个主题的混合每个主题是多个词的分布。用 LDA 能看到比如侦探推理家庭伦理情感救赎这些主题在各作品里各占多少比例。from sklearn.decomposition import LatentDirichletAllocation from sklearn.feature_extraction.text import CountVectorizer # 用词频矩阵作为 LDA 输入 vectorizer CountVectorizer(max_features5000, token_patternr(?u)\b\w\b) count_matrix vectorizer.fit_transform(corpus_texts) lda LatentDirichletAllocation( n_components5, random_state42, learning_methodbatch, max_iter25 ) lda.fit(count_matrix) # 打印每个主题的 top 词 feature_names vectorizer.get_feature_names_out() for topic_idx, topic in enumerate(lda.components_): top_words [feature_names[i] for i in topic.argsort()[:-11:-1]] print(fTopic {topic_idx}: { .join(top_words)})n_components5是先入为主的主题数初学者建议直接用 5 到 8 试跑然后看每个主题的词列表能不能被一句话命名。learning_methodbatch适合语料量小的场景比在线学习更稳定max_iter25是迭代次数的保守值语料大时可调到 50。关于主题数怎么选大作业层面不需要太复杂。常见做法是分别跑 n_components4、5、6、7人工对比每个主题的 top 词可解释性。如果某个主题的 top 词是一个这个就是这种泛化词说明主题数不合理或分词环节有问题。答辩时能解释清楚我选了哪些主题数比较后留下可解释性最强的这个深度足够拿到不错的评价。LDA 的结果建议再可视化一层。pyLDAvis 是业界标准的交互式主题可视化工具它能展示主题间距离和每个主题的 top 词分布放进报告截图视觉说服力很强。import pyLDAvis.sklearn panel pyLDAvis.sklearn.prepare(lda, count_matrix, vectorizer) pyLDAvis.save_html(panel, output/lda_visualization.html)4.2 情感分析SnowNLP 的中文适配与局限情感分析是给大作业加分的第二个分析维度。东野圭吾的作品跨度很大从《白夜行》的压抑到《解忧杂货店》的温暖用情感倾向做作品对比非常直观。from snownlp import SnowNLP import matplotlib.pyplot as plt sentiments [] for text in whole_texts: # whole_texts 是每个作品清洗后的全文 s SnowNLP(text) sentiments.append(s.sentiments) # 返回 0~1 的情感倾向值0.5 为中性SnowNLP 的情感值输出范围是 0 到 1越接近 1 越正向。但注意直接对整本小说做情感分析会有均值回归问题——一本 20 万字的书无论多黑暗平均情感值也会落在 0.4 到 0.6 之间区分度不高。更有效的做法是分段计算按章节或按固定长度切分画出整本书的情感曲线对比《白夜行》和《解忧杂货店》的曲线形态差异。import re def split_by_chunks(text, chunk_len2000): return [text[i:i chunk_len] for i in range(0, len(text), chunk_len)] chunks split_by_chunks(whole_texts[0]) chunk_scores [SnowNLP(c).sentiments for c in chunks] plt.figure(figsize(12, 4)) plt.plot(chunk_scores, linewidth1.2) plt.xlabel(文本分块序号) plt.ylabel(情感倾向值) plt.title(情感走向曲线) plt.savefig(output/sentiment_curve.png, dpi200)需要明确的是SnowNLP 的情感模型基于电商评论语料训练对文学文本的判断存在偏差比如文学性反讽、暗黑叙事可能被误判为中性或正向。所以这部分更适合展示相对差异不要过分依赖绝对值。报告中建议写一句情感分析作为辅助维度结果供参考与主题模型结论互证老师挑不出毛病。4.3 人物共现网络把文本挖掘升级成关系挖掘东野圭吾的作品非常适合做人物共现分析因为侦探小说人物关系是核心。共现网络的思路是如果两个人物出现在同一段落或同一章节里就认为一次共现统计共现次数再用 NetworkX 画关系图。这能让大作业从文本统计上升到结构挖掘是拿高分的一个重要加分项。from collections import Counter, defaultdict import networkx as nx # char_dict: 自定义词典里的人物名列表 co_occur defaultdict(int) # 按段落处理每段内统计人物共现 for paragraph in paragraphs: chars_in_para [c for c in char_dict if c in paragraph] chars_in_para list(set(chars_in_para)) for i in range(len(chars_in_para)): for j in range(i 1, len(chars_in_para)): pair tuple(sorted([chars_in_para[i], chars_in_para[j]])) co_occur[pair] 1 G nx.Graph() for (c1, c2), weight in co_occur.items(): if weight 5: # 只保留共现次数 5 的边去噪 G.add_edge(c1, c2, weightweight)阈值设为 5 可以过滤一大批随机共现的噪声边实际项目中 3 到 8 之间都可以试。画图时用节点大小代表人物的总出现次数用连边粗细代表共现强度这样一眼能看出谁是核心人物。答辩时这个图放在 TF-IDF 热词表旁边形成词与关系两个层面的对照分析深度就出来了。5. 避坑清单文本挖掘大作业的编码、词典和可视化重灾区文本挖掘大作业的翻车点高度集中以下五条是我见过最多的踩坑现场每条都按现象→原因→解决写清楚。5.1 读文件时 UnicodeDecodeError编码不统一现象开着 utf-8 读文件报错UnicodeDecodeError: utf-8 codec cant decode byte。原因语料文件是 GBK 或 GB18030 编码常见于从老网站或论坛下载的 TXT。Windows 简体中文环境下很多文本编辑器默认用 ANSI即 GBK保存这段经历可称为玄学现场。解决读取时先尝试 GB18030 解码。def read_text_safe(filepath): for enc in [utf-8, gb18030, gbk]: try: with open(filepath, r, encodingenc) as f: return f.read() except UnicodeDecodeError: continue raise ValueError(f无法解码文件: {filepath})统一规范是清洗后全部转存为 UTF-8后续代码固定用 UTF-8 读写不再逐次猜编码。5.2 高频词全是人称代词停用词表太薄现象词频统计 top 20 里全是他她的了在分析无从下手。原因通用停用词表只覆盖了常见的虚词小说文本里大量叙事功能词没有被过滤。尤其是他/她这类代词在小说里频率极高但它们不携带风格信息。解决在通用停用词表基础上针对小说文本增量补充一个停用词集合包含说道知道看见时候已经过来什么没有这样那样一下等词。更彻底的做法是用词性过滤只保留名词、动词和形容词用 jieba.posseg 做词性标注后按词性筛选。import jieba.posseg as pseg allowed_flags {n, nr, ns, v, a, vn} words [w.word for w in pseg.cut(text) if w.flag in allowed_flags and len(w.word) 1]注意区分两种方案的适用场景词性过滤会丢掉一些值得分析的双字词比如当然或许这类语气副词它们在风格分析里有价值。如果重点看词义关键词用词性过滤如果做风格对比保留副词更好。5.3 词云中文全是方框缺少字体路径现象词云生成成功图里所有中文都是空心方块。原因wordcloud 默认字体不支持中文没有指定 font_path 时系统找不到中文字体。解决在 Windows 上指定C:/Windows/Fonts/simhei.ttf黑体或simsun.ttc宋体macOS 上指定/System/Library/Fonts/PingFang.ttcLinux 系统先安装文泉驿或思源黑体。一个稳妥办法是把字体文件复制到项目目录下的assets/文件夹用相对路径引用换机器跑不依赖系统字体。wc WordCloud(font_pathassets/fonts/simhei.ttf, ...)5.4 LDA 主题词无法命名没先做充分清洗现象LDA 输出的每个主题 top 词都是这个怎么一个里没有任何可解释性。原因去停用词不彻底虚词和泛化动词占了词频优势LDA 抓不到语义特征。这与 5.2 的问题同源但表现更严重。解决重点检查自定义停用词表是否覆盖了叙事通用词以及是否做了词性筛选。另外一个常见原因是语料长度太短很多大作业只收集了每部作品的开头章节样本量不足导致 LDA 的主题推断不稳定。建议每个作品至少保留全文的三分之一以上篇幅。5.5 Matplotlib 中文乱码或警告全局字体未设置现象绘图标题和图例中文显示为黑色方块控制台输出Glyph missing from current font警告。原因matplotlib 默认字体不含中文字形需要设置字体家族。解决在绘图脚本开头统一设置plt.rcParams[font.sans-serif] [SimHei] # 或用 [PingFang SC] plt.rcParams[axes.unicode_minus] False # 解决负号显示为方块这个配置要放在所有绘图代码之前否则部分图生效部分不生效排查起来很费时间。6. 验证方法与进阶拿可复现和可解释保底冲高到大作业的最后阶段代码能不能跑通已经不是问题了问题变成了怎么证明你的分析是对的。文本挖掘的结果没有绝对正确但有相对可靠的验证思路。这套验证方法的价值在于它在答辩时能帮你挡住一半以上的追问。6.1 三个验证层次第一层是统计合理性。检查分词结果里词的个数、每部作品的平均长度、词频和 TF-IDF 的排序是否符合直觉。比如《嫌疑人 X 的献身》里数学应该排很前《白夜行》里剪刀雪穗应该是标识词。如果排序结果和作品标签高度吻合说明语料清洗和分词是有效的这套流程可以支撑你的结论。第二层是跨方法一致性。LDA 主题词、TF-IDF 关键词和人物共现网络的结论应当互为补充而不是互相矛盾。如果 LDA 跑出悬疑推理主题TF-IDF 里应该有案件线索嫌疑等词呼应。这种交叉验证是数据挖掘报告最重要的可靠性支撑。第三层是维度稳定性。把语料分成前一半和后一半分别做 TF-IDF对比两部半语料的关键词重合率。重合率超过 60% 说明结果稳定如果差异巨大就要考虑是不是语料本身混合了多种风格或清洗不够均匀。6.2 交付物组织建议源码和文档说明是这个题目标注里的两个交付物。源码部分建议按以下结构组织project/ ├── data/raw/ # 原始语料 ├── data/clean/ # 清洗后语料 ├── code/ │ ├── 01_clean.py │ ├── 02_segment.py │ ├── 03_tfidf.py │ ├── 04_lda.py │ ├── 05_sentiment.py │ └── 06_co_occur.py ├── output/ # 图表统一命名 ├── dict/ │ ├── novel_dict.txt │ └── stopwords.txt └── README.md脚本按 01 到 06 编号每步只输出中间结果后一步读前一步的产物这样每一步可以单独重跑答辩演示时可以按顺序跑给老师看。输出图表统一用英文命名加日期后缀tfidf_top_words_20250601.png。README.md 里写明运行环境Python 3.10、核心依赖版本、语料来源和数据清洗规则这是文档说明部分的基本盘。6.3 最后一个加分技巧建立作品-主题-情感三维表把每部作品的主题分布、TF-IDF 关键词、平均情感值三组数据放到同一个表里一页纸展示全局这是报告里最能体现数据挖掘思维的一页。表格形式大致如下作品主导主题核心词 Top 5平均情感值白夜行案侦探案/生存挣扎雪穗、亮司、桐原、剪刀、嫌疑0.43解忧杂货店亲情/人生选择杂货店、咨询、浪矢、梦想、信0.65恶意嫉妒/谎言野野口、日高、凶手、手记、作案0.38这张表本身就是答辩的开场结构从主题差异、到关键词差异、再到情感差异三段式把东野圭吾不同作品间的风格跨度讲完整。把为什么用这些方法放在实验设计部分结果之间的内在关系放在分析部分报告的完整度就远超普通作业了。文本挖掘大作业做完一遍最有价值的收获不是代码多漂亮而是建立了一套从原始文本到可解释结论的处理链路。我自己的习惯是每一次跑完都把终端截图和中间产物留存下来答辩前回看一遍很多细节当场就能想起来。最后提醒一句所有图表写上生成时间、所用版本、随机种子这在答辩时能省掉大量结果能不能复现的追问。希望这些经验能帮你的东野圭吾文本挖掘项目少走弯路稳稳拿到应得的评价。本文还有配套的精品资源点击获取