ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python实战:用文本分析看穿宝可梦同人文结构

Python实战:用文本分析看穿宝可梦同人文结构 当我们在追更一篇超长篇宝可梦同人文时往往不只是想知道“谁和谁最后在一起了”更想从宏观上理解作者的伏笔分布、角色出场频率、情感走向甚至从文本中看出“真实之眼”和“波导之力”这两个核心设定的使用节奏。手动统计当然不现实这时候就需要一套文本分析工具让 Python 帮我们快速“看穿”一部小说的结构。这篇文章将围绕“用 Python 进行中文小说文本分析与可视化”这一主题设计一个完整的实战项目。我们会以《宝可梦我有一双真实之眼》这类同人文常见的文本特征为背景实现字数统计、角色线索提取、分词词频、情感趋势和词云图生成等功能。整个过程覆盖从文件读取、数据清洗到可视化输出的完整链路代码可以直接复制到本地运行。本文适合以下读者想对小说文本做量化分析、但又不知道从哪里下手的 Python 初学者。需要快速完成“文本数据分析”课程作业、项目演示的开发者。对中文分词、jieba、WordCloud 等库有了解但还没有整合经验的学习者。想在宝可梦同人创作社区做出有趣数据作品的作者和读者。整套代码不需要 GPU也不依赖大型深度学习框架只需要 Python 3.8 和几个常用第三方库即可运行。接下来我们一步一步搭建这个“看穿一切”的文本分析工具。1. 为什么需要“真实之眼”文本分析要解决什么问题1.1 同人文长文本分析的常见痛点一部长期连载的宝可梦同人文动辄几十万字章节数量可能超过一百章。读者常遇到的问题包括角色出场频率不清小智、小刚、小霞、火箭队等人气角色在不同篇章的戏份分布如何伏笔位置难找作者在第 50 章埋下的设定到第 200 章才回收读者几乎不可能凭记忆定位。情感节奏无法量化故事进入高潮前文本中的情感词汇密度通常会发生变化但靠肉眼很难判断具体节点。高频词语与主题脱节如果“训练家”“宝可梦”出现次数异常高可能只是背景设定的基础词需要与自定义关键词区分开。这些问题正好可以用 Python 文本分析技术解决。我们可以把一部长篇小说作为数据源提取它的关键词、角色名、情感词汇和结构特征从而得到一个“文本之上的视角”——这正是我们这篇文章标题里“真实之眼”的含义。1.2 核心技术链路的确定在制定方案之前我们先把文本分析常见的技术链路整理出来方便后续对照实现。阶段输入输出常用工具文本读取.txt 编码未知的文件清洗后的纯文本Python 内置 open、正则表达式分词长文本词语列表jieba停用词过滤词语列表过滤后的关键词列表自定义停用词表词频统计词语列表词频字典或 DataFramecollections.Counter、pandas可视化词频数据柱状图、词云图、趋势折线图matplotlib、wordcloud情感分析分句文本情感分值序列SnowNLP可选这个流程是从“字”到“词”再到“图”的逐步抽象过程。初学者往往一上来就做词云忽略了数据清洗的重要性结果生成的图片里全是“一个”“没有”“我们”这种无意义词汇效果很差。下面我们在实战部分会特别强调清洗环节。1.3 “看穿一切却看不透你”的技术隐喻这句话从数据结构上看很有意思。“看穿一切”代表我们可以对全文做大范围统计和可视化获得宏观视角“看不透你”则对应文本中的模糊信息——反讽、暗示、情感潜台词、角色间未说明的关系。这些靠“词频统计”这类硬统计很难捕捉因此我们还需要借助情感分析和上下文语境查看功能。在实践中我们可以把一篇文章拆成两个维度来看显性维度词语、句子、章节、角色名、场景名。这是统计工具可以直接处理的。隐性维度情感倾向、叙事视角、人物情绪曲线、伏笔密度。这需要结合情感词典和自定义规则分析。本项目的“真实之眼.py”会同时覆盖这两个维度。重点落在显性维度的统计上同时对隐性维度给出一个可用的探索思路。2. 环境准备与项目结构2.1 Python 环境与依赖安装本项目的运行环境以 Windows / macOS / Linux 均可但要注意不同操作系统在字体路径上的差异。建议使用 Python 3.8 及以上版本。创建虚拟环境并安装依赖python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate pip install jieba pandas matplotlib wordcloud snownlp各个库在本项目中的职责如下jieba中文分词负责把小说文本切成有意义的词语。pandas数据整理和统计分析。matplotlib绘制基础图表如柱状图、折线图。wordcloud生成词云图。snownlp可选用于粗粒度情感倾向分析。版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。如果安装时因为版本冲突报错可以升级 pip 后重试pip install --upgrade pip setuptools wheel2.2 文本数据准备为了方便演示建议准备两个文件novel.txt待分析的同人文全文文本UTF-8 编码纯文本即可。stopwords.txt停用词表每行一个词。如果没有现成文本可以先用几段示例文本测试流程。下面是我们的小型测试样例后续所有代码都以这段文本为输入演示效果。小智站在真新镇的草地上皮卡丘安静地趴在他的肩上。他拥有能看穿一切的真实之眼 却始终看不清自己内心的波导。远处的火箭队又有了新的动静一场新的冒险即将展开。这段文字虽然短但包含了“小智”“皮卡丘”“真实之眼”“波导”“火箭队”等关键词足以验证分词、过滤、统计、可视化的完整流程。2.3 项目目录结构整个项目建议按下面的结构组织pokemon_text_analysis/ ├── data/ │ ├── novel.txt │ └── stopwords.txt ├── output/ │ ├── 词频统计.csv │ ├── 词云图.png │ └── 情感趋势.png ├── main.py └── requirements.txt这样区分的好处是数据文件、分析脚本、输出结果互不混在一起。我们把data目录作为统一的输入目录output目录保存生成的图表和表格requirements.txt用来记录依赖版本。requirements.txt 内容如下jieba0.42.1 pandas2.0.3 matplotlib3.7.2 wordcloud1.9.2 snownlp0.12.3如果不确定这些版本是否和本机环境完全兼容可以先不指定版本号只写库名让 pip 自动安装最新版本。3. 核心模块拆解从零实现一个“真实之眼”在写完整脚本之前我们先拆解每个核心模块的用途和实现思路。后面的 main.py 只是把这些模块串起来。3.1 模块一文本读取与编码处理小说文本最常见的坑是编码格式。很多人下载的 txt 文件是 GBK 或 GB18030 编码而 Python 默认按 UTF-8 读取时会直接抛出 UnicodeDecodeError。推荐的做法是让代码自动尝试多种编码# 核心片段自动尝试常见编码 def read_text(file_path): encodings [utf-8, gb18030, gbk, big5] for enc in encodings: try: with open(file_path, r, encodingenc) as f: return f.read() except UnicodeDecodeError: continue raise ValueError(f无法识别文件编码{file_path})这里用gb18030而不是gbk的原因是gb18030 是 GBK 的超集可以兼容更多繁体字和生僻字。宝可梦同人文里经常出现角色昵称、自创名词遇到特殊字符的概率比较高。读取成功后文本中可能包含空白行、特殊符号、章节号、括号内容等。清洗规则要根据小说格式调整下面是常见处理示例import re def clean_text(raw): # 替换全角空格 text raw.replace(\u3000, ) # 去掉章节标题行可根据实际情况调整正则 text re.sub(r第[0-9一二三四五六七八九十百千]章.*, , text) # 去掉方括号和圆括号内容 text re.sub(r[\(\[【].*?[\)\]】】, , text) # 去掉多余空白字符 text re.sub(r\s, , text) return text注意去掉括号内容时要谨慎。如果括号中是角色心理描写或重要注释直接删除会影响语义。这里推荐在测试阶段先保留括号内容观察词频结果后再决定是否清洗。3.2 模块二分词与停用词过滤中文分词工具首选 jieba。对于一部小说我们需要让它能识别自定义角色名、技能名和设定术语。使用 jieba 加载自定义词典import jieba def load_custom_words(): custom_words [ 真实之眼, 波导之力, 皮卡丘, 训练家, 精灵球, 火箭队, 超梦, 梦幻, 小智, 小刚, 小霞, ] for word in custom_words: jieba.add_word(word, freq1000)freq1000是一个加权提示表示这个词在文本中出现的可能性较高。如果自定义词本身是常用词这个值可以调大如果是生僻词保持默认即可。接下来实现分词和停用词过滤def cut_words(text): return jieba.lcut(text) def filter_stopwords(words, stopwords_path): with open(stopwords_path, r, encodingutf-8) as f: stopwords set(line.strip() for line in f if line.strip()) return [w for w in words if w not in stopwords and len(w.strip()) 1]这里的过滤条件len(w.strip()) 1可以过滤掉单个字比如“的”“了”“吗”。但要注意像“超梦”这样的双字词不会被误删因为长度大于 1。如果小说中有重要的单字角色名需要单独做白名单设置在停用词过滤之后。3.3 模块三词频统计与 DataFrame 输出过滤完停用词后我们可以用 collections.Counter 统计词频from collections import Counter import pandas as pd def build_word_frequency(words, top_n30): counter Counter(words) common counter.most_common(top_n) df pd.DataFrame(common, columns[词语, 次数]) return dfpandas 在这里的主要价值不是参与复杂计算而是方便输出 CSV 和做后续分析。例如我们可以把结果按“次数降序”保存到 Excel 或 CSVdf.to_csv(output/词频统计.csv, indexFalse, encodingutf-8-sig)utf-8-sig编码很重要。如果直接保存为utf-8用 Excel 打开 CSV 时中文列名可能乱码utf-8-sig会在文件开头写入 BOMExcel 打开正常。3.4 模块四词云图生成词云图是文本分析最常见的展示形式。WordCloud 库本身使用简单但有两个常见坑中文显示问题如果不设置中文字体词云图会出现满屏方框。背景与配色默认参数生成的图片可能看不清。推荐使用系统自带的中文字体。Windows 环境下常见路径是C:\Windows\Fonts\simhei.ttfmacOS 常见路径是/System/Library/Fonts/PingFang.ttc。建议在代码中用一个变量保存字体路径方便切换。from wordcloud import WordCloud def generate_wordcloud(word_freqs, output_path, font_path): wc WordCloud( font_pathfont_path, width1600, height900, background_colorwhite, max_words200, colormapviridis ) # 接收字典格式{词语: 次数} wc.generate_from_frequencies(word_freqs) wc.to_file(output_path) print(f词云图已保存到{output_path})如果word_freqs是一个字典generate_from_frequencies可以直接接收如果是一个 DataFrame需要转成字典word_freqs dict(zip(df[词语], df[次数]))3.5 模块五情感趋势分析补充维度SnowNLP 可以对一句文本计算情感倾向值范围在 0 到 1 之间越接近 1 表示越积极。对于长篇文本我们可以按章节切分然后对每个章节的所有句子求平均情感分得到整部小说的情感波动曲线。from snownlp import SnowNLP import matplotlib.pyplot as plt def sentiment_analysis_by_chapter(chapters): results [] for i, chapter in enumerate(chapters): s SnowNLP(chapter) score s.sentiments results.append({章节: i 1, 情感值: score}) return pd.DataFrame(results) def plot_sentiment(df, output_path): plt.figure(figsize(12, 5)) plt.plot(df[章节], df[情感值], markero, linestyle-, color#2E86AB) plt.xlabel(章节序号) plt.ylabel(情感倾向分值) plt.title(宝可梦同人文情感趋势) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(output_path, dpi200) plt.close()这里的sentiments是 SnowNLP 自带的中文情感模型对现代网文文本效果还行但对宝可梦这类带大量专有名词的文本准确率并非百分百。更严谨的做法是自定义情感词典比如把“羁绊”“守护”“胜利”归为正向词把“背叛”“失去”“失败”归为负向词。4. 完整实战宝可梦同人文分析工具现在把所有模块整合到main.py中。你可以直接复制下面的代码把data/novel.txt替换成自己的小说文本运行。4.1 完整代码# -*- coding: utf-8 -*- # 文件路径main.py 宝可梦同人文文本分析工具 功能读取小说文本进行清洗、分词、词频统计、词云图和情感趋势分析 import os import re from collections import Counter import jieba import pandas as pd import matplotlib.pyplot as plt from wordcloud import WordCloud from snownlp import SnowNLP # ---------- 配置 ---------- BASE_DIR os.path.dirname(os.path.abspath(__file__)) DATA_DIR os.path.join(BASE_DIR, data) OUTPUT_DIR os.path.join(BASE_DIR, output) NOVEL_PATH os.path.join(DATA_DIR, novel.txt) STOPWORDS_PATH os.path.join(DATA_DIR, stopwords.txt) WORDCLOUD_IMG os.path.join(OUTPUT_DIR, 词云图.png) FREQ_CSV os.path.join(OUTPUT_DIR, 词频统计.csv) SENTIMENT_IMG os.path.join(OUTPUT_DIR, 情感趋势.png) # 中文字体路径根据你的操作系统调整 FONT_PATH rC:\Windows\Fonts\simhei.ttf # Windows # FONT_PATH /System/Library/Fonts/PingFang.ttc # macOS def ensure_dirs(): 创建必要的目录 os.makedirs(DATA_DIR, exist_okTrue) os.makedirs(OUTPUT_DIR, exist_okTrue) def read_text(file_path): 自动识别编码并读取文本 encodings [utf-8, gb18030, gbk, big5] for enc in encodings: try: with open(file_path, r, encodingenc) as f: return f.read() except UnicodeDecodeError: continue raise ValueError(f无法识别文件编码{file_path}) def clean_text(raw): 文本清洗 # 替换全角空格 text raw.replace(\u3000, ) # 去掉常见章节标题行根据实际文本调整 text re.sub(r第[0-9一二三四五六七八九十百千]章.*[\n]?, \n, text) # 去掉多余空白字符保留中文和英文 text re.sub(r\s, , text) return text def load_stopwords(stopwords_path): 加载停用词表 if not os.path.exists(stopwords_path): return set() with open(stopwords_path, r, encodingutf-8) as f: return set(line.strip() for line in f if line.strip()) def cut_words(text): jieba 分词 return jieba.lcut(text) def filter_words(words, stopwords): 停用词过滤只保留长度大于1的中文词 result [] for w in words: if w in stopwords: continue if len(w.strip()) 2: continue if not re.search(r[\u4e00-\u9fff], w): continue result.append(w) return result def build_word_frequency(words, top_n50): 词频统计 counter Counter(words) return pd.DataFrame(counter.most_common(top_n), columns[词语, 次数]) def generate_wordcloud(freq_dict, output_path): 生成词云图 wc WordCloud( font_pathFONT_PATH, width1600, height900, background_colorwhite, max_words200, colormapviridis, random_state42, ) wc.generate_from_frequencies(freq_dict) wc.to_file(output_path) print(f词云图已保存{output_path}) def split_chapters(text): 按章节标识切分文本简单演示用 # 这里用“第X章”进行切分仅作为演示 parts re.split(r第[0-9一二三四五六七八九十百千]章, text) return [p.strip() for p in parts if p.strip()] def sentiment_analysis(chapters): 按章节计算情感均值 results [] for i, chapter in enumerate(chapters[:200]): # 限制章节数避免运行过慢 try: s SnowNLP(chapter) score s.sentiments except Exception: score 0.5 results.append({章节: i 1, 情感值: score}) return pd.DataFrame(results) def plot_sentiment(df, output_path): 绘制情感趋势图 plt.figure(figsize(12, 5)) plt.plot(df[章节], df[情感值], markero, markersize3, linestyle-, color#2E86AB) plt.xlabel(章节序号) plt.ylabel(情感倾向分值) plt.title(宝可梦同人文情感趋势) plt.grid(alpha0.3) plt.tight_layout() plt.savefig(output_path, dpi200) plt.close() print(f情感趋势图已保存{output_path}) def main(): ensure_dirs() # 1. 读取文本 print(正在读取小说文本...) raw_text read_text(NOVEL_PATH) print(f原始文本长度{len(raw_text)} 字符) # 2. 清洗文本 print(正在清洗文本...) clean clean_text(raw_text) print(f清洗后长度{len(clean)} 字符) # 3. 加载自定义词典 print(正在加载自定义词典...) custom_words [ 真实之眼, 波导之力, 皮卡丘, 训练家, 精灵球, 火箭队, 超梦, 梦幻, 小智, 小刚, 小霞, 波导, 宝可梦, ] for word in custom_words: jieba.add_word(word, freq1000) # 4. 分词与过滤 print(正在分词...) words cut_words(clean) stopwords load_stopwords(STOPWORDS_PATH) filtered filter_words(words, stopwords) print(f分词数量{len(words)}过滤后数量{len(filtered)}) # 5. 词频统计 print(正在统计词频...) freq_df build_word_frequency(filtered, top_n50) freq_df.to_csv(FREQ_CSV, indexFalse, encodingutf-8-sig) print(freq_df.head(10).to_string(indexFalse)) # 6. 生成词云 print(正在生成词云图...) freq_dict dict(zip(freq_df[词语], freq_df[次数])) generate_wordcloud(freq_dict, WORDCLOUD_IMG) # 7. 情感趋势分析 print(正在分析情感趋势...) chapters split_chapters(clean) if len(chapters) 1: sentiment_df sentiment_analysis(chapters) plot_sentiment(sentiment_df, SENTIMENT_IMG) else: print(文本未检测到分章标识跳过情感趋势分析。) print(全部完成请查看 output 目录。) if __name__ __main__: main()4.2 运行步骤第一次运行前你需要准备数据文件。最简单的办法是先创建data/novel.txt和data/stopwords.txt然后填入测试内容。停用词表示例的 了 和 是 在 我 你 他 她 它 有 就 啊 呢 吗然后运行脚本python main.py4.3 预期输出说明以文章开头的短文本为例运行后输出大致如下正在读取小说文本... 原始文本长度70 字符 清洗后长度70 字符 正在加载自定义词典... 正在分词... 分词数量36过滤后数量22 正在统计词频... 词语 次数 0 皮卡丘 2 1 小智 1 2 真新镇 1 3 真实之眼 1 4 波导 1 5 火箭队 1 6 草地 1 7 肩膀 1生成的词频统计.csv可以在 Excel 中打开词云图.png则可以直接用在文章或分享中。情感趋势图因为只有一段文字不会单独生成。5. 常见问题与排查思路文本分析项目看起来简单实际跑起来问题不少。下面整理几个高频问题。5.1 jieba 分词不准问题现象常见原因解决思路“皮卡丘”被拆成“皮”“卡丘”词典缺少该词或词频过低用jieba.add_word(皮卡丘, freq1000)添加“真实之眼”被拆开未加载自定义词典在load_custom_words中补充普通短语也被合并自定义词过长过泛避免加入过多通用短语只加专有名词5.2 词云图出现方框方框几乎都是字体问题。确认两点FONT_PATH指向的文件存在。字体路径没有包含空格或特殊字符如果有空格用原始字符串写法。可以临时写一段代码验证字体是否存在import os print(os.path.exists(rC:\Windows\Fonts\simhei.ttf))5.3 读取文件报 UnicodeDecodeError如果代码自动尝试编码后仍然失败可能是文件本身带有 BOM 或混合编码。建议先用文本编辑器打开另存为 UTF-8 无 BOM 格式。Windows 记事本另存时选择“UTF-8”即可。5.4 SnowNLP 运行缓慢SnowNLP 是基于 Python 实现的情感模型对长文本逐句处理会比较慢。如果章节很多建议只分析前 200 章或者每章只取前 2000 个字符参与计算。也可以改用更轻量的情感词典方案。5.5 结果里全是“没有”“一个”等无用词停用词表不够完整。可以从网上下载通用中文停用词表再往里面补充网文常见词比如“知道”“说道”“心里”“时候”“已经”等。这里放一个扩展示例知道 说道 心里 时候 已经 现在 这个 那个 自己 他们 它们 我们 你们停止词的目的是让统计结果更聚焦于真正的关键词不需要一步到位可以边看词频结果边迭代补充。5.6 输出 CSV 用 Excel 打开乱码在to_csv中务必使用encodingutf-8-sig。如果还是乱码检查 Excel 版本是否有兼容问题可以直接用 pandas 输出 Excel 文件试试df.to_excel(output/词频统计.xlsx, indexFalse)这时需要额外安装 openpyxlpip install openpyxl6. 最佳实践与工程建议6.1 处理超大文本时要控制内存一部长篇同人文全文可能有 50 万字甚至更多分词后的词条数量接近百万级别。直接用 Counter 统计没问题但做情感分析时如果一次性加载全文内存占用会明显上升。建议策略逐章读取而不是一次性读入全部内容。使用生成器逐行处理文本避免把多个中间结果同时保存在内存中。只保留 top 200 的词频结果用于词云图不需要保存全量词频字典。6.2 自定义词典要分层维护不要把所有词都塞进代码里。工程化做法是准备一个custom_words.txt文件按类别维护# 角色名 小智 小刚 小霞 # 传说宝可梦 超梦 梦幻 # 特殊能力 真实之眼 波导之力代码启动时读取这个文件def load_custom_words(path): if not os.path.exists(path): return with open(path, r, encodingutf-8) as f: for line in f: word line.strip() if word and not word.startswith(#): jieba.add_word(word, freq1000)这样做的好处是分析不同小说时只需要替换文本和词典不需要改代码。6.3 结果可解释性优先于指标词云图好看但只能做初步观察真正值得沉淀的是文本结论。例如通过词频统计发现“波导之力”出现次数集中在第 30 到 60 章说明这一阶段是围绕超梦或路卡利欧的核心剧情情感曲线在第 70 章出现低谷可能是主角挫败的章节。这些才是可以写进分析报告的内容。建议在输出可视化图片的同时导出按章节词频的 DataFrame方便定位特定词在哪个章节出现最多。def word_frequency_by_chapter(chapters, target_word): records [] for i, chapter in enumerate(chapters): count chapter.count(target_word) records.append({章节: i 1, 次数: count}) return pd.DataFrame(records)6.4 安全与合规提醒分析的小说文本请使用自己拥有版权或获得授权的材料不要将他人作品全文上传到公共服务器。生成的分析报告如果包含原文片段注意引用篇幅避免过度引用。代码只做本地文本处理不会联网上传数据但安装第三方库时要从官方 PyPI 源安装避免使用未知镜像。6.5 可扩展方向本项目目前还是“离线文本分析”。在实际工程中你还可以从以下方向继续扩展结合 Flask 或 Streamlit 做一个 Web 版分析工具上传 txt 后在线生成词云。将角色名、地点、宝可梦名称做成实体识别模型替代硬编码词典。引入章节相似度计算找出章节之间的重复片段或伏笔关联。使用 PySpark 处理超大规模语料库支持批量分析多部小说。7. 聊到最后的几点经验这篇文章从“真实之眼”这个概念出发实际落地了一个可运行的宝可梦同人文文本分析工具。我们实现了自动编码识别、文本清洗、分词、词频统计、词云图和情感趋势分析。整体项目没有使用复杂算法但已经覆盖了文本挖掘的常见环节。在运行代码的过程中你可能会发现分词结果和预期不一致——这太正常了。文本分析本身就是一个反复调整词典和清洗规则的过程。只要能把“看穿”宏观结构这件事做出来再一步步优化细节就已经超过了大多数只停留在概念层面的教程。如果你正在写宝可梦同人这个工具也可以用来分析自己作品的节奏比如哪一章的“羁绊”主题词密度过高哪个角色的戏份被意外压缩。写作本身需要直觉但数据可以帮助你验证直觉。最后说一句实在话分词、词频、情感值这些量化结果永远只是文本理解的辅助。真正打动读者的永远是“波导之约生死不离”这样具体的情节设计和人物关系而不是一张漂亮的词云图。数据分析能帮你回顾和迭代但创作的核心始终在作者心中。如果本文的代码或思路帮你节省了一点时间那就值得了。
返回列表