基于Python的微博舆情分析与话题热度预测毕设实战指南 简介这份资源是面向计算机、电子信息工程、数学等专业大学生的课程设计或毕业设计参考资料聚焦新浪微博#新冠疫情话题的舆情分析与话题热度预测。项目经导师指导并认可通过适合作为大作业、期末课设或毕设的完整参考方案帮助读者理解从数据采集、文本预处理到热度建模的完整链路。压缩包共约2000个文件整体183.11MB其中1129个Python脚本承担爬虫、分词、情感分析与预测建模等核心逻辑724个JSON文件保存微博原始数据与中间结果另有71个txt说明、30个C头文件、12份PDF文档及少量前端与C/C源码兼顾算法实现与工程细节。目前已有35人学习下载。借助该资源读者可参考话题热度预测的建模思路、文本特征工程与可视化呈现方式并对照目录结构快速定位数据、代码与文档模块为自身课题提供可复用的实现框架与排错参考。1. 从一份微博话题数据说起舆情分析与热度预测到底在做什么如果你正在找计算机毕设选题或者被“一点毕设”“知网人工智能毕设选题”这类关键词反复刷屏大概率会撞上“舆情分析”这个方向。它听起来比“基于springboot的java毕设”有技术含量又不像“基于rk3588yolo的毕设项目”那样吃硬件属于纯 Python 课设里性价比很高的一类。但真正动手时你会发现难点不在模型而在数据微博话题页的文本短、噪声大、口语化严重还夹着大量转发和表情符号直接套教科书里的情感分析流程准确率会低到让你怀疑人生。这份“新浪微博#新冠疫情话题 舆情分析与话题热度预测”要解决的就是两件事一是把话题下海量微博的情感倾向、关键词、时间分布拆出来二是用历史热度去预测未来几天的讨论量走势。它适合计算机科学与技术毕设选题、网络工程毕设选题里偏数据方向的同学也适合想练一遍“爬取—清洗—分析—建模—可视化”完整链路的 Python 课设。下面我按自己带学生做这类题目的顺序把能复现的路径讲清楚。2. 数据从哪来、怎么存微博话题数据的采集与清洗2.1 采集前先想清楚你要的是话题页还是关键词搜索很多人一上来就写爬虫结果抓回来的字段对不上分析需求。微博话题数据有两个入口话题页#新冠疫情# 这种带井号的聚合页和关键词搜索结果。话题页的好处是内容天然聚焦坏处是翻页限制明显关键词搜索覆盖更广但会混进大量无关微博。我的建议是主用话题页关键词搜索作为补充采集字段至少包含微博正文、发布时间、点赞数、评论数、转发数、用户昵称脱敏后使用、是否原创。采集环节本身不复杂常见做法是用 requests 配合移动端接口或者用 Selenium 模拟滚动加载。这里不展开具体接口地址因为平台规则会变重点是你要在代码里把“翻页终止条件”和“去重逻辑”写死否则跑一晚上回来一半是重复数据。import requests import time import pandas as pd HEADERS { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X), Accept: application/json, } def fetch_page(keyword, page): 按关键词和页码拉取一页数据返回原始 json params {q: keyword, page: page, count: 20} resp requests.get(https://m.weibo.cn/api/container/getIndex, headersHEADERS, paramsparams, timeout10) if resp.status_code ! 200: return None return resp.json() def parse_cards(data): 从返回 json 里抽出微博卡片过滤掉广告和推荐位 rows [] cards data.get(data, {}).get(cards, []) for card in cards: if card.get(card_type) ! 9: # 9 才是普通微博卡片 continue mblog card.get(mblog, {}) rows.append({ text: mblog.get(text, ), created_at: mblog.get(created_at, ), attitudes: mblog.get(attitudes_count, 0), comments: mblog.get(comments_count, 0), reposts: mblog.get(reposts_count, 0), }) return rows all_rows [] for p in range(1, 51): # 先跑 50 页试水确认字段再放量 raw fetch_page(新冠疫情, p) if not raw: break all_rows.extend(parse_cards(raw)) time.sleep(2) # 控制频率别把接口打挂 df pd.DataFrame(all_rows).drop_duplicates(subset[text]) df.to_csv(weibo_raw.csv, indexFalse, encodingutf-8-sig)这段代码的逻辑是“请求—解析—去重—落盘”四步。card_type 9这个判断很关键微博返回的卡片里混着广告和话题推荐不过滤的话后面情感分析会被带偏。time.sleep(2)是血泪经验频率太高轻则返回空数据重则直接封 IP。落盘用utf-8-sig是为了 Excel 打开不乱码答辩演示时省事。2.2 清洗比采集更费时间正则、分词和停用词原始微博正文里全是 某人、#话题#、网页链接和表情符号不清洗直接分词结果里会冒出一堆“回复”“网页链接”这种高频噪声词。清洗分三步去标签、去特殊符号、分词去停用词。import re import jieba STOPWORDS set(open(stopwords.txt, encodingutf-8).read().split()) def clean_text(text): 去掉 、话题符号、链接、表情代码只留中文和基本标点 text re.sub(r[\w\u4e00-\u9fa5-], , text) # 去 text re.sub(r#.*?#, , text) # 去话题标签 text re.sub(rhttp[s]?://\S, , text) # 去链接 text re.sub(r\[.*?\], , text) # 去表情代码 text re.sub(r[^\u4e00-\u9fa5。], , text) # 只留中文和标点 return text.strip() def cut_words(text): 分词并过滤停用词和单字 words jieba.lcut(text) return [w for w in words if w not in STOPWORDS and len(w) 1] df[clean] df[text].apply(clean_text) df[words] df[clean].apply(cut_words) df df[df[clean].str.len() 5] # 丢掉清洗后过短的无效微博clean_text里几个正则的顺序不能乱先去 再去话题否则话题里的 会被漏掉。len(w) 1过滤单字是因为中文单字在舆情分析里几乎没有区分度。停用词表建议用哈工大或百度的那份再手动补上“转发”“微博”“疫情”这类在本话题下高频但无分析价值的词。清洗完的数据量通常会掉三到四成这是正常的别心疼。3. 情感分析与话题热度预测模型怎么选、参数怎么调3.1 情感分析SnowNLP 够用但别信它的原始输出毕设里做中文情感分析SnowNLP 是最省事的选择不用训练直接调用。但它有个坑对微博这种短文本原始情感得分经常挤在 0.5 附近区分度很差。我的做法是先用 SnowNLP 打底再用关键词规则做二次修正。from snownlp import SnowNLP POS_WORDS [加油, 感谢, 致敬, 希望, 好转, 康复] NEG_WORDS [恐慌, 死亡, 确诊, 封控, 崩溃, 谣言] def sentiment_score(text): SnowNLP 打底关键词规则修正返回 0-1 得分 base SnowNLP(text).sentiments pos_hit sum(1 for w in POS_WORDS if w in text) neg_hit sum(1 for w in NEG_WORDS if w in text) if pos_hit neg_hit: base min(base 0.15, 1.0) elif neg_hit pos_hit: base max(base - 0.15, 0.0) return base df[sentiment] df[clean].apply(sentiment_score) df[label] df[sentiment].apply( lambda s: 正面 if s 0.6 else (负面 if s 0.4 else 中性))0.6和0.4这两个阈值不是拍脑袋定的你可以画一下情感得分的分布直方图通常会在两端各有一个峰取峰谷位置作为阈值最稳。关键词修正的0.15是经验值调太大规则会盖过模型调太小又没效果。这套组合在微博短文本上人工抽检 200 条能到 75% 左右的准确率对毕设足够但别在论文里吹到 95%。3.2 热度预测用时间序列还是回归先看你的数据粒度话题热度预测的本质是“用过去几天的讨论量预测未来几天”。这里有个选型分叉如果按天聚合数据点太少一个话题火两周也就 14 个点ARIMA 这类时间序列模型参数都估不准如果按小时聚合数据点够了但噪声大。我一般按天聚合然后用“滑动窗口 梯度提升回归”来做把前 3 天的点赞、评论、转发、发博量作为特征预测第 4 天的发博量。import numpy as np from sklearn.ensemble import GradientBoostingRegressor from sklearn.model_selection import train_test_split # 按天聚合 df[date] pd.to_datetime(df[created_at]).dt.date daily df.groupby(date).agg( posts(text, count), likes(attitudes, sum), comments(comments, sum), reposts(reposts, sum), ).reset_index().sort_values(date) # 构造滑动窗口特征用前 3 天预测第 4 天 WINDOW 3 for i in range(1, WINDOW 1): daily[fposts_lag{i}] daily[posts].shift(i) daily[flikes_lag{i}] daily[likes].shift(i) daily daily.dropna() features [c for c in daily.columns if lag in c] X daily[features] y daily[posts] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, shuffleFalse) # 时间序列不能 shuffle model GradientBoostingRegressor( n_estimators100, learning_rate0.1, max_depth3, random_state42) model.fit(X_train, y_train) print(R2:, model.score(X_test, y_test))shuffleFalse是时间序列建模的铁律打乱顺序会让测试集泄露未来信息R2 虚高到 0.9 以上答辩时老师一问就露馅。max_depth3是防止过拟合数据量小的时候树太深必然过拟合。n_estimators100和learning_rate0.1是常规起点你可以画学习曲线看要不要加树。评价指标别只看 R2MAE 更直观因为它和“发博量”同量纲能直接说“平均误差多少条”。3.3 关键词与可视化词云之外时间轴才是加分项词云图几乎每份舆情分析毕设都有做出来不难难的是做出区分度。我的建议是分情感做词云正面微博一张、负面微博一张对比着放比一张大杂烩词云有说服力。可视化用 pyecharts 做时间轴折线图把发博量和情感均值叠在一张图上能看出“负面情绪峰值往往领先于讨论量峰值”这种结论这才是舆情分析的价值点。from pyecharts.charts import Line from pyecharts import options as opts line Line() line.add_xaxis([str(d) for d in daily[date]]) line.add_yaxis(每日发博量, daily[posts].tolist()) line.add_yaxis(情感均值, (df.groupby(date)[sentiment] .mean().round(3).tolist())) line.set_global_opts( title_optsopts.TitleOpts(title话题热度与情感走势), yaxis_optsopts.AxisOpts(name发博量), ) line.render(trend.html)双 Y 轴这里要注意发博量是几百上千情感均值是 0 到 1不分开轴的话情感线会被压成一条直线。pyecharts 默认单轴需要手动加yaxis_index或者干脆用两个独立图表上下排列答辩时讲起来更清楚。4. 避坑与排查这类毕设最容易翻车的 5 个地方现象一爬回来的数据全是“网页链接”和“转发微博”。原因是没过滤card_type把推荐位和广告卡片也解析了。解决办法是在parse_cards里严格判断卡片类型并且对正文做长度过滤清洗后少于 5 个字的直接丢。现象二情感分析结果一边倒全是中性。原因是 SnowNLP 对短文本的得分集中在 0.5 附近阈值又设得太宽。解决办法是画得分分布图把阈值收紧到 0.6/0.4再叠加关键词规则修正必要时人工标注 100 条做验证集。现象三热度预测 R2 高得离谱但预测曲线和真实曲线对不上。原因是train_test_split没设shuffleFalse或者用了未来数据做特征。解决办法是严格按时间切分特征只能用当前时刻之前的数据并且用 MAE 辅助判断。现象四词云图里全是“疫情”“新冠”这种话题词。原因是停用词表没针对话题补充。解决办法是手动把话题核心词加进停用词或者用 TF-IDF 取权重最高的词而不是词频最高的词。现象五代码在本地跑通换台机器就报编码错误。原因是 CSV 读写没统一编码。解决办法是落盘统一用utf-8-sig读的时候显式指定encodingutf-8-sig别依赖系统默认。5. 让这份毕设从“能跑”到“能讲”一个验证技巧和我的习惯大部分这类题目做到第 4 章就能交差了但答辩时真正拉开差距的是你能不能证明“我的预测不是碰运气”。我一般会加一个回测环节把最后 7 天留出来不参与训练用模型滚动预测这 7 天然后把预测值和真实值画在同一张图上算一个 MAPE平均绝对百分比误差。这个数字比 R2 更能说服老师因为它直接告诉你“预测偏差百分之多少”。# 留出最后 7 天做滚动回测 holdout daily.tail(7) train daily.iloc[:-7] model.fit(train[features], train[posts]) preds model.predict(holdout[features]) mape np.mean(np.abs((holdout[posts] - preds) / holdout[posts])) * 100 print(f7 天回测 MAPE: {mape:.2f}%)如果 MAPE 在 15% 以内说明模型有实用价值超过 30%就得回头检查特征是不是漏了关键变量比如是否节假日、是否有突发事件。这个回测表建议直接放进论文的实验章节比堆一堆模型对比表实在。另外说个习惯这类毕设的数据集和代码我从来不在最后一天才整理。从第一天起就按data/raw、data/clean、src、notebook、output分目录每个脚本头部写清楚输入输出路径。这样到写论文时你只需要把 notebook 里的图导出不用满硬盘找文件。舆情分析这个方向数据清洗和特征工程占了七成工作量模型只占三成把这个比例记在心里选题和排期就不会翻车。希望帮到你。本文还有配套的精品资源点击获取