SnowNLP情感分析实战:豆瓣短评抓取与词云可视化全流程
发布时间:2026/9/30 16:27:23来源:尧图网络
简介这份基于SnowNLP的豆瓣评论情感分析及词云分析资源面向Python数据挖掘初学者与高校课程学员解决调用情感分析库对豆瓣短评进行情感极性判断与词云展示的入门问题。资源包含10个文件核心为8个按编号组织的Python脚本呈递进式设计覆盖评论数据读取、中文分词、情感得分计算、词云绘制等完整流程另附1个csv评论文本数据与1个txt语料素材可直接运行调试整包仅37KB轻量易获取。目前已有16945人学习使用。通过脚本与数据读者能够理解情感分析从文本预处理到结果可视化的基本思路掌握SnowNLP库的常用接口学会将非结构化评论转化为情感得分与词云图并能替换自己的数据完成迁移复用为课程作业或后续机器学习情感分类研究打下基础。1. 为什么用 SnowNLP 拆豆瓣短评一条评论的立场一群观众的画像拿到一部新上映电影的豆瓣短评列表第一反应往往是翻热门短评但几百条看完也只能得出“好像还行”这种模糊结论。我习惯直接把全部短评交给 SnowNLP 做情感分析把它映射成一个 0 到 1 的极性得分再叠一张词云图看大家议论的高频焦点。这套流程把“NLP 应用”四个字落成了几段能跑的 Python 代码适合刚接触中文自然语言处理、想整体跑通情感分析与文本可视化链路的开发者。资源核心不是模型本身而是数据获取到可视化的完整闭环。2. 豆瓣短评的数据获取与清洗请求头、翻页与三条硬规则2.1 用 requests 模拟短评接口请求豆瓣网页端短评列表走的是异步接口返回 JSON比解析 HTML 干净得多。手动抓取时要带齐访问头尤其是User-Agent、Referer和Cookie。Cookie未登录也能拿到但登录后的会话更完整能避免部分字段为空。接口路径是movie.douban.com/j/subject_short_comments参数里start控制偏移量limit控制每页条数。import requests import time movie_id 1292052 url https://movie.douban.com/j/subject_short_comments params { new_score: True, start: 0, limit: 20, ck: , only_show: active, } headers { User-Agent: ( Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 ), Referer: fhttps://movie.douban.com/subject/{movie_id}/, Cookie: 你的浏览器会话Cookie, } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() body resp.json() print(body.get(total))这里start是数据偏移量接口每页固定 20 条翻页就把start加 20。limit不建议调大短评接口对limit有隐藏校验我在第一轮就把limit改成 100结果第二次请求直接 403。后来规规矩矩每页 20 条每次请求之间间隔 2 到 3 秒才算稳定。注意这个接口属于公开数据的常规浏览接口不要在短时间高频请求。如果担心抓取中途断掉可以加一层重试。常见做法是用 tenacity 库包一层from tenacity import retry, stop_after_attempt, wait_fixed retry(stopstop_after_attempt(3), waitwait_fixed(2), reraiseTrue) def fetch_comments(start): params[start] start resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() return resp.json()参数说明stop_after_attempt(3)表示最多重试 3 次wait_fixed(2)表示每次重试前固定等待 2 秒。重试只解决偶发失败如果连续 403说明访问节奏已经被识别这时候应该停下来等半小时而不是换措辞硬冲。我在实际抓取时还做过指数退避连续失败 5 次就休眠 60 秒让请求特征回到正常用户的节奏这比单纯依赖重试更靠谱。2.2 字段拆解与清洗管线接口返回的每条评论包含comment、rating、vote_count、time、useful_count等字段。做情感分析主要用comment但vote_count和time不要扔后面分析时间走势和热评加权时会用到。清洗代码我一般写成函数方便复用import re def clean_comments(items): seen set() result [] for item in items: text item.get(comment, ) text re.sub(r.*?, , text) text text.replace(\n, ).replace(\u3000, ).strip() if not text or text in seen: continue if len(text) 5: continue seen.add(text) result.append({ comment: text, rating: item.get(rating, {}).get(value) if isinstance(item.get(rating), dict) else None, votes: item.get(vote_count, 0), time: item.get(time, ), }) return result这里做了三件事去 HTML 标签、去重复评论、过滤过短文本。len(text) 5把“哈哈哈”“绝了”这类短内容过滤掉因为它们对词频统计和情感分布都是噪声。rating字段是用户手动打的星级如果拿得到可以作为后面验证 SnowNLP 分数是否合理的参照系——注意这个字段有时不在接口里返回拿不到就跳过不影响主流程。接下来把清洗结果存成 DataFrame并控制翻页总量import pandas as pd comments_cleaned [] for start in range(0, 200, 20): data fetch_comments(start) comments_cleaned.extend(clean_comments(data.get(comments, []))) time.sleep(2) df pd.DataFrame(comments_cleaned) df df.drop_duplicates(subsetcomment) df.to_csv(douban_comments.csv, indexFalse, encodingutf-8-sig)参数说明start范围按 200 条上限控制避免短时间请求过多。utf-8-sig编码是为了在 Windows 的 Excel 里打开 CSV 不乱码。如果你想保留用户打分星级作为情感标签就把rating字段一起保留当评分是 1 星或 2 星时基本可以当作真实的负面标注来用这比人工标注省力得多。3. SnowNLP 的情感计算把零散吐槽变成可统计的分值3.1 朴素贝叶斯在中文评论文本上怎么打分SnowNLP 的情感分析不是深度学习而是朴素贝叶斯分类器。它把训练语料中的文本切词统计每个词在正类、负类里出现的概率然后对新文本按贝叶斯公式计算它属于正类的后验概率。调用SnowNLP(text).sentiments返回的 0 到 1 浮点数本质就是那个后验概率。数值越接近 0 越负面越接近 1 越正面。from snownlp import SnowNLP case SnowNLP(剧情平庸节奏拖沓看了一半就睡着了) print(case.sentiments)这个属性读起来简单背后依赖了分词器、停用词和训练语料任何一个环节偏了都会让分数失真。默认训练语料来自电商评论和微博里面“便宜”“快递”这类词频率很高放到影视评论场景里“票房”“镜头”“演技”这些词拿不到合理的先验概率。更麻烦的是影评常用反讽和夸张“这片子太棒了我提前退场”字面全是褒义词实际是负面表达这类句子对任何词袋模型都是硬伤。所以我一直把 SnowNLP 当基线指标用不把它当精确判官。3.2 批量评分、阈值划分和分布统计处理几百条评论直接循环即可不必上并发。每条评论都会触发一次分词和一次概率推断速度大约每秒 20 到 30 条scores [] for text in df[comment]: scores.append(SnowNLP(text).sentiments) df[senti] scores如果想提速可以在批处理前用 jieba 预分词再把词序列传给定制分类器但这会丢掉 SnowNLP 自带分词的便利性。我在真实项目里一般维持原样因为瓶颈不在速度而在后面的标注和校准。阈值划分是另一个容易被忽略的点。如果直接按score 0.5判好评、score 0.5判差评你会发现 0.45 到 0.55 之间躺着大量模棱两可的句子。我习惯分三档def label(score): if score 0.65: return pos if score 0.35: return neg return neu df[label] df[senti].apply(label) print(df[label].value_counts(normalizeTrue))参数说明0.65 和 0.35 是我从多个项目分布里抠出来的经验值。SnowNLP 的分数天然向 0.5 聚拢如果中性区留得太窄误判率会明显上升。把中性区间放宽到 0.35 到 0.65换来的是正、负结论的可信度。再做一步验证用用户星级和模型标签做交叉表判断领域偏差有多大。valid df.dropna(subset[rating]) valid[user_label] valid[rating].apply( lambda x: pos if x 4 else (neg if x 2 else neu) ) confusion pd.crosstab(valid[user_label], valid[label]) print(confusion)这里把用户星级当作近似真实标签与模型标签做交叉统计。如果发现大量user_labelneg而labelpos说明默认语料对影视评论的偏差比想象严重这就得考虑在第 6 章做自定义语料重训。4. 词云分析jieba 分词、停用词表与中文字体缺一不可4.1 分词与关键词过滤情感分数负责定量词云负责定性。把清洗后的全部评论拼成长文本交给 jieba 切词import jieba all_text .join(df[comment]) seg_list jieba.cut(all_text, cut_allFalse) words [w.strip() for w in seg_list if w.strip() and len(w) 1]参数说明cut_allFalse是精确模式它会按词典把“流浪地球”整体切成一个词而不是拆成“流浪”和“地球”。len(w) 1过滤单字“了”“吗”“的”这类字眼对词义贡献太低。直接按词频统计还不够中文里高频虚词和语气词会盖住真正的主题词所以一定要挂停用词表。停用词表可以从公开仓库拉一份常用中文停用词也可以自己维护。我更推荐在常见列表基础上追加当前项目的特有噪声词。比如分析某部电影会发现影评里大量出现“电影”“感觉”“真的”这类高频低信息词加进停用词表后词云才会真正突出“镜头”“剧本”“演技”这些主题词。stopwords set() with open(stopwords_cn.txt, encodingutf-8) as fp: for line in fp: w line.strip() if w: stopwords.add(w) filtered [w for w in words if w not in stopwords]4.2 WordCloud 生成与参数取舍WordCloud 是常用的可视化库但直接输出中文会翻车它默认的字体不支持中文字形画出来全是方框。正确做法是显式指定字体路径from wordcloud import WordCloud from collections import Counter freq Counter(filtered) wc WordCloud( width800, height600, background_colorwhite, font_pathC:/Windows/Fonts/simhei.ttf, max_words200, stopwordsstopwords, collocationsFalse, ) wc.generate_from_frequencies(freq) wc.to_file(wordcloud.png)参数说明font_path必须指向系统里真实存在的中文字体。Windows 常用simhei.ttfmacOS 用/System/Library/Fonts/PingFang.ttfLinux 需要自行安装中文字体。collocationsFalse是关键参数它禁止 WordCloud 把相邻词自动组合成“导演演技”这类伪短语否则词云里会出现大量你从没见过的拼接词。max_words200表示只展示词频前 200 的词数值越大画面越密重点越难读。mask参数也值得留一手它能把词云裁剪成特定形状import numpy as np from PIL import Image mask np.array(Image.open(movie_mask.png)) wc WordCloud( ... maskmask, contour_width1, contour_colorsteelblue, )mask用一个黑白轮廓图把词云裁剪成胶片、放映机等形状。白色区域会被当作空白黑色区域才是词云填充区选图时要注意这个逻辑。contour_width和contour_color控制轮廓线的粗细和颜色可以让裁剪边界更清晰。5. 避坑指南语料偏差、限流阈值与方框字符5.1 负面好评识别不准现象明显是吐槽的评论sentiments分数却落在 0.4 到 0.6甚至超过 0.6。原因SnowNLP 默认语料是电商和微博文本对影视评论里的反讽和夸张表达覆盖不足像“这也太神了吧”字面全是正面词语境里却是贬义。解决第一层调阈值把中性区间放宽到 0.35 到 0.65第二层准备领域语料做增量训练让模型重新估计影视词的概率分布。我在实际项目中两层都做先用窄阈值看整体分布再用重训后的模型跑正式统计。5.2 翻页到一半触发 403现象前几页正常翻到第五页返回 403 Forbidden。原因访问节奏太规律start固定步长、间隔固定秒数请求特征被识别成程序行为。解决把间隔时间设为 2 到 5 秒随机浮动同时记录一个随机起点不要每次都从 0 开始清。如果仍然失败就停半小时再继续。对公开数据的使用要始终尊重服务端的访问策略这也是我脚本里坚持不加并发的原因。5.3 词云输出全是方框现象词云图生成成功但所有中文都显示为实心方块。原因WordCloud 默认字体不含中文字形或者font_path指向的文件不存在。解决生成词云前用os.path.exists检查字体路径import os print(os.path.exists(C:/Windows/Fonts/simhei.ttf))路径无效就换成系统里实际存在的字体。另一个隐蔽情况是字体文件损坏或权限不足把字体文件放项目目录后依旧报错时可以换个字体文件试比如从黑体换成微软雅黑msyh.ttc。5.4 分词把关键短语切碎现象词频统计里出现“不好”“难看”完整的“不好看”没有出现。原因jieba 默认词典是通用词库对短影评里的临时组合词覆盖有限用户自定义停用词表也可能误伤了核心词。解决在分词前把领域内的关键短语注册进 jiebajieba.add_word(不好看) jieba.add_word(值回票价) jieba.add_word(二刷)add_word会提高这些词在分词时的优先级。注意必须在jieba.cut之前调用否则不生效。5.5 CSV 文件中文乱码现象to_csv写完Excel 打开一片乱码。原因写文件时用了默认 utf-8Windows 的 Excel 按 GBK 解析。解决写 CSV 用encodingutf-8-sig会在文件头加 BOMExcel 识别后按 UTF-8 解析。后面读回 DataFrame 时同样用encodingutf-8-sig保持两边一致。6. 进阶技巧用情感分布的形态反推口碑最后一个技巧我反复在项目里用别只看情感分数的平均值要看情感分布的形状。两部电影的平均分可能都是 0.62但一部是观众齐声叫好另一部是两极分化严重平均值完全分辨不出来。用核密度估计画出情感分数分布import matplotlib.pyplot as plt df[senti].plot.kde(bw_method0.1) plt.axvline(0.5, colorgray, linestyle--) plt.xlabel(snownlp score) plt.show()bw_method0.1控制带宽数值越小曲线越贴近真实数据越大越平滑。如果曲线在 0.8 和 0.2 各有一个峰说明影评两极分化严重典型“粉丝夸上天、路人骂到底”的状态。如果曲线集中挤在 0.4 到 0.6说明大部分观众无感话题热度衰减得会很快。再看时间维度。前面清洗时特意保留了time字段这里按日期聚合算滚动均值df[day] pd.to_datetime(df[time]).dt.date daily df.groupby(day)[senti].mean().rolling(3, min_periods1).mean() daily.plot()滚动窗口取 3 天可以消除单日波动。曲线从首日的 0.7 一路滑到 0.45说明口碑高开低走反过来从 0.4 爬到 0.65是典型的低开高走这类片子往往有二次传播价值。如果想进一步贴近豆瓣语境可以准备领域语料重训 SnowNLP。底层snownlp的贝叶斯模型支持保存和加载from snownlp import Bayes bayes Bayes() for sentence, label in manual_data: bayes.train([(sentence, label)]) bayes.save(douban_sentiment.marshal)之后在工程里加载这个.marshal文件替换默认模型再跑全部评论极性分数才会贴合影评的表达习惯。从那以后我每次做情感分析都会先抽一百条短评人工标注训练一份领域专属模型再跑全量数据。这个习惯帮我省掉了无数条被默认模型误判的影评。希望这条从豆瓣短评起步的 SnowNLP 情感分析流程能帮到你换到微博评论或商品评价场景时只要替换数据源、停用词表骨架不用改。本文还有配套的精品资源点击获取
网站建设高端定制企业官网