简介这是一份基于Python的文本挖掘高分课程项目以日本推理作家东野圭吾小说集为语料完整覆盖文本读取、清洗、分词、词频统计、情感分析与可视化展示等典型环节适合数据挖掘、大数据、信息管理等相关专业学生用于期末大作业、课程设计或毕业设计参考。项目代码注释详细关键步骤留有说明新手也能较快理解并复现压缩包大小约25.78MB主要包含可直接运行的Python源码和配套文档说明文档对环境配置、依赖安装及操作流程均有交代。整体系统功能设计完整界面与结果展示直观项目经过调试可稳定运行可作为作业直接部署或扩展改造。目前已有519人学习/下载完成思路与实现方式具有较高参考价值是撰写同类文本挖掘任务的实用范本。1. 基于 Python 数据挖掘大作业东野圭吾小说集文本挖掘能帮你交出一份高分项目期末前一周我把“东野圭吾小说集文本挖掘”写成了 98 分的 Python 大作业。这套项目不是单纯做词频统计而是从语料清洗、jieba 分词、词云展示一路做到 LDA 主题分析和情感曲线源码带注释还配了文档说明。当时我拿到需求心里没底txt 乱码、章节目录混在正文、人名被切得稀碎这些坑全是逐条踩过才填平的。期末大作业、课程设计、毕设要交一个能跑、能讲、有输出的数据挖掘项目这套资源很合适新手按文档能跑通熟手能改参数分析自己想看的小说。下面我把实现思路、关键代码、参数含义和踩坑点拆开讲方便你判断这套资源值不值得下。2. 把小说文本变成干净语料编码识别、正则清洗与去重拿到文本挖掘项目我给自己定下的第一条规矩先清理语料再谈模型。直接对原始 txt 跑 jieba词频统计结果里前十名多半是“的、了、我、他、在、不”正文里的“第X章”也会混进来主题模型就更不用想基本被停用词淹没。这个项目里的小说文本我拆包之后是散装的 txt每个文件一本或一卷命名还算规整但内容质量并不平均有爬虫站加的页脚、错别字、全角空格还有重复段落。所以第二章先解决三件事编码、清洗、去重这三件事不做扎实后面所有统计都是沙地上盖房子。2.1 先摸清语料结构目录安排与编码探测第一步不是直接读文件而是搞清楚目录结构和文件编码。历史教训我用默认 utf-8 打开一个 GBK 文件打印出来全是“锟斤拷”后面清洗逻辑再好也没有用。常见做法是先写一个探测脚本遍历目录读取每个 txt 的前几千字节交给 chardet 判断编码再把结果写到一个对照表里这样不用在清洗阶段反复试。import os import chardet novel_dir novels/ for fname in sorted(os.listdir(novel_dir)): if not fname.endswith(.txt): continue path os.path.join(novel_dir, fname) with open(path, rb) as f: sample f.read(5000) result chardet.detect(sample) print(f{fname}: {result[encoding]} | 置信度 {result[confidence]})这里用二进制方式读取避免文件内容里夹杂非法字符时直接触发 UnicodeDecodeError只读 5000 字节也就是几页内容足够判断绝大多数文本编码又不至于把一个 20MB 的大文件整个读进内存。chardet.detect 返回的字典里 encoding 和 confidence 最重要confidence 接近 1 时可以直接信。如果结果显示 gb2312统一按 gbk 读也问题不大因为爬虫站的 txt 实际编码经常在 GBK 和 GB2312 之间混用。探测出来后最好把每个文件的编码记下来或者在读取时直接做一次统一转换。我会把所有文件重新存成 utf-8 的干净副本放到clean/目录这样后面的处理脚本不用反复判断编码也方便搬进 Jupyter Notebook。这个过程用循环跑一遍就行。import os import chardet novel_dir novels/ clean_dir clean/ os.makedirs(clean_dir, exist_okTrue) for fname in sorted(os.listdir(novel_dir)): if not fname.endswith(.txt): continue path os.path.join(novel_dir, fname) with open(path, rb) as f: raw f.read() enc chardet.detect(raw)[encoding] or utf-8 if enc.lower() in [gb2312, gbk]: enc gbk text raw.decode(enc, errorsignore) out_path os.path.join(clean_dir, fname) with open(out_path, w, encodingutf-8) as f: f.write(text)写入时统一用 utf-8因为这是跨平台最稳的编码。errorsignore会丢掉无法解码的字节可能损失个别符号但能保住正文主体。这里有个细节如果源文件是带 BOM 的 utf-8直接按 utf-8 读会把 BOM 变成行首的不可见字符之后正则行匹配会莫名失败。所以探测到是 utf-8 时还要看一眼文件头是不是\xef\xbb\xbf是的话就用utf-8-sig解码。这个检查代码很少但能避免一种很难发现的脏数据。2.2 正则清洗正文去章节名、去转义字符、合并断行编码统一之后文本里还带着爬虫站的噪声。我习惯按行清洗因为小说正文和掐头去尾的杂质基本都是按行出现的。最常见的杂质有四类章节标题、空行、页脚广告、全角空格。正则在这一步是主角但用不好会误伤正文。import re def clean_novel_text(text): lines text.splitlines() cleaned [] for line in lines: line line.strip() # 去掉全角空格和行首尾空白 line line.replace(\u3000, ) # 跳过明显的章节标题例如“第一章 白夜行”或“Chapter 1” if re.match(r^第[0-9一二三四五六七八九十百千零]\s*[章回节], line): continue if re.match(r^Chapter\s\d, line, re.IGNORECASE): continue # 清理常见页脚和广告痕迹 if 手机用户 in line or txt下载 in line.lower(): continue # 过滤太短的无意义行 if len(line) 2: continue cleaned.append(line) return \n.join(cleaned)这里的关键是正则做行首匹配而不是全局替换。我见过有人用re.sub(r第.*章, , text)结果把正文里“第一个人”截掉数据直接废了。re.match从行首开始匹配后面用\s*吸收空格再限定落在“章/回/节”这几个字上误伤概率小很多。如果下载的文本里是“第一话”这种字眼把结尾改成[章回节话]就行。页脚过滤行是个黑匣子代码里写死了一部分自己拿到项目后要按文本里实际出现的噪声调整。re.IGNORECASE用于容忍 Chapter 的大小写变体否则大写开头的章节头会漏掉。还有一类噪声是英文书名号和链接可以在跑清洗前用re.sub(rhttp\S, , line)提前清掉。清洗粒度要讲平衡东野圭吾的原文里偶尔有英文短句直接全部过滤到中英文标点之外可能会丢信息所以这一版清洗只删非文本符号不删字母数字。清洗后保存的是纯文本正文后续分词脚本只依赖这一份中间文件改参数时不用重新跑全量清洗。2.3 去重与抽样用集合做归一化判断是否重复爬虫类 txt 经常出现重复段落尤其是同一本书被合并了好几个版本。去重我按行做但不会直接拿原文行去重因为行尾空格、全角半角不一致会导致明明一样的内容被当成两行。先把行归一化再去查集合既快又不会误杀太多。def dedup_lines(text): seen set() unique [] for line in text.splitlines(): line line.strip() if not line: continue norm re.sub(r[\s\u3000], , line) norm norm.lower() if norm in seen: continue seen.add(norm) unique.append(line) return \n.join(unique)归一化规则是去掉所有空白和全角空格并转小写这样“Hello World”和“hello world”会判定重复。代价是英文大小写区别被抹掉了但英文在小说正文里占比低可以接受。如果把seen换成Counter还能统计重复次数大作业报告里能多写一小节“数据情况”导师会觉得你考虑了数据质量。如果重复发生在不同文件的整段文本行级去重就无能为力了。常见做法是计算每段文本的 simhash 或 MinHash但这属于超纲操作期末大作业不要求。我一般用最土的办法把两个文件各自的分词结果做个 top 100 交集超过一半就怀疑是重复文本人工抽查。这个办法很糙但能拦住最明显的重复。清洗和去重完的语料我会按文件统计一次字符数和行数记录下来。后面报告里写“总语料 X 万字去重 Y 条”这个数字是实打实算出来的不是拍脑袋。3. jieba 分词与停用词过滤词频统计的细节和参数语料干净后真正的文本挖掘才刚刚开始。东野圭吾小说最让人头疼的是人名比如“加贺恭一郎”“汤川学”默认 jieba 词典很可能把它们拆成单字或两字词。分词结果一旦不对后面词频、词云、主题模型全都不对。所以这一章先把分词调好再做词频统计最后落到能写进报告的结果文件。3.1 自定义词典的重要性把人名地名救回来在项目里我维护了一个user_dict.txt格式是 jieba 自定义词典标准格式每行一个词后面跟词频和词性中间用空格分隔。词频数字不是越大越好几百就够作用只是告诉分词器“这是一个完整概念”。词性可填可不填填了可以在后续做词性过滤时用。这个文件是分词环节最重要的配置没有它整本小说的角色名都会被切碎。import jieba jieba.load_userdict(user_dict.txt) print(jieba.lcut(加贺恭一郎继续调查白夜行里的真相))load_userdict加载后整个进程都生效不用对每句重复添加。如果只是临时调试一个词用jieba.add_word(白夜行, freq100)更方便但正式脚本里建议统一放到 user_dict.txt 里方便评审老师看。我这份 user_dict.txt 里大概收了三五十个人名和书名都是从东野圭吾小说角色列表里整理出来的比如加贺恭一郎、汤川学、桐原亮司、唐泽雪穗。词频都给的 100词性用 nz也就是专有名词。加载词典后最好抽样打印几十句分词结果肉眼看人名是否完整。这一步不是玄学因为 jieba 是基于统计成词的词典只是提高优先级不是强制切分。遇到确实切不开的词可以用jieba.suggest_freq调整单个词的频率。我很少调因为大作业不需要做到十全十美能稳住主要人名就够了。# 如果某个词始终被拆分可以单独调整频率 jieba.suggest_freq(桐原亮司, tuneTrue) print(jieba.lcut(桐原亮司和唐泽雪穗))suggest_freq会让 jieba 重新计算该词在统计模型里的优先级tuneTrue表示根据当前句子动态调整。这里的“桐原亮司”如果不做处理很容易被切成“桐原/亮司”加载词典后基本能保住。3.2 停用词表不是越全越好过滤与保留的平衡停用词表是文本挖掘最容易翻车的地方。网上流传很多停用词表哈工大版、百度版、机器之心版各有侧重。它们用来过滤“的、了、把、被”这类高频虚词非常有效但直接套到小说上有个问题很多常见停用词在小说里是有叙事作用的。比如“夜”“人”“手”在普通语料里是停用词候选但在东野圭吾的文本里可能是高频词删掉之后主题模型就会把“推理”变得更加模糊。所以我的做法是先加载通用停用词表再根据本书语料把误杀的词捞回来。stopwords set() with open(stopwords.txt, encodingutf-8) as f: for line in f: w line.strip() if w: stopwords.add(w) # 根据语料复查后把部分词从停用词表中豁免 for w in [人, 夜, 手, 世界, 女人]: stopwords.discard(w)这里用discard而不是remove因为不确定通用表里是否包含这个词discard不存在时不会抛异常脚本跑起来更稳定。过滤逻辑里除了停用词还会过滤纯数字、纯标点和单字符。注意单字符不是绝对要过滤比如“夜”在这套小说里单字也有意义如果你强行过滤单字词云里就少了氛围词。我把单字过滤写成一个参数keep_single_char方便在复现时切换。def tokenize(text, keep_single_charFalse): words jieba.cut(text) result [] for w in words: w w.strip() if not w: continue if w in stopwords: continue if w.isdigit(): continue if not keep_single_char and len(w) 1: continue result.append(w) return resultjieba.cut默认用精确模式HMM参数默认 True对新词识别有帮助。如果你希望结果完全可复现建议把 jieba 版本锁死因为不同版本词典更新会影响切分。项目说明里一般会写依赖版本比如 jieba0.42.1固定版本是文本挖掘的后悔药改版本前先备份当前结果。3.3 词频统计与 Top N 输出从 Counter 到 DataFrame过滤完之后词频统计就变得很简单。我用 Counter 累计所有小说分词结果再取 Top N 做展示。对大作业来说 Counter 已经足够快语料十几万字秒出不需要上 Spark 之类的重型工具。from collections import Counter counter Counter() for doc in docs: # docs 是每一部小说的分词结果list of list counter.update(tokenize( .join(doc))) top counter.most_common(50) for rank, (word, count) in enumerate(top, 1): print(rank, word, count)update接收可迭代对象所以把每部小说的分词结果先拼成字符串再喂进去也行但直接列表更省内存。most_common(50)的 50 不是固定值词云用 top 200报告表格用 top 30避免前 50 里全是同一批虚词。如果看到 Top 1 是“没有”别急着调停用词表先确认过滤条件里的长度限制是否生效。把结果存成 CSV 也是给大作业写报告用的。Excel 打开不能乱码所以编码用utf-8-sig而不是utf-8。这一点很多新手没注意脚本在 PyCharm 里打印正常一写到 CSV 就乱。import pandas as pd df pd.DataFrame(top, columns[word, count]) df.to_csv(top_words.csv, indexFalse, encodingutf-8-sig)indexFalse去掉 DataFrame 默认的序号列编码utf-8-sig会写一个 BOMExcel 看到 BOM 才知道用 UTF-8 解析否则默认按本地编码打开中文直接变乱码。这个细节在评审演示时能救你一命。另外词频表里除了统计词频我还留了一列“是否在停用词表内”用布尔值标记这样导师能看出你不仅会跑 Counter还考虑过停用词对结果的影响。4. 从 LDA 主题模型到情感曲线代码实现与可视化词频统计只是文本挖掘的及格线导师通常最关心的更深一层问题是这些小说的主题是什么角色和情节的情绪有没有走向。我在这个项目里加了 LDA 主题模型和情感曲线两部分各解决一个问题前者把几十万字的语料压缩成几个可解释的主题词后者把整本的叙事节奏量化出来。这一章代码不长但参数设置直接影响结果值得细看。4.1 工具选型gensim 还是 sklearnLDA 的实现里 gensim 最常被提到但大作业环境里 sklearn 更省心。gensim 的 LdaModel 对输入格式要求多语料需要经过 Dictionary、bow_corpus 两轮转换版本 3.x 和 4.x 的 API 还变过踩坑成本高。sklearn 的 LatentDirichletAllocation 接口类似普通分类器输入就是一个文档-词频矩阵更适合快速出结果。所以我选了 sklearn。from sklearn.feature_extraction.text import CountVectorizer from sklearn.decomposition import LatentDirichletAllocation vectorizer CountVectorizer( tokenizerlambda x: x.split(), max_features5000, min_df2, max_df0.8 ) doc_term vectorizer.fit_transform([ .join(words) for words in docs]) lda LatentDirichletAllocation( n_components5, random_state42, max_iter100, learning_methodbatch ) lda.fit(doc_term)这里tokenizer直接按空格切词是因为前面的 tokenize 函数已经把句子切成词并过滤干净了原始文本并没有传进来。max_features5000限制词表大小词表越大训练越慢3000 到 5000 对大作业够了。min_df2表示至少在 2 个文档中出现的词才保留去掉只在某一本里冒一次的噪声词max_df0.8过滤掉出现在 80% 以上文档里的词这类词往往是通用词会干扰主题区分。learning_methodbatch比在线更新更稳定代价是内存稍高几本小说完全在意料之中。random_state42固定随机种子否则 LDA 结果每次跑都可能不一样导师问起来很难解释。4.2 主题数怎么选perplexity 与主题连贯性LDA 的 n_components 是文本挖掘里最像玄学的参数。官方文档说困惑度越低越好实际跑下来却发现主题数越大困惑度越小8 个主题的方案困惑度最低但主题词读起来一片混沌根本分不出哪是哪。所以我的习惯是把困惑度当参考把主题词可读性当最终标准。for n_topics in [3, 5, 8, 10]: lda_model LatentDirichletAllocation( n_componentsn_topics, random_state42, max_iter100 ) lda_model.fit(doc_term) perplexity lda_model.perplexity(doc_term) print(f主题数 {n_topics}: 困惑度 {perplexity:.2f}) for idx, topic in enumerate(lda_model.components_): top_words [vectorizer.get_feature_names_out()[i] for i in topic.argsort()[:-9:-1]] print(f Topic {idx}: { .join(top_words)})lda_model.components_是每个主题的词分布形状是(n_topics, n_words)。argsort()[:-9:-1]取每个主题权重最高的 8 个词索引再通过get_feature_names_out()还原成词。运行后如果 3 个主题分别是“案件调查”“人物关系”“情感纠葛”5 个主题还能多分出一个“家庭/童年”说明主题数合适如果 5 个主题里有两个主题词几乎重合就要把主题数调回 3 或 4。不要迷信困惑度的拐点主题数选择最终是解释性问题。跑出来的主题词我会整理成一张表放在报告里每个主题取 8 个词加一段人工命名比如“主题 2亲子关系”。4.3 情感曲线SnowNLP 打分与窗口平滑情感曲线是我觉得这套项目里最出彩的部分。东野圭吾的小说整体都偏暗但亮司和雪穗那条线到后半段情绪张力完全不一样。用情感分数量化出来比口头描述更有说服力。SnowNLP 是基于评论语料训练的情感模型给小说打分有偏差但作为大作业完全够用重点在展示趋势。import re from snownlp import SnowNLP def chapter_sentiment(chapter_text): # 按句子切分避免长文本造成计算过慢 sentences re.split(r[。\n], chapter_text) scores [] for s in sentences: s s.strip() if len(s) 2: continue try: scores.append(SnowNLP(s).sentiments) except Exception: # SnowNLP 对个别特殊字符串会抛异常直接跳过 continue if not scores: return 0.5 return sum(scores) / len(scores)按句子切分而不是整章直接打分是因为 SnowNLP 内部对长文本的处理时间不是线性的传一整章进去又慢又容易出边界问题。先过滤长度小于 2 的句子再逐句打分取平均得到一个 0 到 1 的章节情感分。0.5 算中性越接近 0 越负向越接近 1 越正向。try/except不是多此一举SnowNLP 在输入只有标点或特殊字符时会在内部抛 UnicodeEncodeError这种错误不影响整体结果直接跳过即可。import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Microsoft YaHei] plt.rcParams[axes.unicode_minus] False plt.plot(range(len(scores)), scores, markero, markersize3, linewidth1.2) plt.axhline(0.5, colorgray, linestyle--, label中性线) plt.xlabel(章节) plt.ylabel(情感分) plt.title(东野圭吾小说情感曲线) plt.legend() plt.show()font.sans-serif必须放在画图前否则 matplotlib 找不到中文字体标题变成方块。axes.unicode_minusFalse让负号正常显示否则情感分低于 0 时刻度上的负号会变成方框。排序靠前的章节序号不一定对应下载文件顺序跑之前先确认章节列表是按时间顺序排的。情感曲线画完后我还会用移动平均做一次平滑方法是把前后 5 个章节的分数平均作为当前点的输出值这样曲线不会因为某一章特别短而剧烈抖动。5. 复现时最容易翻车的五个坑环境、编码与展示排查这套项目我在提交前前后后跑过不下十遍真正让我翻车的往往是环境、编码和可视化这三类。下面把最典型的五个坑按现象、原因、解决三部分写清楚你复现时命中任何一个都能快速定位不用把整个流程重新排查一遍。5.1 环境与依赖装错包、版本不一致、字体缺失坑 1import jieba直接报 ModuleNotFoundError现象下载项目后在终端运行python main.py立刻提示ModuleNotFoundError: No module named jieba。原因这个项目依赖 jieba、chardet、snownlp、scikit-learn、wordcloud 等第三方库压缩包不会把依赖打包进去需要自己安装。很多新手用pip install jieba后仍然报错是因为系统里同时存在 Python2 和 Python3或者装了多个虚拟环境pip 装到另一个环境去了。解决使用python -m pip而不是裸pip这样装到的是当前python解释器对应的环境。python -m pip install -r requirements.txt如果找不到 requirements.txt就手动安装python -m pip install jieba chardet snownlp scikit-learn gensim matplotlib wordcloud pandas装完用python -c import jieba; print(jieba.__version__)确认版本。如果输出正常再跑主脚本。多环境共存的机器上这一步能省下半小时。坑 2wordcloud 生成词云时中文全部变成方块现象wordcloud 跑出图片很快词云轮廓正常但所有中文文字都显示为小方块。原因wordcloud 自带英文字体如果让 WordCloud 自己选字体中文字符没有对应字形就会渲染成方块。必须在 WordCloud 里显式指定中文字体文件路径。解决找一个系统的中文字体文件Windows 常用C:/Windows/Fonts/simhei.ttfmacOS 常用/System/Library/Fonts/PingFang.ttcLinux 常见/usr/share/fonts/wqy-microhei/wqy-microhei.ttc。代码里这样指定from wordcloud import WordCloud wc WordCloud(font_pathsimhei.ttf, width800, height600)把字体路径写成一个独立变量方便切换系统。最好把字体文件复制到项目根目录这样换机器跑也不会因为系统缺字体而失败。5.2 编码与统计结果乱码、正则误删、LDA 不稳定坑 3读入 txt 后输出一堆“锟斤拷”现象脚本能跑所有 print 和写出的文件里中文全成了乱码尤其 Windows 系统高发。原因下载的原始 txt 是 GBK/GB2312 编码但读取时强制用了 utf-8解码出来的字节流对不上Python 在终端和文件写入时再转码就变成了“锟斤拷”。解决回到第二章的编码探测步骤先用 chardet 探测再按探测结果解码。写入结果文件时统一用encodingutf-8-sig。如果探测结果是 GB2312统一改成 GBK 再试一次GB2312 编码的文本用 GBK 解码通常更稳。我习惯把一个文件的读取编码和写入编码都打印出来方便复查。坑 4正则清洗误删了正文里的关键句子现象清洗后检查语料发现小说正文出现大面积断行甚至“第一个人”这种词被删掉。原因用re.sub(r第.*章, , text)这类全局正则会匹配从“第”到“章”之间的任意内容遇到“第一个人”也会匹配到一个片段正则没限定行首导致正文被切片。解决改成逐行读取并用re.match(r^第[0-9一二三四五六七八九十百千零]\s*[章回节], line)判断确认匹配的是行首的章节标题。清洗后要随机抽样 10 到 20 个段落人工看一眼不要相信正则跑完就万事大吉。文本清洗本来就是手工活和数据工程的重合地带想靠一条正则吃遍所有 txt 是不可能的。坑 5LDA 两次运行结果完全不一样现象同一次报告里跑两次 LDA第一次 Topic 1 是“案件、调查、线索”第二次 Topic 1 变成了“母亲、回忆、房间”没法解释。原因LDA 是随机算法初始节点是随机选择的不固定随机种子每次都会得到不同结果。解决在 LatentDirichletAllocation 中设置random_state42并且把max_iter设成相同值。固定之后同一份语料在同一版本环境下结果可复现。如果用的是 gensim注意 gensim 4.x 的random_state参数写法直接查官方文档对应版本。提交报告前我会把 LDA 参数和随机种子也写进报告附录导师看到这一步就知道你懂可复现性的重要性。6. 把词云做成风格画像验证文本挖掘结果的通用技巧词云是文本挖掘大作业里最直观的加分项但只给一张词云图还不够说清楚词云背后的验证逻辑才真正有价值。我处理这套项目时养成了一个习惯跑完主流程再拿一个对比语料做同样的处理把东野圭吾的 top 词和对比文本的 top 词做重合度分析。如果重合率太高说明你的停用词表还不够猛如果重合率明显低说明这套流程确实捕捉到了文本风格。from wordcloud import WordCloud wc WordCloud( font_pathsimhei.ttf, width800, height600, background_colorwhite, max_words200, collocationsFalse ).generate( .join(all_words)) wc.to_file(wordcloud.png)max_words200表示只展示权重最高的 200 个词词云不是越满越好太满会糊collocationsFalse让 wordcloud 不要自动组合二元词组否则“白夜/行”这类相邻词可能被拼成“白夜行”和 jieba 词典冲突。生成词云后对比语料也跑一遍同样的词频统计然后算重合率top_a set([w for w, _ in counter_a.most_common(100)]) top_b set([w for w, _ in counter_b.most_common(100)]) overlap len(top_a top_b) / 100 print(f重合率 {overlap:.2%})这里的 100 是 top 词数量你可以跑 50、100、200 三个粒度把结果画成一条折线报告里能多一张图。对比语料随便选一个风格差异大的文本就行我习惯用一本同类型的推理小说或一本文艺小说。把词云图和对照表放到实验部分前面再放上 LDA 主题词和情感曲线整个项目的证据链就完整了。这套资源的压缩包里代码和文档是配套的docs 目录里有环境说明和运行步骤下载后先按文档过一遍再改参数比直接敲代码稳得多。从那以后我每次做文本挖掘项目都会强制走一遍固定流程先探测编码、固定随机种子、锁定 jieba 版本再做任何统计。只要这三件事做在前面后面翻车的概率直接砍掉一大半。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?