资讯动态

Python微博舆情分析系统:爬虫、情感分析与可视化实战

发布时间:2026/9/10 7:16:20 来源:尧图企业网站定制
简介这份面向高校毕业设计、课程设计与期末大作业的Python微博舆情分析可视化项目集爬虫采集、情感分析与可视化展示于一体代码注释完整适合Python初学者参照学习也可作为答辩高分项目模板。压缩包共146个文件包含Python源码与pyc编译文件、HTML/CSS/JS前端页面、CSV数据文件及SQL数据库脚本等类型整体仅3.85MB部署简单便捷。已有698人学习浏览具备较高参考热度。项目内置多套页面样式与示例数据可清晰还原舆情数据从采集、清洗、情感计算到可视化展示的完整链路同时配合关键词列表、评论数据表等文件便于理解微博文本处理与情感分类的实际应用。整体功能设计完善管理逻辑清晰适合用来快速完成一套可演示、可扩展的舆情分析系统对毕业设计或课程报告有直接帮助。1. 微博舆情分析为什么值得拆从一条微博到一张舆情报表大多数人在微博上刷到的是一条条碎片信息但在舆情分析场景里一条微博背后的发布时间、评论情感、转发扩散路径才是真正有价值的数据。这套基于 Python 的微博舆情分析可视化系统本质上是把「爬虫采集 → 数据清洗 → 情感分析 → 可视化报表」串成一条完整流水线先从搜索接口拉取与目标词相关的微博正文和评论落盘成 CSV再做情感极性判断最后通过 Flask ECharts 展示关键词趋势、评论情感占比和热词分布。它适合三类人正在做毕业设计或课程设计需要一套有完整交互界面的 Python 项目想学习 requests 爬虫与文本分析的初学者以及需要快速验证舆情分析思路的从业者。比起动辄分布式爬虫、深度学习模型的方案这套系统的设计更贴合「一个人、一台机器、一个周末跑通」的诉求。2. 爬虫与数据持久化requests 抓取、CSV 存储与字段设计2.1 为什么选 requests CSV而不是 Scrapy 和 MySQL微博的搜索接口反爬力度不算弱但对毕业论文级别的项目来说Scrapy 的下载中间件、Pipeline 调度反而增加了理解成本。requests 足够处理单机、低频、小批量的抓取任务代码透明出错了也容易定位。存储端我没有选择 MySQL因为 CSV 三个显著优势一是与 pandas、Excel、ECharts 无缝衔接二是部署时不需要额外安装数据库服务三是答辩时可以直接用 Excel 打开展示数据。项目里的articleData.csv和articleComments.csv正是这样的设计——前者存微博正文后者存单条微博下的评论。2.2 目标词配置target.csv 的作用系统把检索词放在target.csv里每行一个词爬虫启动后逐个读取。这样的好处是业务与代码分离换题目、换关键词时不需要改代码逻辑。# target.csv 研究生就业 考研压力 双减政策爬虫启动时用pandas.read_csv读取目标词再拼接成搜索 URL。注意目标词不要带换行符否则 URL 编码后会变成%0A导致搜索结果为空。我一般会在这里做一次strip()把首尾空格和换行清掉。2.3 请求构造与响应解析微博搜索页的 URL 格式是https://s.weibo.com/weibo?q关键词返回 HTML。直接 requests.get 会被重定向到登录页所以通常的做法是携带 Cookie。Cookie 从哪里来手动登录微博网页版打开开发者工具复制请求头里的Cookie字段写进一个配置文件。注意 Cookie 有有效期过期后系统会提示重新获取。import requests import pandas as pd import time from bs4 import BeautifulSoup def fetch_weibo(keyword, cookie, page1): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Cookie: cookie, Referer: https://s.weibo.com/ } params { q: keyword, page: page, Refer: g } url https://s.weibo.com/weibo resp requests.get(url, headersheaders, paramsparams, timeout10) resp.encoding utf-8 return resp.text这段代码里Refer: g是搜索接口要求的一个固定参数缺失会返回异常页。resp.encoding utf-8不能省微博返回的页面头部没有显式 charsetrequests 可能误判为 gbk导致中文乱码。拿到 HTML 后用 BeautifulSoup 解析。搜索结果里每条微博是一个div classcard-wrap需要从里面抽取 mid、用户名、正文、时间和转发评论点赞数。def parse_html(html): soup BeautifulSoup(html, html.parser) items [] for card in soup.select(div.card-wrap): try: mid card.get(mid) username card.select_one(.name).text.strip() content card.select_one(.txt).text.strip() # 时间可能在 .from 里也可能在 .time 里 time_node card.select_one(.from a) or card.select_one(.time) pub_time time_node.get(title) if time_node else nums [int(x) if x.isdigit() else 0 for x in card.select_one(.act).stripped_strings] items.append([mid, username, content, pub_time, *nums]) except Exception as e: continue return itemsstripped_strings会把转发、评论、点赞的文本和数字混在一起例如转发 123、评论 45、赞 6。这里用列表推导把纯数字挑出来不是数字的置为 0。注意异常处理必须写因为很多卡片结构不完整某一处select_one返回 None 会导致整条解析失败用continue跳过是稳妥的做法。2.4 CSV 字段设计与去重采集到的字段保存为articleData.csv字段不一定固定但至少包含以下列字段名说明示例mid微博唯一 ID用于去重4fJqS2xyzusername发博用户名科技小观察content微博正文今天看到一份报告…pub_time发布时间2025-01-15 10:32repost_count转发数123comment_count评论数45like_count点赞数678写入 CSV 时重点做两件事按mid去重以及把时间字符串统一成YYYY-MM-DD HH:MM:SS格式。时间格式化直接用pandas.to_datetime不要自己写字符串切片因为微博的时间格式有两种今天发布的显示01-15跨年显示2024-12-30统一处理才方便后面按小时或按天聚合。def save_articles(items, filepatharticleData.csv): df pd.DataFrame(items, columns[ mid, username, content, pub_time, repost_count, comment_count, like_count]) df df.drop_duplicates(subsetmid, keepfirst) df[pub_time] pd.to_datetime(df[pub_time], formatmixed) df.to_csv(filepath, indexFalse, encodingutf-8-sig) return len(df)utf-8-sig编码是为了让 Excel 直接打开时中文不乱码。如果用utf-8Excel 会把 UTF-8 BOM 误认成 ANSI导致第一列出现乱码。这一点在答辩演示时非常重要。2.5 反爬与采集节奏微博的反爬主要体现在两个维度IP 频率限制和账号风控。常见做法是每次请求之间随机 sleep 1~3 秒并限制单次任务抓取不超过 10 页。如果发现返回页面里没有card-wrap几乎可以断定 Cookie 已失效或被风控此时应该停止并提示用户重新登录而不是继续跑空循环。for page in range(1, 11): html fetch_weibo(keyword, cookie, page) items parse_html(html) if not items: break save_articles(items, fdata/{keyword}.csv) time.sleep(random.uniform(1, 3))休息时间用random.uniform(1, 3)而不是固定 2 秒是为了打乱请求间隔的固定模式。这里还要注意采集每一条微博的评论时需要再请求一次https://weibo.com/ajax/statuses/...获取评论列表评论字段与正文分开存到articleComments.csv用mid作为外键关联。3. 情感分析从 SnowNLP 到词典修正的落地细节3.1 为什么选 SnowNLP 而不是 BERT对于毕业设计来说情感分析的结果只要符合直观认知即可不一定非要追求 SOTA 准确率。SnowNLP 是一个纯 Python 实现的朴素贝叶斯情感分析库训练语料来自电商评论虽然领域不完全匹配微博但胜在无需 GPU、无需微调、安装即用。BERT 等预训练模型准确率更高但要处理 GPU 环境、模型下载、推理速度反而容易把项目复杂度推向失控。如果后续想提升效果可以替换成百度的paddlehub的senta模型这是后话。3.2 情感得分计算与阈值划分SnowNLP 的sentiments属性返回 0~1 的分数越接近 1 越正向越接近 0 越负向。我一般按 0.4 和 0.6 作为分界from snownlp import SnowNLP def classify_sentiment(text, pos_th0.6, neg_th0.4): if not text or len(text.strip()) 2: return 中性 s SnowNLP(text) score s.sentiments if score pos_th: return 正向 elif score neg_th: return 负向 else: return 中性阈值不是固定不变的。如果整套系统用在电商评论上pos_th可以提高到 0.7因为电商评论整体偏正向而微博里负面情绪的表达更隐晦0.6 更合适。调试时把分布打印出来看三个类别的数量是否均衡再微调阈值。3.3 评论级与正文级的情感聚合我建议对评论和正文分别做情感分析。正文表达的是博主的观点评论反映的是大众反馈两者分开统计更有价值。比如一条吐槽帖的正文是负向但评论区可能吵成两派。聚合逻辑按目标词分组def aggregate_by_keyword(articles_df, comments_df): article_senti articles_df.copy() article_senti[sentiment] article_senti[content].apply(classify_sentiment) # 评论按 mid 关联到目标词 merged comments_df.merge(articles_df[[mid, keyword]], onmid, howleft) comment_senti merged.copy() comment_senti[sentiment] comment_senti[comment].apply(classify_sentiment) summary article_senti.groupby([keyword, sentiment]).size().unstack(fill_value0) return article_senti, comment_senti, summary这里merge的作用是把评论数据里每一条评论的mid映射到它所属的关键词。注意articles_df中必须保留keyword字段否则多个目标词的结果全混在一起无法区分。3.4 自定义情感词典修正SnowNLP 的缺陷是没考虑否定词和程度副词。比如「不漂亮」得分可能偏高因为「漂亮」本身是强正面的。一个简单的修正规则是检测文本中是否存在否定词不、没、莫、非如果存在且情感得分绝对值大于中位数则反转类别。NEG_WORDS [不, 没, 莫, 非, 难以, 无法] def corrected_classify(text): label classify_sentiment(text) if any(w in text for w in NEG_WORDS): if label 正向: return 负向 elif label 负向: return 正向 return label注意这个规则的副作用「不负责任」本来是负向会被误判为正向。所以更稳妥的做法是只对得分介于 0.35~0.65 之间且含否定词的文本做翻转避免用力过猛。我常用的策略是先直接分类再看混淆矩阵——把随机抽样的 200 条人工标注计算准确率如果低于 70%再考虑加否定词规则或换模型。3.5 结果导出与复核情感分析结果建议写回 CSV 新增列不要覆盖原始数据。最终article_comments_analyzed.csv包含原始字段、情感标签和得分三部分答辩时可以展示「原始评论 自动标签」的对照表。同时在可视化页面上提供一个「随机抽样」功能从每类情感中随机抽 5 条展示让老师逐条看是否合理。4. Flask ECharts 可视化目标词检索、情感占比与趋势图实现4.1 项目结构拆分系统后端用 Flask 提供 JSON 接口前端用 ECharts 生成图表。目录组织如下与项目中的多个 CSS 文件对应静态资源分离。weibo_analysis/ ├── app.py # Flask 入口 ├── spider.py # 爬虫模块 ├── sentiment.py # 情感分析模块 ├── data/ │ ├── target.csv │ ├── articleData.csv │ └── articleComments.csv ├── templates/ │ └── index.html └── static/ ├── css/ # main.css main3.css 等 └── js/4.2 Flask 数据接口设计前端需要三类数据总体情感占比、情感趋势、高频词。Flask 路由读取处理好的 CSV按参数返回 JSON。from flask import Flask, jsonify, render_template import pandas as pd app Flask(__name__) app.route(/) def index(): return render_template(index.html) app.route(/api/summary/keyword) def summary(keyword): df pd.read_csv(data/analyzed.csv, encodingutf-8-sig) sub df[df[keyword] keyword] pie_data sub[sentiment].value_counts().to_dict() # 转换 ECharts 格式 pie_data [{name: k, value: v} for k, v in pie_data.items()] return jsonify(pie_data)to_dict()返回的 key 是情感类别字符串但 ECharts 的 pie 系列需要{name, value}对象数组所以必须做一次转换。这里有个细节value_counts()默认按数量排序如果某类情感数量为 0结果里就不包含这个 key前端饼图会缺一个扇区可以在前端做默认填充也可以在后端补零。4.3 趋势图按天聚合评论的情感趋势需要按时间聚合图表展示每天的正负中三条折线。先把pub_time转成日期再分组app.route(/api/trend/keyword) def trend(keyword): df pd.read_csv(data/analyzed.csv, encodingutf-8-sig) sub df[df[keyword] keyword].copy() sub[date] pd.to_datetime(sub[pub_time]).dt.date trend sub.groupby([date, sentiment]).size().unstack(fill_value0) trend trend.reset_index() result { dates: trend[date].astype(str).tolist(), positive: trend.get(正向, [0]*len(trend)).tolist(), negative: trend.get(负向, [0]*len(trend)).tolist(), neutral: trend.get(中性, [0]*len(trend)).tolist() } return jsonify(result)注意trend.get(正向, [0]*len(trend))很有必要。如果某一天没有正向评论unstack后那一列不存在直接trend[正向]会报 KeyError。用get兜底能避免接口崩溃前端拿到的数组长度也和日期对齐。4.4 词云与热词提取高频词展示用jieba分词排除停用词然后取 top 30 渲染到词云。由于 ECharts 没有官方词云组件可以用wordcloud2或 ECharts 的 scatter 模拟。后端只负责提供词频列表import jieba from collections import Counter STOP_WORDS set(微博 转发 分享 回复 评论 https weibo com.split()) app.route(/api/words/keyword) def words(keyword): df pd.read_csv(data/analyzed.csv, encodingutf-8-sig) sub df[df[keyword] keyword] texts .join(sub[content].tolist()) tokens [w for w in jieba.cut(texts) if w.strip() and len(w) 1 and w not in STOP_WORDS] counter Counter(tokens).most_common(30) return jsonify([{name: w, value: n} for w, n in counter])jieba.cut默认使用精确模式切出来的词大多是名词和动词能直接反映舆论焦点。len(w) 1过滤单字词避免「的」「了」「是」等高频无意义字干扰。停用词表不要写死在代码里放stopwords.txt更便于扩展。4.5 前端 ECharts 渲染逻辑index.html里通过 fetch 请求接口拿到数据后动态生成图表。概览页包含四个卡片舆情总数、正向数、负向数、中性数以及两个主体图表。async function loadData(keyword) { const [summary, trend, words] await Promise.all([ fetch(/api/summary/${keyword}).then(res res.json()), fetch(/api/trend/${keyword}).then(res res.json()), fetch(/api/words/${keyword}).then(res res.json()) ]); renderPie(summary); renderLine(trend); renderWordCloud(words); }这里用Promise.all并行请求三个接口避免串行等待。注意要处理关键字中含中文的情况前端需要encodeURIComponent(keyword)否则 URL 里的中文会被浏览器自动编码后端request.path拿到的可能不是原始中文。4.6 交互筛选与图表联动页面左侧是目标词下拉框从target.csv读取切换目标词时所有图表联动刷新。ECharts 实例要复用不能每次 new 一个否则内存泄漏切换多次后页面卡顿。正确做法是初始化一次后续用setOption更新const pieChart echarts.init(document.getElementById(pieChart)); pieChart.setOption({ series: [{ type: pie, data: summary }] });如果图表容器最初是隐藏的echarts.init拿到的宽度是 0渲染出来是空白。解决方法是初始化时传入容器宽度或者在显示后再resize()。5. 部署与排错环境配置、依赖安装与 Cookie 失效处理5.1 本地部署三步走环境建议 Python 3.8 以上版本不要用 3.12 以下太旧避免jieba和lxml的编译问题。部署流程归纳为三个命令pip install flask pandas requests beautifulsoup4 snownlp jieba python spider.py python app.py第一行安装所有依赖。beautifulsoup4在代码里用bs4导入注意包名和导入名的差异。python spider.py会先抓取网页数据并落到 CSV这个步骤不要省略否则可视化界面打开是空的。python app.py启动后访问http://127.0.0.1:5000。5.2 常见异常与排查对照表下面是这个项目最常遇到的五种报错以及对应的处理方式。报错信息原因解决办法ModuleNotFoundError: No module named bs4没装 beautifulsoup4pip install beautifulsoup4KeyError: 正向CSV 中不存在该情感列检查情感分析是否已执行或在代码用 get 兜底UnicodeDecodeErrorCSV 编码与读取不一致统一用encodingutf-8-sig或gbkHTTP 412Cookie 失效或频率过快重新登录微博获取 Cookie增加 sleep 间隔templates/404Flask 没找到模板确认templates目录和index.html是否存在5.3 一个实用技巧批量切换目标词自动生成报告做毕业设计答辩时老师大概率会问「换个关键词还能用吗」。为了避免现场重新跑爬虫我写了个小函数遍历target.csv里所有词抓完数据后分别生成各自的趋势图 JSON导出成静态 HTML 文件。这样答辩时可以直接打开预生成好的报告不需要现场联网。def generate_report(keyword): # 抓取数据 html fetch_weibo(keyword, cookie) items parse_html(html) save_articles(items, fdata/{keyword}.csv) # 情感分析与聚合 article_senti articles_df.copy() article_senti[sentiment] article_senti[content].apply(classify_sentiment) # 保存结果 article_senti.to_csv(fdata/{keyword}_analyzed.csv, indexFalse, encodingutf-8-sig)这个函数把爬虫、情感分析、结果导出串成一条链跑完一个关键词就产出一个 CSV前后端都从这些文件里读数据。日常调试时我建议每次抓取数据后先看 CSV 的行数如果不足 50 条先查 Cookie 是不是过期了再查目标词是否过于冷门。调整好采集这一步后面所有分析才有数据支撑。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价