ARTICLE DETAIL

资讯详情

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

Python微博情感分析系统实战:从数据抓取到情绪曲线可视化

Python微博情感分析系统实战:从数据抓取到情绪曲线可视化 简介这是一套面向Python初学者与自然语言处理爱好者的微博情感分析完整源码围绕数据爬取、清洗、建模与可视化四个环节展开可用于课程设计、毕业项目或情感分析入门实践。压缩包共73个文件约517KB以20个py源码文件为核心辅以15个pyc编译文件、5个vue前端组件、4个js脚本及svg、xml、json等配置与图表资源前后端结构清晰。系统采用Scrapy爬取微博推文通过自定义脚本完成繁简转换、URL去除与表情包处理并构建BERT与LSTM混合模型进行情感分类最终以柱状图展示各情感标签下的模型表现。前端基于Vue搭建后端提供预测与训练模块目录划分明确便于按模块阅读与二次开发。目前已有113人学习适合希望掌握从数据采集到模型部署完整流程的读者参考借鉴。1. 微博情感分析系统到底能跑出什么从一条吐槽到一张情绪曲线你手上有一份「基于 Python 的微博情感分析系统」源码第一反应大概率是这东西到底能干嘛是不是又一个跑不起来的玩具。我先把结论放前面——它解决的核心问题只有一句话把非结构化的中文短文本批量映射成可统计的情绪标签再聚合成能看趋势的曲线。典型场景是品牌舆情监控、活动口碑复盘、竞品评论区对比输入是一批微博正文通常带发布时间和互动量输出是每条文本的正负中性判定以及按小时或按天聚合的情绪占比。适合谁会一点 Python 基础语法、能照着教程装好环境的人想拿一个完整项目练手数据分析与可视化的人以及需要快速搭一个舆情 demo 给业务方看的人。不适合谁指望开箱即用、零改动就达到商用精度的人——中文情感分析的坑后面几章会一条条拆给你看。这一章先建立整体认知从第 2 章开始进入可复现的落地步骤。2. 环境与数据链路把微博情感分析系统在本地跑起来2.1 环境准备python安装与依赖库的版本对齐热词里 python安装教程、python安装、vscode配置python 出现频率极高说明大量人卡在第一步。我的建议是别追最新版选 Python 3.9 或 3.10因为 jieba、snownlp、scikit-learn 这些库在新版本上偶发编译问题。装完 Python 后用 venv 建独立环境避免污染全局。# 创建并激活虚拟环境Windows 用 venv\Scripts\activate python -m venv venv source venv/bin/activate # 一次性装齐核心依赖版本区间是踩坑后收敛出来的 pip install jieba0.42.1 pip install snownlp0.12.3 pip install pandas1.5.3 pip install scikit-learn1.2.2 pip install matplotlib3.7.1 pip install requests2.28.2 pip install beautifulsoup44.12.2逻辑说明jieba 负责中文分词snownlp 提供开箱即用的情感打分pandas 做数据清洗与聚合scikit-learn 用于后续自训练模型matplotlib 出图。参数说明版本号不是随便写的snownlp 0.12.3 之后接口稳定pandas 1.5.x 与 scikit-learn 1.2.x 兼容性最好升到 2.x 会出现append方法被移除的报错。如果你用 vscode python环境配置记得在设置里把解释器指向 venv 里的 python否则终端装好了、编辑器里还是找不到库。提示装 sklearn 报错时先确认 pip 已升级到最新再检查是否缺 C 编译环境Windows 上装 numpy 失败多半是没装对应版本的 wheel。2.2 数据获取python爬虫抓微博正文的边界与替代方案标题里是「系统」但系统要有数据。常见做法是用 python爬虫 抓公开微博页面但这里必须说清楚边界微博有反爬机制登录态、频率限制、动态渲染都会让简单 requests 抓取失效。我一般会分两条路走——小批量用移动端接口配合合理间隔大批量直接找合规的数据集或人工导出。下面给一个最小可跑的抓取骨架重点看结构而不是照抄去高频请求。import requests import time import random from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile Safari/604.1 } def fetch_page(url): 抓取单页失败返回 None避免异常中断整批任务 try: resp requests.get(url, headersHEADERS, timeout10) if resp.status_code 200: return resp.text return None except requests.RequestException: return None def parse_texts(html): 从页面里抽出微博正文选择器要按实际页面结构调整 soup BeautifulSoup(html, html.parser) items soup.select(div.card-wrap div.content p) return [it.get_text(stripTrue) for it in items if it.get_text(stripTrue)] if __name__ __main__: for page in range(1, 6): html fetch_page(fhttps://m.weibo.cn/api/container/getIndex?page{page}) if html: texts parse_texts(html) print(f第 {page} 页拿到 {len(texts)} 条) # 随机间隔是降低被封概率的最低成本手段 time.sleep(random.uniform(3, 6))逻辑说明fetch_page做了异常兜底单页失败不影响整体parse_texts用 CSS 选择器抽正文实际页面结构变了要同步改选择器。参数说明timeout10防止请求挂死random.uniform(3, 6)是间隔区间低于 3 秒风险陡增。如果你只是练手强烈建议先用一份现成的微博文本 CSV把分析链路跑通再回头折腾抓取——顺序反了时间全耗在对抗反爬上。2.3 数据清洗去重、去噪、去广告的三道工序原始微博文本脏得超出新手想象转发前缀、话题标签、 提及、表情符号、URL、重复内容。不清洗直接喂给模型准确率会被拉低一大截。我一般固定做三步。import re import pandas as pd def clean_text(text): if not isinstance(text, str): return # 去掉转发前缀和话题标签 text re.sub(r//.*?[:], , text) text re.sub(r#.*?#, , text) # 去掉 提及 和 URL text re.sub(r[\w\u4e00-\u9fa5\-], , text) text re.sub(rhttps?://\S, , text) # 去掉表情符号和多余空白 text re.sub(r\[.*?\], , text) text re.sub(r\s, , text).strip() return text df pd.read_csv(weibo_raw.csv) df[clean] df[content].apply(clean_text) # 去重完全相同的正文只保留一条 df df.drop_duplicates(subset[clean]) # 丢掉清洗后长度小于 3 的短文本这类基本没有情感信息 df df[df[clean].str.len() 3] df.to_csv(weibo_clean.csv, indexFalse, encodingutf-8-sig)逻辑说明正则顺序有讲究先去转发前缀再去话题否则嵌套结构会残留。参数说明len 3是经验阈值中文两字词情感倾向太模糊encodingutf-8-sig是为了 Excel 打开不乱码。这一步做完数据量通常会掉 20% 到 40%属于正常损耗别心疼。3. 情感判定snownlp 快速基线 vs 自训练模型3.1 用 snownlp 十分钟搭出可用基线snownlp 是中文情感分析里上手最快的库SnowNLP(text).sentiments返回 0 到 1 的分数越接近 1 越正面。它内置的是电商评论语料训练的模型用在微博上会有偏差但作为基线足够让你先看到结果。from snownlp import SnowNLP import pandas as pd def get_sentiment(text): 返回情感分数和标签0.6/0.4 是常用的双阈值 if not text: return 0.5, 中性 score SnowNLP(text).sentiments if score 0.6: return score, 正面 elif score 0.4: return score, 负面 return score, 中性 df pd.read_csv(weibo_clean.csv) df[[score, label]] df[clean].apply( lambda x: pd.Series(get_sentiment(x)) ) print(df[label].value_counts()) df.to_csv(weibo_scored.csv, indexFalse, encodingutf-8-sig)逻辑说明用apply配合pd.Series把返回的元组拆成两列这是 pandas 里处理多返回值的标准写法。参数说明0.6 和 0.4 这对阈值不是固定的snownlp 在微博语料上偏中性你可以先跑一遍看分布如果中性占比超过 70%就把阈值放宽到 0.55/0.45。这一步跑完你已经有了一个能出结论的系统雏形。3.2 自训练模型把标注数据变成分类器snownlp 的短板是领域不匹配微博里的反讽、网络梗它基本抓瞎。要提升精度就得自己标一批数据训练模型。常见做法是人工标 500 到 1000 条用 TF-IDF 加朴素贝叶斯或逻辑回归效果往往比通用模型好。import jieba import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split from sklearn.metrics import classification_report # 假设 labeled.csv 有 text 和 label 两列label 取 0/1/2 df pd.read_csv(labeled.csv) def cut(text): # 分词时过滤单字和空白减少噪声特征 return .join([w for w in jieba.cut(text) if len(w) 1]) df[cut] df[text].apply(cut) vectorizer TfidfVectorizer(max_features5000, ngram_range(1, 2)) X vectorizer.fit_transform(df[cut]) y df[label] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) clf LogisticRegression(max_iter1000, C1.0) clf.fit(X_train, y_train) y_pred clf.predict(X_test) print(classification_report(y_test, y_pred))逻辑说明TF-IDF 把分词后的文本转成向量ngram_range(1, 2)同时考虑单字词和双字词组合对中文短文本有帮助。参数说明max_features5000控制特征维度太大容易过拟合C1.0是正则强度调小更保守、调大更激进stratifyy保证训练测试集类别比例一致样本不均衡时必加。跑完看classification_report重点看负面类的 recall舆情场景漏报负面比误报正面代价大得多。3.3 两种方案怎么选一张对比表说清维度snownlp 基线自训练模型上手时间10 分钟半天到一天标注需求无500 条起微博场景准确率约 60% 到 70%约 75% 到 85%可解释性黑匣子可看特征权重适用阶段demo、快速验证正式上线、持续迭代我的习惯是先用 snownlp 把整条链路跑通拿到业务方反馈再决定要不要投入标注。很多项目死在「一上来就追求高精度」链路没通精度再高也没人看。4. 可视化与聚合把逐条分数变成能汇报的图表4.1 按时间聚合情绪曲线单条情感分数没有汇报价值业务方要看的是趋势。把weibo_scored.csv里的时间字段解析出来按天或按小时聚合正负中性占比画成堆叠面积图或折线图。import pandas as pd import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 解决中文乱码 plt.rcParams[axes.unicode_minus] False df pd.read_csv(weibo_scored.csv) df[time] pd.to_datetime(df[publish_time]) df[date] df[time].dt.date # 按天统计各情绪占比 pivot df.groupby([date, label]).size().unstack(fill_value0) ratio pivot.div(pivot.sum(axis1), axis0) ratio.plot(kindarea, stackedTrue, figsize(12, 5), color[#e74c3c, #95a5a6, #2ecc71]) plt.title(微博情绪趋势) plt.ylabel(占比) plt.tight_layout() plt.savefig(sentiment_trend.png, dpi150)逻辑说明groupby加unstack是 pandas 做交叉表的经典组合div按行归一化得到占比。参数说明SimHei是 Windows 自带中文字体Linux 上换成WenQuanYi Micro Heidpi150保证导出图清晰。如果横坐标太密集用plt.gca().xaxis.set_major_locator(plt.MaxNLocator(10))限制刻度数量这是 python画图横坐标太密集 的标准解法。4.2 高频词与负面词云趋势图看整体词云看细节。把负面文本单独拎出来做词频统计能快速定位问题点。import jieba from collections import Counter neg df[df[label] 负面][clean] words [] for text in neg: words.extend([w for w in jieba.cut(text) if len(w) 1]) # 过滤无意义高频词 stopwords {微博, 转发, 真的, 什么, 这个, 就是} counter Counter([w for w in words if w not in stopwords]) for word, cnt in counter.most_common(20): print(word, cnt)逻辑说明负面文本单独统计比全量词云更有指向性。参数说明停用词表要按你的数据补充微博场景里「转发」「微博」这类词几乎必然霸榜不滤掉词云没法看。这一步的输出可以直接贴进汇报文档比一张花哨的词云图更有说服力。5. 避坑与排查微博情感分析系统最常见的 5 个翻车点5.1 现象snownlp 对反讽句全部判成正面原因snownlp 训练语料是电商评论反讽、网络梗在电商场景几乎不存在模型没见过这类表达。解决对反讽高发场景要么人工标注补充训练数据要么加规则兜底比如检测到「呵呵」「真是绝了」「厉害了」等词时强制降分。别指望通用模型能自动理解反讽这是领域适配问题不是调参能解决的。5.2 现象分词后准确率反而下降原因jieba 默认词典对网络新词覆盖不足「绝绝子」「yyds」会被切成单字丢失语义。解决加载自定义词典把领域高频词加进去。import jieba jieba.load_userdict(user_dict.txt) # 每行一个词可带词频和词性参数说明词典文件格式是「词语 词频 词性」词频可省略。这一步对短文本分类提升明显属于低成本高回报的操作。5.3 现象训练集准确率 95%测试集只有 60%原因典型过拟合要么样本量太小要么特征维度太高。解决先看样本量低于 500 条别急着上复杂模型再降max_features从 5000 降到 2000 试试最后加交叉验证cross_val_score跑 5 折看方差大不大。血泪经验是标注数据质量比模型选择重要十倍。5.4 现象中文图表全是方框原因matplotlib 默认字体不含中文。解决设置plt.rcParams[font.sans-serif]Windows 用 SimHeiMac 用 Arial Unicode MSLinux 用 WenQuanYi Micro Hei。设完记得加axes.unicode_minus False否则负号也会变方框。5.5 现象抓取跑一会儿就被限制原因请求频率过高或缺少必要请求头。解决间隔拉到 3 秒以上加随机抖动轮换 User-Agent控制单次任务总量。如果还是不行说明该换数据来源了硬刚反爬的投入产出比极低。6. 进阶技巧把系统打包成可交付的形态链路跑通之后下一步是让它能被别人用起来。我一般做两件事一是把分析逻辑封装成函数或类二是用 argparse 做成命令行工具方便非技术同事调用。import argparse import pandas as pd from snownlp import SnowNLP def analyze(input_path, output_path): df pd.read_csv(input_path) df[score] df[clean].apply(lambda x: SnowNLP(x).sentiments) df[label] df[score].apply( lambda s: 正面 if s 0.6 else (负面 if s 0.4 else 中性) ) df.to_csv(output_path, indexFalse, encodingutf-8-sig) print(f完成共处理 {len(df)} 条) if __name__ __main__: parser argparse.ArgumentParser(description微博情感分析) parser.add_argument(--input, requiredTrue, help输入 CSV 路径) parser.add_argument(--output, defaultresult.csv, help输出路径) args parser.parse_args() analyze(args.input, args.output)逻辑说明把核心逻辑收进analyze函数命令行参数用 argparse 解析这样别人不用改代码就能换输入输出。参数说明requiredTrue强制传输入路径default给输出兜底。如果对方连命令行都不想用可以用 pyinstaller 打成 exepyinstaller -F analyze.py注意打包时把自定义词典和停用词表一起带上否则运行时报文件找不到。验证方法上我习惯留一个 20 条的小测试集每次改完代码先跑它看标签有没有异常跳变。这个习惯帮我拦下过好几次「改了一行正则结果全量数据标签翻转」的事故。情感分析系统没有后悔药改动前先备份中间结果改动后先小样本验证这是我踩了足够多坑之后养成的肌肉记忆。希望帮到你。本文还有配套的精品资源点击获取
返回列表