资讯详情

豆瓣评论情感分析与词云实战:SnowNLP 中文 NLP 流水线

📅 2026/10/12 22:30:03 | 华诺云谱 👁 阅读
豆瓣评论情感分析与词云实战:SnowNLP 中文 NLP 流水线
简介这份资源面向Python数据挖掘初学者与自然语言处理入门者围绕豆瓣《肖申克的救赎》评论文本演示如何调用SnowNLP库完成情感分析并配合词云与分词处理帮助读者建立从文本读取、中文分词到情感倾向判定的完整思路适合作为课程实验或自学练手素材。压缩包共10个文件以8个py脚本为主覆盖豆瓣评论读取、分词、SnowNLP多版本调用等环节另含1个csv评论数据与1个txt文本整体约37KB轻量易运行。目前已有16945人学习下载说明该案例在入门群体中认可度较高。读者可据此复现情感分析流程理解SnowNLP打分机制并在此基础上迁移到其他评论语料完成词云可视化与结果解读为后续文本挖掘项目提供可复用的脚本参考与排错思路。1. 豆瓣评论 SnowNLP一条能跑通的情感分析与词云流水线豆瓣短评是中文互联网里少有的、带评分标签又带真实文本的语料池。做情感分析练手它比公开英文数据集更贴地气因为你会立刻撞上反讽、缩写、表情符号和「一星护体」这类真实噪声。SnowNLP 是一个轻量级中文情感分析库开箱即用对每条评论输出 0 到 1 之间的情感概率配合 jieba 分词和 wordcloud 就能把「观众到底在夸什么、骂什么」画出来。这套方案适合两类人一是想快速拿到一个能跑通的中文 NLP 小项目的新手二是需要给产品评论做粗粒度正负向分流的工程师。它不追求 SOTA 精度追求的是两小时内从原始评论到可视化结论的闭环。下面按数据获取、情感打分、词云生成、避坑、进阶的顺序讲透。2. 数据从哪来豆瓣短评的采集边界与清洗2.1 采集前先想清楚的三件事动手写爬虫之前有三个现实约束必须先接受。第一豆瓣对未登录请求和频繁请求有访问限制短评页在翻到一定页数后会要求登录或直接返回空列表所以别指望一次抓几万条几百到两千条足够做情感分布和词云。第二短评文本里混着大量非评论文本比如「有用」「回应」「举报」这类界面词以及用户昵称、时间戳清洗时必须剔除。第三评分星级和文本情感并不总是一致有人打五星写「还行」有人打一星写「年度最佳」这正是后面要观察的噪声来源。常见做法是用 requests 加合理的请求间隔抓取短评的评论文本和星级两个字段存成 CSV。不要用多线程猛冲一是容易被限制二是几百条数据根本不需要并发。我一般会设置每次请求间隔 1 到 2 秒并且只抓前 20 页左右。2.2 采集与清洗的最小代码import requests import time import csv import re from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36 } def fetch_comments(movie_id, start0, limit20): 抓取指定电影的一页短评返回 (文本, 星级) 列表 url fhttps://movie.douban.com/subject/{movie_id}/comments params {start: start, limit: limit, status: P, sort: new_score} resp requests.get(url, headersHEADERS, paramsparams, timeout10) if resp.status_code ! 200: return [] soup BeautifulSoup(resp.text, html.parser) items [] for node in soup.select(div.comment-item): text_node node.select_one(span.short) star_node node.select_one(span.rating) if not text_node: continue text text_node.get_text(stripTrue) # 星级形如 allstar50 rating取数字部分 star 0 if star_node: cls .join(star_node.get(class, [])) m re.search(rallstar(\d), cls) if m: star int(m.group(1)) // 10 # 50 - 5 items.append((text, star)) return items def clean_text(text): 去掉多余空白和表情占位符 text re.sub(r\s, , text) text re.sub(r\[.*?\], , text) # 去掉 [图片] [音乐] 之类 return text.strip() if __name__ __main__: movie_id 1292052 # 举例用的条目 ID换成你要分析的 rows [] for page in range(0, 200, 20): batch fetch_comments(movie_id, startpage) if not batch: break for text, star in batch: rows.append((clean_text(text), star)) time.sleep(1.5) # 控制频率别把对方打疼 with open(comments.csv, w, newline, encodingutf-8-sig) as f: writer csv.writer(f) writer.writerow([comment, star]) writer.writerows(rows) print(f共抓取 {len(rows)} 条)这段代码的逻辑分三层fetch_comments负责发请求和解析 HTMLclean_text负责文本规整主流程负责翻页和落盘。参数上start是偏移量每次加 20limit固定 20这是豆瓣短评页的默认每页条数time.sleep(1.5)是请求间隔低于 1 秒会明显提高被限制的概率。星级解析用正则从 class 名里抠数字因为豆瓣把星级编码在allstar50这种类名里50 代表 5 星。提示抓下来的 CSV 用utf-8-sig编码Excel 打开不会乱码。如果中途返回空列表说明已经触发限制或翻到末尾直接 break 即可不要硬翻。2.3 清洗后要做的两件检查落盘之后别急着喂给 SnowNLP先做两个检查。一是看文本长度分布如果大量评论只有一两个字比如「好看」「烂」这些对情感模型和词云都是噪声可以设一个最小长度阈值比如保留 4 个字以上的。二是看星级分布如果五星占九成说明这个条目本身口碑一边倒情感分析的意义会打折这时候换一个争议大的条目更有分析价值。这两步用 pandas 几行就能看完。import pandas as pd df pd.read_csv(comments.csv) df df[df[comment].str.len() 4] print(df[star].value_counts()) print(df[comment].str.len().describe())str.len() 4是长度过滤value_counts()看星级分布describe()看长度统计。这一步不写进最终流程也行但强烈建议做因为它决定了你后面结论的可信度。3. SnowNLP 情感打分从单条到整表的批量处理3.1 SnowNLP 到底在算什么SnowNLP 的情感分析基于一个朴素贝叶斯模型训练语料是中文购物评论输出的是「这条文本属于正向情感的概率」范围 0 到 1。注意它输出的是概率不是标签通常以 0.5 为界大于 0.5 判为正向小于 0.5 判为负向。它的优点是零训练、零配置、装上就能用缺点是领域偏移明显购物评论上表现尚可到了电影评论这种反讽密集的场景准确率会掉。所以用它做粗筛可以做精细判断要谨慎。常见做法是先用 SnowNLP 跑一遍得到情感分数再和星级做交叉验证看看哪些评论是「文本和评分打架」的这些往往是最有意思的分析点。3.2 批量打分的代码与参数import pandas as pd from snownlp import SnowNLP df pd.read_csv(comments.csv) df df[df[comment].str.len() 4].copy() def get_sentiment(text): try: return SnowNLP(text).sentiments except Exception: return None # 空文本或异常时返回 None df[sentiment] df[comment].apply(get_sentiment) df df.dropna(subset[sentiment]) # 按 0.5 切分正负向 df[label] df[sentiment].apply(lambda x: 正向 if x 0.5 else 负向) print(df[label].value_counts()) print(df.groupby(star)[sentiment].mean()) df.to_csv(comments_scored.csv, indexFalse, encodingutf-8-sig)SnowNLP(text).sentiments是核心调用返回浮点数。apply逐行处理几百到几千条秒级完成。dropna把异常行去掉避免后续统计报错。groupby(star)[sentiment].mean()这行很关键它告诉你每个星级对应的平均情感分如果五星的平均分明显高于一星说明模型方向是对的如果两者接近说明模型在这个领域基本失效需要换策略。参数上唯一要调的是 0.5 这个阈值。如果你发现正向评论被大量误判为负向可以把阈值降到 0.4反之升到 0.6。这个阈值没有标准答案取决于你的语料和容忍度。我一般会先看groupby的结果再决定。3.3 用星级做一次交叉验证SnowNLP 的分数和豆瓣星级是两套独立信号把它们放一起看能暴露很多问题。理想情况下星级越高情感分越高呈单调关系。实际跑下来经常出现一星评论情感分 0.7 的情况点进去一看原来是「一星给导演演员演技在线」这种转折句。这类样本就是模型的黑匣子也是你后续优化或人工复核的重点。import pandas as pd df pd.read_csv(comments_scored.csv) # 找出评分低但情感分高的矛盾样本 conflict df[(df[star] 2) (df[sentiment] 0.7)] print(conflict[[comment, star, sentiment]].head(10))这段代码筛出「低星高情感」的矛盾样本head(10)先看十条。你会发现矛盾样本里大量存在「但是」「不过」「除了」这类转折词这提示你可以在预处理阶段对转折句做特殊处理或者干脆把这类样本单独拿出来人工标注。4. 词云分析让高频词说出评论的真实焦点4.1 分词、停用词与词频统计词云的前提是分词和词频统计。中文分词用 jieba停用词表要自己准备因为豆瓣评论里「电影」「一部」「真的」这类词会淹没真正有信息量的词。停用词表可以从网上找一份通用中文停用词再手动加几个领域词比如「导演」「演员」如果和你的分析目标无关也可以去掉。import jieba import pandas as pd from collections import Counter df pd.read_csv(comments_scored.csv) # 加载停用词 with open(stopwords.txt, encodingutf-8) as f: stopwords set(line.strip() for line in f if line.strip()) def tokenize(text): words jieba.lcut(text) return [w for w in words if len(w) 1 and w not in stopwords] # 分别统计正向和负向评论的词频 pos_words [] neg_words [] for _, row in df.iterrows(): words tokenize(row[comment]) if row[label] 正向: pos_words.extend(words) else: neg_words.extend(words) pos_counter Counter(pos_words).most_common(100) neg_counter Counter(neg_words).most_common(100) print(正向高频词:, pos_counter[:20]) print(负向高频词:, neg_counter[:20])jieba.lcut返回列表len(w) 1过滤单字stopwords过滤无意义词。分开统计正负向词频是关键一步因为整体词云只能告诉你「大家在聊什么」分开之后才能告诉你「夸的人和骂的人分别在意什么」。most_common(100)取前 100 个高频词实际画词云时用前 50 到 80 个效果最好太多会显得杂乱。4.2 生成词云的代码与字体坑from wordcloud import WordCloud import matplotlib.pyplot as plt def draw_wordcloud(counter, filename, title): freq dict(counter) wc WordCloud( font_pathSimHei.ttf, # 中文字体路径必须指定 width800, height500, background_colorwhite, max_words80, colormapviridis ) wc.generate_from_frequencies(freq) plt.figure(figsize(10, 6)) plt.imshow(wc, interpolationbilinear) plt.axis(off) plt.title(title) plt.savefig(filename, dpi150, bbox_inchestight) plt.close() draw_wordcloud(pos_counter, wordcloud_pos.png, 正向评论词云) draw_wordcloud(neg_counter, wordcloud_neg.png, 负向评论词云)font_path是中文词云最容易翻车的地方不指定中文字体出来的全是方块。Windows 上可以用C:/Windows/Fonts/simhei.ttfLinux 上需要自己放一个SimHei.ttf到工作目录。generate_from_frequencies直接吃词频字典比传原始文本更可控因为你可以自己决定哪些词进、哪些词不进。max_words80控制词数colormap换配色dpi150保证导出清晰度。注意如果词云里出现大量无意义的高频词不要怪 wordcloud回去检查停用词表。词云的质量九成取决于分词和停用词一成取决于配色。4.3 正负向词云怎么读两张词云摆在一起对比才有价值。正向词云里如果「演技」「剧情」「感动」占主导说明观众认可的是内容层面负向词云里如果「尴尬」「拖沓」「圈钱」突出说明不满集中在节奏和诚意上。这种对比结论比单纯一个情感饼图有说服力得多。我一般会把两张图并排放在报告里再配一句「正向集中在 X负向集中在 Y」结论就立住了。5. 避坑与排查SnowNLP 情感分析最常见的五个翻车点5.1 情感分和星级完全对不上现象五星评论平均情感分只有 0.4一星评论反而 0.6。原因SnowNLP 的训练语料是购物评论电影评论的用词和句式差异大加上反讽和转折句模型直接失灵。解决不要迷信 0.5 阈值先看groupby(star)[sentiment].mean()的结果如果单调性不成立说明模型在这个语料上不可用要么换领域适配的模型要么只把 SnowNLP 当粗筛结论以星级为主、情感分为辅。5.2 词云全是方块现象生成的图片里中文全部显示为方框。原因WordCloud 默认字体不支持中文。解决显式指定font_path指向一个中文字体文件Windows 用simhei.ttfLinux 和 macOS 需要自己下载或拷贝一个中文字体到项目目录。这是最高频的翻车点没有之一。5.3 停用词没做好词云被「电影」「真的」霸屏现象词云里最大的词是「电影」「一部」「真的」「就是」。原因停用词表不完整或者根本没加载。解决准备一份中文停用词表再针对豆瓣评论补充「豆瓣」「评分」「导演」「演员」等词。判断标准很简单如果一个词出现在每一条评论里它就不该进词云。5.4 抓取中途返回空列表现象翻到第 10 页左右fetch_comments开始返回空列表。原因触发了访问频率限制或翻页上限。解决加大time.sleep间隔到 2 秒以上减少抓取页数或者换一个时间段再抓。不要试图用代理池硬刚几百条数据不值得。抓到空列表就停已经拿到的数据足够做分析。5.5 情感分析结果无法复现现象同样的代码今天跑出来正向占 60%明天跑出来 55%。原因SnowNLP 本身是确定性的问题多半出在数据抓取阶段——每次抓到的评论不同或者清洗规则变了。解决把原始 CSV 固定下来后续所有分析都基于同一份comments.csv不要每次重新抓。数据版本固定了结论才能复现。6. 进阶把 SnowNLP 和规则、多模态信号拼起来用SnowNLP 单打独斗的精度有限但它的价值在于快和轻。真正让这套流水线好用的做法是在它外面包一层规则和多信号融合。我一般会做三件事。第一用转折词规则修正情感分。评论里出现「但是」「不过」「可惜」时情感极性往往由后半句决定。可以写一个简单规则如果句子含转折词取转折词之后子串的 SnowNLP 分数作为最终分。这个改动很小但在电影评论上能明显改善矛盾样本。import re from snownlp import SnowNLP TURN_WORDS [但是, 但, 不过, 可惜, 然而, 只是] def sentiment_with_turn(text): for w in TURN_WORDS: if w in text: tail text.split(w, 1)[1] if len(tail) 4: return SnowNLP(tail).sentiments return SnowNLP(text).sentimentssplit(w, 1)只切一次取转折词之后的部分len(tail) 4防止后半句太短导致误判。这个函数替换掉原来的get_sentiment即可其他流程不变。第二把星级作为强信号做加权。星级是用户主动给出的比模型推断更可靠。可以定义一个综合分final 0.6 * star_norm 0.4 * sentiment其中star_norm是星级除以 5。这样既保留了文本信号又不会被模型带偏。权重 0.6 和 0.4 可以按你的信任度调整。第三关注多模态情感分析这个方向。豆瓣评论不只有文本还有评分、有用数、发布时间甚至关联的海报和剧照。文本情感只是其中一个模态把评分、点赞数、时间衰减加权进去能得到更稳的情感趋势。比如按月份聚合情感分看口碑是随时间上升还是崩塌这比单条分析有意思得多。信号权重建议说明星级0.5 到 0.6用户主动给出最可靠SnowNLP 情感分0.3 到 0.4文本信号需规则修正有用数0.1高赞评论权重略高最后说个我自己的习惯每次跑完这套流程我都会把矛盾样本单独存一份人工看二十条。这二十条里藏着的语言现象比任何调参都更能告诉我模型到底哪里不行。情感分析没有一劳永逸的模型只有不断对着坏案例修的过程。希望帮到你。本文还有配套的精品资源点击获取
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑