十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

用Python挖掘剧集评论:从中文分词到情感分析实战

用Python挖掘剧集评论:从中文分词到情感分析实战 十二年过去一部被很多人收进“剧单角落”的日剧《为了N》依然会在深夜被翻出来重温。豆瓣 8.6 分讨论区每隔一段时间就有人重新提起“安希”提起那句“他是我黑暗世界里唯一可以守护的光”。作为一个追剧型程序员我关注的不只是哪对角色更好哭而是另一个问题这种跨越十二年的“后劲”到底能不能用数据描述出来这篇文章我会带着你做一个完整的 Python 文本分析小项目。它不只能用来分析《为了N》也可以复用到任何一部你意难平的剧把评论区、短评、弹幕、自己的二创笔记收集成语料做分词、词频、情感打分、CP 语境共现分析最后生成词云和统计图。整个过程不依赖 GPU不涉及大模型用 Pandas、jieba、SnowNLP、WordCloud 就能跑通。我先给出一个明确判断文艺作品留给观众的后劲并不是玄学。观众的讨论文本里藏着大量可观测的模式高频情绪词、人名搭配、时间分布、语境共现都是可以被挖掘的数据。与其反复写“这部剧太好哭了”之类的感叹不如把它变成一个最小可行数据分析项目用 Python 回答几个现实问题十二年后的讨论到底在聊什么所谓“守护”和“光”的表达是高浓度集中在特定角色关系里还是整部剧的共性“安希”被不断提起时周围最常出现哪些词这篇文章适合刚入门 Python 数据分析、想用兴趣项目练手的人。你不需要有自然语言处理基础只需要电脑上装了 Python 3能运行 pip 安装命令。读完以后你会得到一套可以直接复用的脚本结构也能避开中文文本处理里常见的坑。1. 这篇文章真正要解决的问题很多人追完一部剧会写剧评但纯文字剧评的问题在于它依赖个人表达很难回答“我这种感受是不是个案”。如果有一个社区十几万人打分评论区有成百上千条讨论这些内容本身就是一份真实的大众口碑样本。可惜这份样本没有被好好利用。一方面平台不直接提供方便的批量分析能力人工翻评论效率极低。另一方面影视剧讨论文本包含大量网络黑话、角色昵称、剧情简称“安希”“为了N”这类词如果直接分词会被切成莫名其妙的结果。这正是文本处理要处理的核心问题不是分析自然语言本身而是分析“一部剧粉丝之间使用的方言”。所以这篇文章要解决的技术问题有三个怎么把非结构化的中文评论文本整理成结构化数据并保证不乱码、不重复、不丢失关键信息。怎么让分词工具认识“安希”这种观众自造词而不是把 CP 名切开。怎么在纯规则和轻量模型都不算完美的情况下对粉丝讨论做情感和语境分析同时不下不负责任的结论。如果把问题翻译成更通用的表达这是一个典型的“中文短文本情感与话题挖掘入门案例”。它足够小小到一个人一个周末能跑完它也足够完整完整到覆盖数据读取、清洗、分词、统计、建模、可视化、排错的全流程。2. 项目背景与数据思路先简单回顾项目背景。《为了N》的豆瓣评分长期稳定在 8.6这在日剧里属于相当扎实的口碑。剧集本身情节围绕人际关系的悬疑与救赎展开很多观众在讨论时高频使用“黑暗”“光”“守护”“永远”这类带有强烈情绪色彩的词。标题里那句“他是我黑暗世界里唯一可以守护的光”就是典型的粉丝话语。从技术角度看这句话本身就是一个完美的语料样本。它有明确情绪倾向有角色指向有隐喻词也包含“安希”这个粉丝自定义简称。如果把一百句、一千句类似的表达放在一起统计就能够看到“哪些词是这部作品讨论里真正的高频词”“哪些情绪总是绑定出现”。我把整个项目拆成七步准备一份规模适中的评论语料以 CSV 文件存储。读取后进行清洗去掉 HTML、URL、重复内容、多余空白。使用 jieba 分词并配合自定义词典让“安希”保持完整。统计词频输出 Top N 关键词生成可视化词云。使用 SnowNLP 输出情感分按正/中/负分桶。做“安希”共现分析观察哪些词最常出现在 CP 名附近。输出结果文件最后给出排错清单。这里要特别强调数据合规。豆瓣短评虽然是公共内容但不意味着可以高并发抓取、大规模存储或商用。本文演示采用的思路是你自己准备一个 demo 文件放几条符合格式的示例文本把流程跑通后再替换成你合法获取的语料。如果你有豆瓣账号阅读你自己的“想看/看过”笔记是没问题的如果你想研究社区讨论建议手动整理少量公开评论或者使用平台明确开放的接口。不要为了跑一个兴趣项目去写高并发采集脚本也不要想办法绕过登录限制。这是个学习项目不是数据工厂。3. 环境准备与依赖安装整个项目只需要一台普通电脑操作系统不限重点确保 Python 3 可用。建议创建一个独立目录并在目录里启用虚拟环境避免依赖污染系统 Python。先准备项目目录mkdir n_study cd n_study python3 -m venv venv source venv/bin/activate # Windows 上使用 venv\Scripts\activate然后安装依赖。本文的依赖清单如下# requirements.txt pandas jieba snownlp wordcloud matplotlib安装命令pip install -r requirements.txt如果下载速度慢可以使用国内镜像源例如pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple下面说明每个库的用途库用途一句话说明pandas数据处理负责读取 CSV、清洗、去重、保存结果jieba中文分词把评论拆成有意义的词而不是整段文字snownlp情感分析给每条中文评论输出 0 到 1 之间的情感分数wordcloud词云生成把词频可视化适合做内容总结图matplotlib绘图生成柱状图和情感分布图版本我刻意没有写死。因为这类库迭代较快不同版本 API 基本一致按最新稳定版安装即可。如果后续代码报错提示某个函数不存在再根据报错调整版本这是更稳妥的思路。4. 数据准备与清洗在项目目录下创建 data 子目录并放入一个 CSV 文件。为了不涉及真实用户隐私下面给出的是一个示例语料文件。你可以完全使用它来跑通流程也可以把自己的学习笔记、手写评论按同样的格式替换进去。# data/comments.csv comment,source,date “他是我在这段黑暗里唯一看到的光。”,自存笔记,2026-01-01 “十二年过去还是会为这段关系哭出声。”,自存笔记,2026-01-02 “N是起始字母也是每个人心里藏的结。”,自存笔记,2026-01-03 “安希之间的守护和沉默太戳人了。”,自存笔记,2026-01-04 “比起圆满更打动我的是各自走过至暗时刻。”,自存笔记,2026-01-05 “那句守护不是占有是希望对方好好活下去。”,自存笔记,2026-01-06注意这个文件里的文本是我为演示写的格式样本不是任何真实平台内容的采集结果。之所以用“自存笔记”作为来源是为了演示脱敏思路。真实项目中如果语料来自公开评论且涉及他人表达不要在生成文件里保留用户 ID、头像链接、主页地址等个人信息。接下来写第一个脚本完成读取和清洗。# clean_data.py import pandas as pd def clean_comment(text: str) - str: 清洗单条评论的通用处理函数。 text str(text) # 去掉 HTML 标签 text text.replace([^], , regexTrue) # 去掉 URL text text.replace(https?://\\S, , regexTrue) # 去掉 用户名 形式 text text.replace(\\w, , regexTrue) # 多余空白压缩成单个空格 text text.replace(\\s, , regexTrue) return text.strip() df pd.read_csv(data/comments.csv, encodingutf-8) df[comment] df[comment].map(clean_comment) df df.dropna(subset[comment]) df df.drop_duplicates(subset[comment]) df.to_csv(comments_clean.csv, indexFalse, encodingutf-8-sig) print(df.head()) print(f清洗后剩余评论数: {len(df)})这段代码解决了几个常见问题第一评论里可能包含 HTML 转义内容尤其是从网页端复制的文本直接分词会把br这种标签也纳入统计必须先剔除。第二URL 和 用户信息对语义分析没有帮助甚至会造成噪声直接清掉。第三有些观众会重复发同一句话表达情绪分析样本时重复评论会放大特定句子的权重需要按评论内容去重。第四保存 CSV 时使用utf-8-sig编码。这一点很多人会踩坑如果用纯utf-8保存再拿 Excel 打开中文可能显示成乱码因为 Excel 默认以带 BOM 的 UTF-8 识别文件。utf-8-sig能解决跨软件打开乱码的问题。在真实项目里清洗策略要根据数据来源调整。如果做的是弹幕分析可能还要去掉纯数字、纯语气词、重复刷屏内容如果做的是同人文分析可能还要做段落切分。先保持最小可用的清洗逻辑后续再按需扩展。5. 分词与词频统计分词是中文文本分析里最基础也最容易出问题的一步。英文单词天然有空格分隔中文没有所以要用 jieba 这样的工具把句子切成词。先准备两个辅助文件。第一个是自定义词典用来让 jieba 认识粉丝向词汇。在项目根目录创建userdict.txt安希 5 nz 守护 5 v 至暗时刻 5 n 为了N 5 nz自定义词典的格式是“词语 词频 词性”词频数字越大越倾向于被识别为独立词。如果没有这一行安希很容易被切分成“安”和“希”两个单字。词性标识不参与结果统计这里用nz表示专有名词v表示动词n表示名词。如果你要加入新的角色昵称比如其他 CP 名或角色口头禅直接按这个格式加进去即可。第二个是停用词表。像“但是”“可以”“自己”“真的”这类词在每一篇讨论都会高频出现但对于分析剧集话题帮助不大。在stopwords.txt中维护一份停用词集合这个 那个 我们 你们 自己 真的 可以 就是 应该 因为 所以 但是停用词表没有统一标准取决于你的语料。跑出第一版词频后如果发现噪声集中在某个功能词上就把这个词加进停用词表再跑一轮。这是一个反复调优的过程。接下来是分词与词频统计脚本。# analyze_words.py import jieba from collections import Counter import pandas as pd STOPWORDS_FILE stopwords.txt USERDICT_FILE userdict.txt INPUT_FILE comments_clean.csv jieba.load_userdict(USERDICT_FILE) stopwords set() with open(STOPWORDS_FILE, r, encodingutf-8) as f: for line in f: word line.strip() if word: stopwords.add(word) df pd.read_csv(INPUT_FILE, encodingutf-8-sig) full_text .join(df[comment].tolist()) words [word for word in jieba.cut(full_text) if word.strip()] words [word for word in words if word not in stopwords] words [word for word in words if len(word) 1] counter Counter(words) top_words counter.most_common(30) print(词频 Top 30:) for word, count in top_words: print(f{word}\t{count})运行python analyze_words.py基于本文开头的示例语料输出会接近下面这种形式。这是示意结果不是真实社区结论词频 Top 30: 守护 3 光 2 黑暗 2 安希 2 ……在这个简单例子里“守护”出现次数最高符合原句“唯一可以守护的光”的核心意象。“安希”作为一个整体被保留下来而不是被切成“安”“希”说明自定义词典生效了。看词频时要保留一点距离。语料规模小任何统计都只能代表样本自身不能说它是全豆瓣观众的共识。只有当语料足够大、来源足够多样时词频结果才具有更强的参考价值。本文演示的是方法不是结论。6. 情感分析初体验情感分析是自然语言处理里让人最有直觉感的任务。简单说就是让程序判断一段话的情绪倾向是正向还是负向。本文使用 SnowNLP它不需要训练安装之后可以直接调用对中文电影评论、社交媒体短文本有基本的区分能力。脚本如下# analyze_sentiment.py import pandas as pd from snownlp import SnowNLP INPUT_FILE comments_clean.csv df pd.read_csv(INPUT_FILE, encodingutf-8-sig) scores [] for comment in df[comment]: try: score SnowNLP(comment).sentiments scores.append(score) except Exception: scores.append(None) df[sentiment] scores df df.dropna(subset[sentiment]) df[emotion_level] pd.cut( df[sentiment], bins[0, 0.4, 0.6, 1.0], labels[负向, 中性, 正向] ) df.to_csv(sentiment_result.csv, indexFalse, encodingutf-8-sig) print(df[[comment, sentiment, emotion_level]])运行时你会注意到一个现象很多看起来很“悲伤”的句子情感分并不一定低。比如“会为这段关系哭出声”SnowNLP 可能把“哭”识别为负向但这句话在粉丝语境中其实是“被打动”的正向表达。这是 SnowNLP 的固有限制。它使用的是通用语料训练出来的模型对影视圈粉丝话语不敏感。一个词是正向还是负向往往要结合上下文甚至要结合特定作品来判断。所以我在脚本里加了emotion_level分桶但任何阈值划分都是为分析服务的简化不是绝对标准。真实项目中如果想让情感分析更贴合剧粉语境有两个改进方向。第一个方向是准备几百条已标注语料训练一个小型分类器。第二个方向是自建领域情感词典把“意难平”“哭死”“戳人”这类词按作品的粉丝语境重新打标。切记不要直接把模型输出当成平台口碑的最终结论情绪表达极度依赖语境。7. CP 语境共现分析接下来是本文最有意思的部分。如果我们想知道“安希”被提起时观众习惯用什么词、什么意象围绕它就可以做一次共现分析。这比简单统计“安希出现了多少次”更深入一层。共现分析背后的逻辑是一个词的意义往往由它周围的词决定。单独看“守护”可能没什么感觉但如果在很多条评论里“守护”都出现在“安希”前后五个词范围内这就说明粉丝讨论中确实存在一种稳定的语境绑定。实现方式如下# analyze_cooccurrence.py import pandas as pd import jieba from collections import Counter INPUT_FILE comments_clean.csv USERDICT_FILE userdict.txt TARGET_WORD 安希 WINDOW_SIZE 5 jieba.load_userdict(USERDICT_FILE) stopwords set() with open(stopwords.txt, r, encodingutf-8) as f: for line in f: word line.strip() if word: stopwords.add(word) df pd.read_csv(INPUT_FILE, encodingutf-8-sig) target_df df[df[comment].str.contains(TARGET_WORD, naFalse)] co_counter Counter() for comment in target_df[comment]: word_list [w for w in jieba.cut(comment) if w.strip()] positions [i for i, w in enumerate(word_list) if TARGET_WORD in w] for pos in positions: start max(0, pos - WINDOW_SIZE) end min(len(word_list), pos WINDOW_SIZE 1) window_words word_list[start:end] for w in window_words: if w word_list[pos]: continue if w not in stopwords and len(w.strip()) 1: co_counter[w] 1 print(安希 共现词 Top 15) for word, count in co_counter.most_common(15): print(f{word}\t{count})这个脚本的核心是定位安希在分词列表里的所有位置然后取前后五个词作为窗口。窗口里的非停用词都会被累计计数。最终结果能反映“当观众提到安希时语境里还装着什么”。示例语料运行后你可能会看到“守护”“光”“沉默”“关系”等词排在前面。这个结果本身不算意外但它完成了从定性到定量的转化我们就可以说“安希”的讨论语境里防守、沉默、羁绊是高频伴奏词而不只是靠印象做判断。这个思路很容易迁移到其他作品。比如分析某位角色的讨论可以把TARGET_WORD改成角色名看观众讨论角色时最常提及谁、哪种情绪、哪个剧情场景。这对同人创作选题、内容盘点、社区运营分析都有实际价值。需要提醒的是共现不等于因果。“安希”和“光”经常同时出现只能说明观众倾向于把光这个意象放在这段关系里表达不能据此推出剧情中这段关系真的代表了某种价值观。技术分析描述的是话语现象不负责解释作品本质。8. 可视化输出数据分析缺少可视化会失去很多信息。词云和柱状图是两种最直观的输出形式。先看词云。中文词云最关键的坑是字体。WordCloud 默认字体不支持中文不指定font_path时生成图片大概率是满屏方块。Windows 系统可以指定宋体或微软雅黑路径通常是C:/Windows/Fonts/msyh.ttcLinux 系统需要先确认安装了中文字体比如文泉驿正黑。# make_wordcloud.py import pandas as pd import jieba from collections import Counter from wordcloud import WordCloud import matplotlib.pyplot as plt 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) df pd.read_csv(comments_clean.csv, encodingutf-8-sig) full_text .join(df[comment].tolist()) words [w for w in jieba.cut(full_text) if w.strip() and w not in stopwords and len(w) 1] text_for_cloud .join(words) font_path C:/Windows/Fonts/msyh.ttc # Windows 示例 # font_path /usr/share/fonts/truetype/wqy/wqy-zenhei.ttc # Linux 示例 wordcloud WordCloud( font_pathfont_path, width800, height600, background_colorwhite, max_words200, ).generate(text_for_cloud) plt.figure(figsize(10, 8)) plt.imshow(wordcloud, interpolationbilinear) plt.axis(off) plt.tight_layout() plt.savefig(wordcloud_result.png, dpi150) plt.show()如果你希望展示情感分布可以画一个简单柱状图。下面的脚本读取sentiment_result.csv统计三种情感档位各有多少条评论。# plot_emotion.py import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(sentiment_result.csv, encodingutf-8-sig) counts df[emotion_level].value_counts().reindex([负向, 中性, 正向]) plt.figure(figsize(8, 6)) plt.bar(counts.index, counts.values) plt.title(评论情感倾向分布) plt.ylabel(评论数) for i, v in enumerate(counts.values): plt.text(i, v 0.1, str(v), hacenter) plt.tight_layout() plt.savefig(emotion_distribution.png, dpi150) plt.show()这里再次提醒本文示例语料只有几行画出来的情感分布没有统计意义只是演示脚本用法。当你替换成真正的大规模语料后这张图才有分析价值。9. 完整运行与效果验证为了让读者能直接复用下面给出项目的完整文件结构n_study/ ├── data/ │ └── comments.csv ├── venv/ # 虚拟环境目录通常不提交到 git ├── userdict.txt ├── stopwords.txt ├── requirements.txt ├── clean_data.py ├── analyze_words.py ├── analyze_sentiment.py ├── make_wordcloud.py ├── plot_emotion.py ├── comments_clean.csv # 脚本生成的中间结果 ├── sentiment_result.csv # 脚本生成的分析结果 ├── wordcloud_result.png # 脚本生成的图 └── emotion_distribution.png # 脚本生成的图正确的执行顺序是python clean_data.py python analyze_words.py python analyze_sentiment.py python make_wordcloud.py python plot_emotion.py每一步的验证标准如下clean_data.py运行后控制台打印数据行数同时当前目录生成comments_clean.csv。如果没有报错且 CSV 打开后中文不乱码说明第一步成功。analyze_words.py运行后能打印出“词频 Top 30”列表。重点检查安希是否被完整保留。analyze_sentiment.py运行后生成sentiment_result.csv里面每条评论应有一个 0 到 1 之间的情感分和一个情感档位。make_wordcloud.py运行后如果当前目录出现非空 PNG 图片且图片文字不是乱码说明词云成功。plot_emotion.py运行后生成情感分布图如果没有报IndexError且三个柱状标签完整说明情感分桶逻辑正确。如果最终流程图没有完整跑通不要急着怀疑代码逻辑先按第 10 章的排查表逐项检查环境和文件格式。10. 常见问题与排查方法中文数据分析看着简单实际跑起来会遇到很多和环境、编码、分词相关的问题。下面是我整理的高频问题清单。问题现象可能原因排查方式解决方案读取 CSV 报UnicodeDecodeError文件编码不是 UTF-8用 VSCode/记事本打开看右下角编码统一保存为 UTF-8 或 UTF-8 with BOM生成的 CSV 用 Excel 打开中文乱码缺少 BOM 标识用文本编辑器查看文件头部保存时指定encodingutf-8-sig词云图片上全是方块未指定中文字体或字体路径错误检查font_path是否存在换成系统已安装的中文字体Linux 先安装中文字体安希被分成两个字jieba 不认识自定义词打印jieba.lcut(安希)确认userdict.txt内容并重新调用jieba.load_userdict词频统计里全是“这个、那个、可以”停用词表缺失或过小打印 Counter 原始结果把高频无意义词加入stopwords.txtSnowNLP 情感分全部集中在 0.5 附近评论情绪表达含蓄、模型不敏感抽样检查得分曲线自建领域词典或标注少量样本后训练专用模型matplotlib 画图没有中文matplotlib 默认字体不含中文观察图片文字是否为方块在图中设置支持中文的字体或用 WordCloud 渲染后再用 PIL 拼图采集评论时收到平台限制请求频率过高查看返回状态码停止抓取改用官方接口或本地已保存数据这里单独强调一下最容易被忽略的分词问题。如果你看到“安希”相关的共现结果异常比如 window_words 里出现了大量单字通常是自定义词典没生效。解决方式是在分析脚本最前面调用jieba.load_userdict且要在执行jieba.cut之前加载。如果你已经运行过一次交互式 Python 环境再加载词典后某些缓存可能不会立刻更新最直接的办法是重启 Python 进程再跑。情感分析还有一个隐蔽问题如果例句中带有引号和中括号SnowNLP 对特殊符号的容忍度有限。清洗脚本最好把中文全角标点替换为空格或句号避免长句被当成一个超长词。遇到异常时我的建议是先打印被分析的文本确认清洗后的内容是否还是完整的一句话。11. 最佳实践与工程建议把兴趣项目做到可运行只是第一步。如果你希望把它变成一个正式的分析项目或者分享给团队使用下面这些工程建议会很有帮助。第一隔离依赖。所有依赖只用requirements.txt管理还不够更稳妥的方式是在项目初始化时固定一个 Python 版本并把requirements.txt里核心库的版本也固定下来。这样即使半年以后重新运行分析结果也能保持可复现。第二中间产物不提交到 Git。comments_clean.csv、sentiment_result.csv、PNG 图都属于生成产物它们会在每次运行后变化。建议在.gitignore中加入venv/ __pycache__/ *.pyc comments_clean.csv sentiment_result.csv wordcloud_result.png emotion_distribution.png第三数据脱敏要前置。如果你的语料确实来自公开评论区不要保存评论区页面里的用户 ID、点赞数、回复树、主页链接等无关字段。分析目标是“文本”不是“用户行为画像”。给每条数据保留一个抽象来源标签即可例如“平台A”“用户自存笔记”这样既尊重隐私又不会丢失来源维度的观察能力。第四迭代式清洗。不要试图一次写出完美的清洗逻辑。正确的方式是先跑通打印中间结果观察数据分布再把新的、没有处理干净的噪声模式加进清洗函数。比如你可能发现很多弹幕文本里有“前方高能”“名场面”这样的词如果分析目标是剧情相关词就需要把它们纳入停用词或者单独建一个“氛围词表”。第五不要过度解读小样本。任何文本分析项目都会面对样本偏差。评论多的人更可能发声体验不极致的观众可能根本不评论。也就是说我们分析的是“愿意在评论区表达的人”不是所有观众整体。结论放在正文时要使用“从样本看”“在讨论这个作品的人里”这样克制的措辞。第六情绪表达有强烈语境性。本文用的 SnowNLP 只是一个轻量基线模型更适合做探索而不是做裁判。如果这个项目要继续往前推进更推荐考虑使用更成熟的中文预训练模型对最终结论做人工复核把正负情感两分类改成“剧情讨论”“情感抒发”“角色偏爱”“其他”这样的内容分类对“光”“黑暗”“守护”这类意象词做单独的词频追踪观察它们在时间上的变化。这个方向比追求某个情感分准确率更贴近内容研究本身。12. 结语与后续方向这整套代码跑下来你会得到一份关于《为了N》讨论语料的词频表、情感分布图和 CP 语境共现结果。它会告诉你一些印象之外的细节哪些情绪反复出现、哪些词绑定关系更紧、哪些表达隐藏在十二年前的讨论颗粒里。对一个怀念型观众来说这份技术成品本身就是一种纪念方式。它不是观后感但它以另一种方式回答了“这些年我们到底在为什么而心动”。如果你熟悉这部电影或者想拿其他作品做练习建议先跑通《为了N》的最小示例再把自己的语料替换进去看差异。尝试给 userdict 加入其他角色昵称把 TARGET_WORD 改成你想观察的任意角色逐步扩大语料规模观察“意难平”到底是集中在某个角色关系里还是整部剧的普遍情绪。你会发现这种追问比单纯刷弹幕更有意思。收藏这篇文章下个周末挑一部你最喜欢的剧试试把它变成数据。
返回列表