资讯动态

东野圭吾小说集文本挖掘:Python中文NLP大作业全流程实战

发布时间:2026/10/13 19:41:20 来源:尧图企业网站定制
简介这份资源是面向高校学生与Python初学者的数据挖掘课程项目以日本作家东野圭吾的小说集为分析对象适合用作期末大作业、课程设计或毕业设计的参考方案。项目围绕文本挖掘展开涵盖文本预处理、词频统计、关键词提取、情感分析与可视化等典型环节代码注释详尽新手也能逐步理解整体流程。压缩包为zip格式大小约25.78MB内含完整源码与配套文档说明源码可直接部署运行文档则帮助读者快速把握项目结构与实现思路。目前已有519人学习下载说明该方案在同类作业中具有一定参考价值。对于需要完成数据挖掘大作业的读者而言这份资源提供了可复用的分析框架与清晰的代码组织方式既能作为提交模板也便于在此基础上替换数据集、调整分析维度从而形成自己的项目成果。1. 东野圭吾小说集文本挖掘一份大作业怎么做出可复现的工程味很多人做「基于 Python 数据挖掘大作业」时第一反应是找一份现成源码改改交差结果答辩时被问「你的停用词表怎么来的」「TF-IDF 权重为什么这么排」就当场卡壳。这份东野圭吾小说集文本挖掘项目真正值钱的地方不是那几百行代码而是它把「中文文本挖掘」这条链路完整走了一遍从原始 txt 清洗、分词、去停用词到词频统计、关键词抽取、人物关系共现、情感倾向最后落到可视化图表和文档说明。它适合两类人一类是选修了数据挖掘、NLP 入门课需要一份能讲清楚每一步为什么这么做的课程设计另一类是想用 Python 练手真实中文语料但苦于找不到体量合适、版权干净、结构规整的数据集。东野圭吾的作品集恰好满足——单本篇幅适中、人物命名规律、叙事结构清晰做共现网络和角色分析时不会像《红楼梦》那样人物爆炸也不会像短篇集那样样本太碎。下面我按自己带学生做这类作业的顺序把整条链路拆开讲。2. 语料准备与清洗把 txt 变成能喂给算法的干净文本2.1 为什么中文文本挖掘第一步永远是清洗拿到东野圭吾小说集的原始 txt你会发现它远没有想象中干净。常见的问题包括章节标题混在正文里、页眉页脚残留、全角半角标点混杂、对话引号不统一、还有从不同来源拼凑导致的编码不一致。如果直接把这些丢进 jieba 分词你会得到一堆「第」「章」「页」这种高频噪声词TF-IDF 排序前列全是它们关键词抽取直接废掉。所以清洗不是可选项是决定后续所有分析质量的地基。我一般把清洗拆成四步统一编码、去除非正文行、标点归一化、分章存储。统一编码用chardet探测后统一转 UTF-8去除非正文行靠正则匹配「第X章」「Page」「页码」这类模式标点归一化把全角引号、省略号统一方便后续按句子切分分章存储则是为了后面做「单本 vs 全集」的对比分析。这四步做完语料才算能用。2.2 清洗与分章的实操代码import re import os import chardet def detect_and_read(path): 探测编码并读取避免中文乱码 with open(path, rb) as f: raw f.read() enc chardet.detect(raw)[encoding] # 有些探测结果不准兜底用 gbk try: return raw.decode(enc) except (UnicodeDecodeError, TypeError): return raw.decode(gbk, errorsignore) def clean_text(text): 去页眉页脚、统一标点、压缩空行 # 去掉常见的页眉页脚模式 text re.sub(r第\s*\d\s*页, , text) text re.sub(rPage\s*\d, , text, flagsre.I) # 全角标点归一化 trans {“: , ”: , ‘: , ’: , ……: ...} for k, v in trans.items(): text text.replace(k, v) # 压缩连续空行 text re.sub(r\n{3,}, \n\n, text) return text.strip() def split_chapters(text): 按“第X章”切分返回 [(章节名, 正文), ...] pattern re.compile(r(第[一二三四五六七八九十百零\d]章[^\n]*)) parts pattern.split(text) chapters [] # parts[0] 是第一章前的序言通常丢弃 for i in range(1, len(parts), 2): title parts[i].strip() body parts[i 1].strip() if i 1 len(parts) else if len(body) 50: # 过滤空章节 chapters.append((title, body)) return chapters if __name__ __main__: raw detect_and_read(higashino.txt) cleaned clean_text(raw) chapters split_chapters(cleaned) print(f共切出 {len(chapters)} 章)这段代码的关键在split_chapters的正则第[一二三四五六七八九十百零\d]章同时兼容中文数字和阿拉伯数字章节号因为不同来源的 txt 格式不统一。parts用捕获组切分后奇数位是章节标题偶数位是正文这个技巧比逐行判断更稳。参数上len(body) 50这个阈值是过滤掉目录页残留的假章节实际语料如果章节很短可以调到 30。chardet探测偶尔会把 GBK 误判成别的编码所以加了 try-except 兜底这是中文语料处理的血泪经验——不兜底迟早翻车。提示清洗后建议先人工抽查 3 到 5 章确认没有把正文误删。正则清洗最大的风险是「删过头」尤其是页眉页脚模式和正文里的「第X页」引用撞车时。3. 分词、停用词与词频统计中文文本挖掘的核心工序3.1 jieba 分词模式怎么选停用词表怎么建中文不像英文有天然空格分词质量直接决定后续所有统计的可信度。jieba 提供三种模式精确模式、全模式、搜索引擎模式。做词频统计和关键词抽取我一般用精确模式因为它不会产生大量重叠的碎片词做共现网络时搜索引擎模式对长词的召回更好但会引入噪声需要配合停用词过滤。选哪个取决于你的分析目标不是越细越好。停用词表是另一个重灾区。网上随便下的「中文停用词表」动辄一两千词直接套用会把「不」「没」「很」这类情感分析关键否定词也删掉导致情感倾向完全失真。我的做法是分层通用停用词的、了、在、是 领域停用词东野圭吾小说里反复出现的「警察」「案件」如果做主题分析可以保留做人物分析则要斟酌 自定义补充标点、数字、单字。停用词表不是一次建好的是在看词频结果时不断迭代出来的。3.2 分词与词频统计代码import jieba from collections import Counter # 加载停用词一行一个 def load_stopwords(pathstopwords.txt): with open(path, encodingutf-8) as f: return set(line.strip() for line in f if line.strip()) STOPWORDS load_stopwords() def tokenize(text, stopwordsSTOPWORDS): 精确模式分词 停用词过滤 去单字和纯数字 words jieba.lcut(text) result [] for w in words: w w.strip() if len(w) 2: # 去单字降低噪声 continue if w in stopwords: continue if w.isdigit(): # 去纯数字 continue result.append(w) return result def word_freq(chapters, topn50): 全语料词频统计 counter Counter() for _, body in chapters: counter.update(tokenize(body)) return counter.most_common(topn) if __name__ __main__: # 假设 chapters 已由上一步得到 top word_freq(chapters) for word, cnt in top: print(f{word}\t{cnt})jieba.lcut返回列表比cut生成器方便后续多次遍历。len(w) 2去单字这一步很关键中文单字词如「的」「了」之外的实词单字在小说里噪声极大去掉后词频表可读性明显提升。但要注意如果你做的是人名分析「加贺」「汤川」这类两字以上没问题但某些单字姓氏会被误删所以人物分析要单独走命名实体识别不能只靠词频。Counter.update直接吃列表比循环累加快语料上万字时差距明显。注意停用词表里千万别放「不」「没」「无」这类否定词除非你确定不做情感分析。我见过太多作业因为停用词表抄来的情感分析结果整个反了。4. 关键词抽取与人物共现网络从词频到结构化洞察4.1 TF-IDF 和 TextRank 该用哪个词频统计只能告诉你「什么词出现得多」但高频词往往是「一个」「自己」这种无意义词。关键词抽取要解决的是「哪些词对这本书有代表性」。常见两种方法TF-IDF 和 TextRank。TF-IDF 依赖「文档」概念把每章当一篇文档计算词在单章的高频与全语料的低频之比适合找「某章特有」的关键词TextRank 基于词共现图不需要文档划分适合找「全局重要」的词。做东野圭吾小说集我一般两个都跑TF-IDF 看单本特征TextRank 看全集主题。两者结果交叉验证比单用一个可信。TF-IDF 的坑在于「文档」粒度。如果把整本书当一篇文档IDF 全为常数退化成词频如果按章切章太短会导致 IDF 波动剧烈。我的经验是单本小说按章切章节数在 10 到 30 之间最稳如果章节太少就按段落聚合到固定字数再切。4.2 关键词抽取与共现网络代码import jieba.analyse import networkx as nx from itertools import combinations def extract_keywords(text, topn20): TF-IDF 和 TextRank 双路抽取 tfidf jieba.analyse.extract_tags( text, topKtopn, withWeightTrue, allowPOS(n, nr, ns, nt, nz, v, vn) ) textrank jieba.analyse.textrank( text, topKtopn, withWeightTrue, allowPOS(n, nr, ns, nt, nz, v, vn) ) return tfidf, textrank def build_cooccurrence(chapters, window5, min_freq10): 滑动窗口构建词共现网络 G nx.Graph() for _, body in chapters: words tokenize(body) for i in range(len(words) - window): chunk words[i:i window] for a, b in combinations(set(chunk), 2): if G.has_edge(a, b): G[a][b][weight] 1 else: G.add_edge(a, b, weight1) # 过滤低频边降低图规模 edges [(u, v, d) for u, v, d in G.edges(dataTrue) if d[weight] min_freq] G2 nx.Graph() G2.add_weighted_edges_from(edges) return G2 if __name__ __main__: full_text \n.join(body for _, body in chapters) tfidf, textrank extract_keywords(full_text) print(TF-IDF Top10:, [w for w, _ in tfidf[:10]]) print(TextRank Top10:, [w for w, _ in textrank[:10]]) G build_cooccurrence(chapters) # 按度中心性排序找核心人物 centrality nx.degree_centrality(G) top_chars sorted(centrality.items(), keylambda x: -x[1])[:15] print(核心节点:, top_chars)allowPOS参数限定词性只保留名词和动词能大幅过滤「很」「非常」这类副词噪声。window5是共现窗口意思是前后 5 个词内算共现这个值太小会漏掉远距离人物关系太大则引入无关连接小说文本一般 5 到 10 之间。min_freq10过滤低频边否则图会有几千个节点可视化出来是一团毛线。degree_centrality找核心节点在东野圭吾小说里通常能准确捞出主角和关键配角这是验证共现网络是否合理的最快方法。提示共现网络里「警察」「尸体」这类高频场景词也会成为核心节点如果只想看人物关系需要先用人名识别如jieba.posseg的 nr 标签过滤出人名再建图。5. 避坑与排查这类大作业最容易翻车的五个地方5.1 编码问题导致分词结果全是乱码现象分词后打印出来是「ä½ å¥½」这种乱码或者词频表里全是单字符。原因原始 txt 是 GBK 或 GB18030用 UTF-8 读取时没报错但内容错了。解决用chardet探测后显式指定编码读取时加errorsignore兜底并在清洗后打印前 200 字人工确认。5.2 停用词表删掉了情感分析的关键词现象情感分析结果和人工阅读感受完全相反正面小说被判成负面。原因停用词表里包含「不」「没」「无」等否定词或者把程度副词「很」「太」删了。解决情感分析单独维护一份停用词表保留否定词和程度副词或者用 SnowNLP 这类自带情感词典的工具不要自己删词。5.3 共现网络节点爆炸可视化卡死现象nx.draw跑十分钟没出图或者出来的图完全看不清。原因没有过滤低频边图有上万个节点。解决先按边权重过滤min_freq调到 20 以上再按度中心性取 Top 50 节点画子图不要试图画全图。5.4 TF-IDF 关键词全是章节标题词现象关键词抽取结果里「第一章」「第二章」排前面。原因分章时章节标题混进了正文或者清洗没去掉目录。解决split_chapters时把标题单独存正文里不包含标题或者在分词前用正则去掉「第X章」模式。5.5 词频统计把不同写法的同一人物算成两个现象「加贺」和「加贺恭一郎」分别统计人物分析失真。原因中文没有词形还原全称和简称是不同 token。解决建一个人名别名词典分词后用词典做归一化替换或者用jieba的自定义词典load_userdict把全称设为一个词让分词器优先切全称。6. 从作业到可展示成果可视化与文档说明的加分技巧把分析跑通只是及格线真正让这份大作业拿高分的是可视化呈现和文档说明的工程感。我一般会做三张图词云图用wordcloud库注意中文字体路径要显式指定否则全是方块、人物共现网络图用networkxmatplotlib节点大小按度中心性映射、情感倾向随章节变化的折线图用SnowNLP逐章打分。这三张图覆盖了「全局主题」「人物结构」「叙事节奏」三个维度答辩时每一张都能讲出背后的分析逻辑。词云图最容易踩的坑是字体。wordcloud默认字体不支持中文必须传font_path参数指向系统里的中文字体文件Windows 一般在C:/Windows/Fonts/simhei.ttfLinux 需要自己确认字体路径。另一个技巧是词云不要直接用原始词频先用 TF-IDF 权重过滤一遍否则「一个」「自己」会占最大字号图很难看。文档说明部分我建议按「数据来源 → 清洗规则 → 方法选择理由 → 参数设置 → 结果解读 → 局限性」六段写。其中「方法选择理由」和「局限性」是拉开差距的地方前者说明你不是照抄代码后者说明你有批判性思维。比如你可以写「TF-IDF 在本语料上的局限是章节长度不均导致 IDF 波动后续可考虑 BM25 改进」这一句就能让老师看出你理解原理。最后一个具体技巧把整个流程封装成一个pipeline.py用命令行参数控制「只清洗」「只分词」「全流程」这样复现时不用改代码。参数用argparse管理输出结果统一存到output/目录中间结果存cache/避免重复计算。这个工程习惯在课程设计里是加分项在实际工作中是基本要求。我自己带学生做这类作业时最常提醒的一句话是别急着跑模型先把清洗后的文本打印出来读三章。很多翻车不是因为算法选错是因为根本没看数据长什么样。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑