简介面向Python数据挖掘课程学习者这份资源演示了如何调用SnowNLP库对豆瓣《肖申克的救赎》评论文本进行情感分析与词云展示帮助读者在掌握分类、聚类算法后拓展自然语言处理的基础思路。压缩包共10个文件、约37KB包含8个Python脚本、1个csv评论数据集和1个txt辅助文本脚本覆盖评论预处理、分词、SnowNLP情感得分计算与词云可视化等环节适合教学演示与自学练习。资源源自Eastmount的系列教学文章已有16948人浏览学习内容基础易懂且可直接运行尤其适合刚接触Python文本挖掘的读者。通过配套代码与数据读者能完整走通从豆瓣评论读取到情感极性分析、再到词云展示的流程并学习如何借助现成库快速搭建情感分析小实验为后续更复杂的NLP项目提供参考。1. 基于SnowNLP的豆瓣评论情感分析一份开箱即跑的完整工程豆瓣短评几乎是中文情感分析领域最理想的练手数据文本短、口语化重、情绪表达直接。市面上大部分教程只会教你调一个SnowNLP(text).sentiments拿到 0 到 1 的分值然后画一张饼图收工。这套代码不是那样。它是一个能直接跑通的完整工程从豆瓣短评采集、清洗、情感评分、情绪分布统计到词云可视化一把梭到底中间还内置了停用词过滤、打分阈值调整和结果导出拿来改一改就能接其他数据源。有个最反直觉的结论得先说在前面SnowNLP 默认模型是在电商购物评论上训练的直接扔电影短评进去准确率大概在七成上下不是它不行而是领域偏差没处理。这份工程里给出了怎么用少量人工标注把模型拉回正轨的完整思路。适合谁两类人第一类是正在做课程设计、毕业设计或作品集需要一套数据采集到可视化的完整体验第二类是工作中突然接到把这个评论区给我做一波用户情绪分析的需求想快速看到可行方案。项目技术栈就是 Python 3、requests、SnowNLP、jieba、wordcloud 和 matplotlib没有任何重型框架。2. 整体架构与目录结构一条数据从抓取到出图的完整链路2.1 五个模块怎么各司其职整个项目按职责拆成五个文件遵循的是采集与解析分离、分析独立、可视化兜底的思路。第一眼看上去不像工业级工程但胜在每一层都能单独替换。采集层哪天从豆瓣换成微博、淘宝评论只需改采集器一个文件分析层从 SnowNLP 换成情感词典或大模型接口业务逻辑完全不会受到牵连。这种低耦合结构才是这类小工程最划算的组织方式。工程依赖的核心库只有六个全部是纯 Python 实现没有需要编译的底层包安装成功率非常高。为了不让读者去猜依赖版本工程里放置了一份requirements.txt其中snownlp、jieba、wordcloud这三个库是功能核心requests、beautifulsoup4、matplotlib负责抓取和绘图pandas负责中间数据的结构化输出。2.2 推荐的项目落盘结构我自己在复现这个工程时会先建一个douban_sentiment/工作目录把代码与产物分文件夹归类。数据文件、图片输出、停用词表各归其位既方便重跑也方便后期接定时任务把整个流程自动化。停用词表单独放的好处是你可以随时往里面加词不用改代码。停用词的质量直接影响词云的可读性这一步懒不得。douban_sentiment/ ├── requirements.txt ├── config.py # 全局参数目标URL、阈值、停用词路径 ├── crawler.py # 短评采集器分页抓取、限速、去重、落地CSV ├── analyzer.py # 情感分析批量评分、情绪分布、导出标注集 ├── wordcloud_gen.py # 词云生成jieba分词、停用词过滤、形状裁剪 ├── stopwords.txt # 停用词表可自行扩充 ├── output/ │ ├── comments.csv # 采集去重后的原始短评 │ ├── sentiment_result.csv │ └── wordcloud.png这套目录结构不是拍脑袋想出来的它对应的是三条稳定输出链路crawler.py只产生 CSVanalyzer.py只消费 CSV 并产出新的 CSV 和统计指标wordcloud_gen.py只消费 CSV 产出图片。你在中间任何一环替换数据源都不需要动另外两个文件。当前工程将豆瓣的某部热门电影作为采集目标但通过修改 config 中的评论页码参数即可平滑扩展到任意电影条目。2.3 运行流程的视角从main.py或手动依序执行的角度看正常流程是采集、分析、出图三步走。采集阶段需要考虑抓取强度工程内建的限速机制会对每次请求之间随机休眠因而本地跑完 500 条短评大致需要一到两分钟耗时的瓶颈在网络 IO 而非计算。SnowNLP 的单条短评分析在纳秒到毫秒级500 条总数算下来完全不需要多线程。这一版工程刻意不引入并发省去了对反爬策略的额外负担也让代码更具教学价值。调参的重点是config.py中的情感阈值系统默认将sentiments值大于等于 0.6 判为积极小于等于 0.4 判为消极中间值归为中性。真正跑完一批数据会看到中性比例往往偏高因为很多短评本身就是客观描述或剧情讨论这正说明阈值设计要依据语料动态微调。3. 核心模块拆解从评论采集到词云落地的代码实战3.1 采集器requests 抓页面再用 BeautifulSoup 解析整个项目的起点是爬虫模块。有人喜欢直接用豆瓣 API但真实开发场景里接口权限或频率限制很难绕开。这份工程采取的是传统抓取策略构造请求头模拟浏览器用requests.Session维持连接按分页规则抓取评论列表页并抽取短评内容。值得注意的是豆瓣对未登录访客开放了前 10 页短评而短评列表页的数据是直接渲染在 HTML 里的不需要额外解析 JSON 接口。# crawler.py 核心代码 import time import random import requests from bs4 import BeautifulSoup import pandas as pd def fetch_comments(movie_url, pages5, output_pathoutput/comments.csv): session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, }) all_comments [] seen set() for page in range(pages): params { start: page * 20, limit: 20, status: P, sort: new_score, } # 反爬血泪经验短评接口对频率极敏感随机停顿比固定停顿效果好得多 time.sleep(random.uniform(2, 4)) resp session.get(movie_url, paramsparams, timeout10) resp.raise_for_status() soup BeautifulSoup(resp.text, html.parser) for tag in soup.select(span.short): text tag.get_text(stripTrue) if text and text not in seen: seen.add(text) all_comments.append({comment: text, page: page}) if len(all_comments) pages * 20: break df pd.DataFrame(all_comments, columns[comment, page]) df.to_csv(output_path, indexFalse, encodingutf-8-sig) return df核心逻辑就两步构造分页参数、解析span.short标签。sortnew_score表示按热门排序拿到的评论热度权重比较均匀。你可能会问encodingutf-8-sig和utf-8的区别前者在 Excel 里打开 CSV 不会出现中文乱码这是发过牢骚之后养成的习惯。raise_for_status()负责把 HTTP 4xx、5xx 错误直接抛出来避免后续解析到空页面时留下没有任何提示的静默失败。3.2 情感分析SnowNLP 的分数到底怎么解读情感分析模块是全项目技术含量最高的部分。SnowNLP 的sentiments属性返回的是一个 0 到 1 的浮点数表示文本为正面的概率。0.5 是分水岭但并不是绝对中轴因为贝叶斯模型的先验概率受训练语料影响电商场景下中性表达也会被轻微压向负面所以理想的中性区间应该是 0.4 到 0.6。分析器在工作时批量读取采集结果 CSV逐条计算分值并打标签然后把全量数据另存一份带分数的新表。# analyzer.py 核心代码 from snownlp import SnowNLP import pandas as pd def analyze_comments(csv_path, pos_th0.6, neg_th0.4, output_pathoutput/sentiment_result.csv): df pd.read_csv(csv_path) scores, labels [], [] for text in df[comment]: s SnowNLP(str(text)) score s.sentiments # 返回正面概率越接近1越积极 if score pos_th: label pos elif score neg_th: label neg else: label neu scores.append(round(score, 4)) labels.append(label) df[score] scores df[label] labels distribution df[label].value_counts().to_dict() df.to_csv(output_path, indexFalse, encodingutf-8-sig) total len(df) stats {k: round(v / total * 100, 2) for k, v in distribution.items()} return df, stats调用SnowNLP(text).sentiments时内部会先分词再计算所以不需要你提前单独调用 jieba。真正需要开发者上心的只有阈值。工程给出的默认参数是 0.6 和 0.4当你处理的是严重口语化的短评时可能要把中性区间收窄到 0.45 至 0.55 才能看到更鲜明的情绪分层。3.3 词云生成jieba 分词和停用词过滤是灵魂词云模块有两个容易被忽略的细节一是 jieba 默认分词对电影名字和演员名这类专有名词非常敏感一个完整的片名被切开成三五个孤零零的字这在词云里呈现出来十分难看二是词云的字体参数在 Linux 服务器上常见缺字体导致乱码方块。这两点工程内部都提前处理。分词之后接停用词表过滤再把过滤后的词以空格拼接喂给 wordcloud 的generate方法。# wordcloud_gen.py 核心代码 import jieba from wordcloud import WordCloud import matplotlib.pyplot as plt def gen_wordcloud(csv_path, stopwords_path, output_pathoutput/wordcloud.png): df pd.read_csv(csv_path) with open(stopwords_path, encodingutf-8) as f: stopwords set(f.read().split()) # 只对积极评论做词云这样能看出观众夸电影的点在哪 pos_text .join(df[df[label] pos][comment]) words jieba.lcut(pos_text) filtered [w for w in words if w.strip() and w not in stopwords and len(w) 1] # 单字词大多是语气词或噪声 wc WordCloud( font_pathC:/Windows/Fonts/simhei.ttf, # Windows 用黑体Mac 换成 PingFang.ttc width800, height600, background_colorwhite, max_words200, ).generate( .join(filtered)) wc.to_file(output_path) plt.figure(figsize(10, 6)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.savefig(output_path, dpi300, bbox_inchestight)代码里四个关键点len(w) 1保证进词云的词至少两个字符切片词、语气词最先被过滤max_words200限定词云只展示最重要的 200 个词避免画面被低频词填满interpolationbilinear让词云边缘平滑不出现锯齿dpi300直接生成符合印刷质量的图片。如果你试着把pos_text换成正态全评论分词你会看到词云里全是剧情关键词而非情绪词那就失去了情感分析词云的意义。3.4 结果导出与中间数据工作流中还有个小加分项是输出分析后的sentiment_result.csv每一行包含原始短评、情感分数、积极/中性/消极标签。这个 CSV 有两个用途一是你可以快速在 Excel 里筛选置信度低于 0.1 的短评人工读一遍看模型是不是真的错判这些错判样本是后续微调的训练集来源二是你可以跑一个数据透视比较不同页数评论的情绪均值差异前面页是热评、后面页是时间序评论两者情绪分布往往相差很大。4. 运行环境与快速上手从零到出图只花十分钟4.1 环境准备与依赖安装工程基于 Python 3.8 及以上版本开发不建议用 Python 3.7 或更早版本跑pandas新接口。第一步是创建独立的虚拟环境避免把包装进系统级 Python。Windows 用户打开命令行执行python -m venv venv然后venv\Scripts\activatemacOS 或 Linux 用户执行source venv/bin/activate。虚拟环境这一步看起来多此一举实际上能救你于水火——系统里已经装有其他版本的matplotlib或numpy时直接pip install会引发依赖地狱。依赖安装只用一行命令pip install -r requirements.txtrequirements.txt的内容是六个纯 Python 库加两个传递依赖。安装耗时视网络环境而定一般五分钟内能完成。安装过程中如果看到wordcloud构建失败的报错多数是你的 Python 版本太新或太老比如 3.13最快解法是切换到 3.9 至 3.11 的版本再装因为 wordcloud 的预编译 wheel 只覆盖主要版本这个坑值得记一笔。4.2 改配置、跑流程、看产物跑动之前需要确认config.py中三个关键参数是否正确。第一是采集目标 URL工程默认指到某部高分电影短评页你要换成其他电影需要记住只替换subject/xxxxx部分第二是短评页码默认 5 页代表最多 100 条这是豆瓣未登录状态下的匿名可读上限边界第三是情感阈值首次跑建议保持默认值拿到分布结果后再回头调。整个流程的启动没有任何花哨操作按顺序在项目根目录执行三条命令即可python crawler.py python analyzer.py python wordcloud_gen.py每一条命令都有清晰的终端输出crawler 会在控制台逐页打印抓取条数analyzer 会打印情感分布的比例字典wordcloud_gen 则输出去重后的词数。这三条命令之间没有参数传递依赖因为中间结果全部落到output/目录这也方便你只重跑某一步而不用连坐后面所有步骤。如果你的数据量超过 500 条爬虫模块的限速逻辑会明显拉长采集时间届时可以调低pages参数控制在两分钟内跑完。全部完成后打开输出目录你会同时拿到原始评论表、带分数和标签的分析结果表、以及一张高清词云图一篇项目报告的配图和数据表素材到这步就齐了。5. 避坑指南我在复现这套项目时的四个翻车现场5.1 豆瓣短评页改版导致解析不到评论现象requests请求能拿到 200 状态码但select(span.short)返回空列表爬虫静默结束且没报错。原因豆瓣页面结构经过改版web 站点的短评列表对未登录用户实际可用的字段降级部分电影条目需要添加ck参数或使用完全不同的移动端接口。解决五条以内的评论隐藏在页面源码的div.comment-item结构里且新版本中短评内容由span.short变为span.rate-content时优先升级包的解析逻辑抓取前先手动打开页面右键检查确认选择器再写死。从那以后我每次写爬虫都强制先做一步页面结构探活用print(soup.prettify()[:500])看一眼再往下写。5.2 情感阈值一刀切导致中性占比过高现象跑完 500 条短评中性占比超过六成图表看起来毫无区分度。原因默认阈值 0.4 到 0.6 的中性区间对于豆瓣短评这种表达含蓄的语料过宽大量还可以能看但没惊艳被划进中性。解决先输出分数分布直方图看分数在 0.4 和 0.6 附近是否有明显堆积如果有收窄区间到 0.45 和 0.55。这种按数据分布反向调阈值的行为是分析工程师的基本功参数本身不值得迷信。5.3 词云里全是乱码方块现象词云图里的汉字全部显示为方框图片等于废品。原因wordcloud 默认字体不支持中文源码里font_path指向的路径不存在或 Linux 服务器上根本没有中文字体文件。解决代码里font_path的值改成你系统里真实存在的字体路径。Windows 查看C:/Windows/Fonts/simhei.ttf是否存在macOS 换成/System/Library/Fonts/PingFang.ttcLinux 可以先执行fc-list :langzh找可用中文字体。这个坑 100% 会出现项目代码首版统一内置了这个参数跑完如果还是乱码就检查两件事路径是否存在、字体文件是否为 TrueType 格式。5.4 SnowNLP 默认模型对短文本系统性歧视现象一些明显积极的短评如太喜欢了打分只有 0.3被评为消极。原因SnowNLP 内置训练语料是电商评论口癖、夸张表达、表情符号在电商语料里出现分布完全不同而豆瓣用户大量使用反讽和网络流行语。解决用 5.2 节产出的错判样本挑 200 条人工打标后调用SnowNLP的train接口做领域微调在领会到微调逻辑之前可以先跑出一个按标签分列的准确率表格确认消极类的错误率远高于积极类再动手。这正是这份工程把标注样本和情感结果统一落到 CSV 的原因让你不至于为了微调又重新爬一次数据。6. 二次开发把这套工程改造成你自己的情感分析工具6.1 让模型更懂你的语料领域微调实操对 SnowNLP 做领域微调是投资回报率最高的一个改动。调用接口前先准备好带标签的训练数据格式是每行一句评论加一个__label__pos或__label__neg后缀的 txt 文件。然后只需要短短三行代码from snownlp import sentiment sentiment.train(custom_train.txt, custom_model.pkl) sentiment.save(custom_model.pkl)训练完把SnowNLP内部使用的sentiment类替换成新模型后续线上预测全部走本地模型路径。注意训练集每类最好不低于 500 条不然新模型会被明显的数据稀疏带偏。真实项目中我一般会把采集到的错判样本持续沉淀进训练集每两周重训一次这种预测、纠错、重训的飞轮是这套小型工程最值得保留的运作方式。6.2 三个实战改造方向想要支持新平台只需要替换crawler.py的解析函数保持DataFrame输出列名与comment字段不变其余模块与视觉部分无需改动。这里有个技巧把解析逻辑抽象成parse_html(html) - list[dict]接口函数返回的字典统一包一层comment键适配新的平台也就是换一下请求头和选择器而已。维度扩展是在analyzer.py中增加按page字段分组统计的逻辑这样可以观察热评区与最新评论区的情绪漂移现象。交互式调参是为config.py接入argparse命令行参数这样跑自动化脚本时不用反复改文件一条python analyzer.py --pos_th 0.55就能完成试验。更细一步可以并行做按评分的折线图与按标签的堆叠柱状图一屏看出整体走向。6.3 验证改造效果回到真实数据改完系统的最后一步永远是验证而不是自信上线。我习惯预留 200 条人工标注结果计算精确率、召回率和 F1对比微调前后的混淆矩阵。一套完整方案的标准是准确率从基线 70% 提升到 80% 以上且不会因为模型拟合训练集而在新数据上掉点。整个改造过程的测量指标全部建立在sentiment_result.csv之上这是一份会自己生长的数据资产。从那以后我每次涉及情感分析都会强制走一遍采集、标注、训练、评估四步闭环不再直接裸跑预训练模型就宣布分析完成。希望这份工程能让你少踩几个坑拿到数据的那一刻就是产出结论的倒计时。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?