简介:一份基于Python实现的微博舆情分析系统毕业设计源码,主要面向计算机相关专业学生、毕业设计者以及社交媒体数据分析爱好者。该系统完整覆盖微博数据采集、文本清洗、情感倾向分析、热点词提取与结果可视化等关键环节,并提供了可运行的前后端代码,能够直接用于课程设计、毕业设计演示或二次开发学习。压缩包共49个文件,总体积约2.94MB,包含Python后端源码(py)、前端页面样式与逻辑(css/js)、接口配置信息(json/xml)、数据库初始化脚本(sql),以及项目部署说明和核心代码目录myProject。目录划分清晰,便于按功能模块快速定位;部署说明zip附带了从环境准备到启动运行的指引,对初学者非常友好。此外,资源标签中的Java字样提示项目可能采用了多语言混合设计,有助于深入理解不同技术栈在Web系统中的应用。目前已有187人学习下载,适合希望快速上手舆情分析系统搭建的读者,作为毕业设计或实战项目参考。
1. 微博舆情分析系统:一个能直接跑起来的 Python 毕设源码
「微博舆情分析系统」这个关键词,在毕业设计平台上一搜一大片,但真正 zip 解压就能跑起来的其实不多。这套 Python 源码是其中少见的完整实例:爬虫负责从微博拿数据,分析模块负责算热词和情感倾向,Flask 后端配合 static 里的前端模板把结果渲染成页面,前后端代码全在 myProject 目录里,按部署说明配置环境后可以直接启动。它适合正在做毕业设计、课程设计的人,也适合想搞明白爬虫、分词、Web 展示是怎么串起来的数据分析初学者。需要提醒一句:压缩包标签里虽然带了 java,那更多是检索标签,核心实现是 Python,别被这个带偏。
2. 三层架构与目录拆解:爬虫、分析、展示的数据流整理
2.1 三层架构:采集层、分析层、Web 层各管什么
拿到压缩包先别急着跑,把结构理清楚,后面排错会省很多时间。这套系统从数据流上看是标准的三层:最下面是采集层,对应 xlwb_spider 和 spider 两个模块;中间是分析层,对应 analyze、hotword、api;最上面是 Web 展示层,入口是 app.py,配合 templates 和 static 目录渲染页面。
采集层的工作方式很简单直白:用 requests 请求微博移动端接口,拿到 JSON 数据,解析出微博正文、用户昵称、发布时间、点赞数、评论数、转发数,然后写入本地数据库。分析层则是在数据落库之后做两件事,一是对微博文本做 jieba 分词和词频统计,输出高频热词;二是用情感词典给每条微博打一个情感分,再聚合出整体倾向。Web 层做的事情是把这些分析结果通过 JSON 接口暴露给前端页面,前端用图表库渲染成词云、柱状图和趋势线。
2.2 为什么选 Python + Flask 而不是 Java Spring Boot
压缩包标签里的 java 容易让人误解,实际上这套系统在技术选型上是一个典型的 Python 毕设项目。爬虫部分用 requests 这种轻量级 HTTP 库就能搞定,没必要上 Scrapy;Web 端用 Flask 而不是 Django,是因为毕设场景只需要两三个页面和 JSON 接口,Flask 的灵活度更高,代码量也更少。
有个细节值得留意:资源目录里有 pojo 这个命名,这明显是从 Java 项目里带过来的习惯,用 Java 的术语管数据模型类。实际内容还是 Python 的类或字典结构,一般是定义微博、用户这类数据实体的。这是一个很常见的过渡期代码风格,项目作者估计是先用 Java 思维设计了一遍数据模型,再用 Python 实现,不影响运行,但你在读代码时要有这个心理预期。
2.3 目录逐层拆解与入口文件定位
我拆包时习惯先把目录树打出来,再逐个模块过一遍。下面是一个按实际场景整理后的结构,文件名如果和你的包略有出入,不奇怪,毕业设计项目整理代码时经常会有文件挪动:
myProject ├── app.py # Flask 入口,启动服务 ├── analyze/ # 舆情分析:情感打分、热词统计 ├── api/ # 对外 JSON 接口 ├── xlwb_spider/ # 微博爬虫主目录 │ └── spider/ # 具体爬虫实现 ├── pojo/ # 数据模型定义 ├── resource/ # 停用词表、情感词典等资源文件 ├── hotword # 热词结果输出目录或文件 ├── static/ # 前端样式和 JS 图表库 ├── templates/ # HTML 页面模板 └── .idea/ # PyCharm 工程配置第一步永远先看 app.py,它是整个系统的入口,路由怎么注册、接口怎么挂载、数据库怎么初始化,全部能从这一个文件顺藤摸瓜查清楚。resource 目录里的停用词表和情感词典是分析效果好不好看的关键,后面会专门讲。
提示:.idea 是 PyCharm 的项目配置目录,对运行没有影响,可以直接忽略。如果打开项目时 IDE 报错说找不到 Python 解释器,从这里也能看出这套代码原本就是用 PyCharm 开发的。
3. 微博爬虫 xlwb_spider:请求参数、登录态与数据落库
3.1 构造微博搜索接口:从 keyword 到响应解析
爬虫层的关键是选对接口。我拆过的毕设微博项目里,绝大多数都走微博移动端接口,也就是 m.weibo.cn 搜索页背后的接口,因为它返回的是结构化 JSON,不需要去解析复杂的 HTML 页面。下面这段代码是典型的请求构造方式:
import requests headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)", "Referer": "https://m.weibo.cn/", "Cookie": "SCookie=这里填你自己账号的登录Cookie" } def fetch_weibo(keyword, page=1, count=50): url = "https://m.weibo.cn/api/container/getIndex" params = { "containerid": "100103type=1&q=" + keyword, "page_type": "searchall", "page": page } resp = requests.get(url, headers=headers, params=params, timeout=10) data = resp.json() if data.get("ok") != 1: return [] cards = data["data"]["cards"] weibos = [] for card in cards: if card.get("card_type") != 9: continue mblog = card.get("mblog", {}) weibos.append({ "content": mblog.get("text", ""), "user": mblog["user"]["screen_name"], "time": mblog["created_at"], "like_count": mblog.get("attitudes_count", 0), "comment_count": mblog.get("comments_count", 0), "repost_count": mblog.get("reposts_count", 0) }) return weibos这里的参数有几个值得说明:containerid 是搜索容器的内部标识,q 后面拼上你要查的关键词;page_type 设为 searchall 表示全网搜索,改成 mention 之类的会变成搜索结果的分组方式。返回的 cards 里会混着多种 card_type,只有 card_type 等于 9 的才是真正的微博卡片,所以代码里先过滤一层,再取 mblog 字段拿正文和互动数据。
3.2 登录态与反爬:Cookie、随机延时、重试策略
微博这几年对自动爬取的管控挺严,毕设代码里如果直愣愣地高频请求,很快会被限制。我一般会把请求包装成带重试和延时的安全函数,这样可以显著提高采集成功率:
import random import time def safe_request(url, headers, params, max_retry=3): for attempt in range(max_retry): try: resp = requests.get(url, headers=headers, params=params, timeout=10) if resp.status_code == 200: return resp elif resp.status_code in (401, 403): print(f"第 {attempt + 1} 次被拒绝,确认Cookie是否失效") else: print(f"HTTP {resp.status_code}") except requests.exceptions.Timeout: print("请求超时,准备重试") time.sleep(random.uniform(3, 8)) return None随机延时是这类项目里最重要的反爬手段。固定延时会让对方服务器很容易识别出爬虫特征,随机分布在 3 到 8 秒之间,配合移动端的 User-Agent,基本能跑完一个小规模毕业设计所需的数据量。Cookie 失效是爬虫翻车的最常见原因,代码里如果发现连续出现 401,不要反复重试,直接提示人手去浏览器重新登录一次微博,把新 Cookie 贴回来更实际。
3.3 落库设计:微博数据存成什么结构才能喂给分析模块
爬下来的数据总得找个地方存。毕设项目常见的做法是 SQLite,文件型数据库不需要额外装服务,拷到哪都能跑。表结构长这样:
import sqlite3 conn = sqlite3.connect("weibo_analysis.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS weibo ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, user_name TEXT, publish_time TEXT, like_count INT, comment_count INT, repost_count INT, keyword TEXT ) """) conn.commit() def insert_weibo(rows, keyword): for row in rows: cursor.execute( "INSERT INTO weibo (content, user_name, publish_time, like_count, comment_count, repost_count, keyword) VALUES (?, ?, ?, ?, ?, ?, ?)", (row["content"], row["user"], row["time"], row["like_count"], row["comment_count"], row["repost_count"], keyword) ) conn.commit()这里有个容易被忽略的细节:content 字段在接口里是带 HTML 标签的,像 这种,入库前最好用正则把标签去掉,只保留纯文本,否则后边分词会分出一堆 "a"、"href" 这样的垃圾词。keyword 字段是给每次采集打标签的,方便分析层按关键词过滤数据。
注意:数据去重也值得做。翻页采集时微博接口偶尔会返回同一批数据,我一般会在插入前按 user_name + publish_time + content 拼一个唯一键检查一遍,或者在 SQLite 里建唯一索引,后续跑任务就不会积累重复脏数据。
4. 热词统计与情感分析:analyze 和 api 模块的关键实现
4.1 jieba 分词与停用词过滤:热词表是怎么产生的
热词统计是舆情分析系统最直观的产出。思路很朴素:把所有微博文本丢给 jieba 分词,统计每个词出现的频次,去掉停用词和单字,剩下的就是高频关键词。实际实现时,分词结果的干净程度决定热词榜的质量:
import jieba from collections import Counter STOP_WORDS = set() with open("resource/stopwords.txt", encoding="utf-8") as f: STOP_WORDS = set(line.strip() for line in f) def extract_hotwords(texts, top_n=20): word_counter = Counter() for text in texts: words = jieba.cut(text) for word in words: word = word.strip().lower() if len(word) < 2: continue if word in STOP_WORDS or not word.isalpha(): continue word_counter[word] += 1 return word_counter.most_common(top_n)stopwords.txt 是热词质量的命门。很多人直接拿网上的通用停用词表,结果还是出现一堆「我们」「你们」「这种」「因为」,因为这些词带有很强的场景性,通用表覆盖不全。我一般会先跑一次分词,把结果里出现频率最高的前两百个词打出来,人工扫一遍,把明显没意义的口语词补进停用词表,再重跑一次,效果立竿见影。
4.2 情感打分:情感词典加否定词和程度副词的权重
毕设项目的情感分析绝大多数不用深度学习,一套情感词典打分发就足够撑起整条链路。原理是给每个情感词一个正负分值,正面的加 1,负面的减 1,再叠加否定词和程度副词的影响:
positive_words = set(open("resource/positive.txt", encoding="utf-8").read().split()) negative_words = set(open("resource/negative.txt", encoding="utf-8").read().split()) deny_words = {"不", "没", "无", "非", "莫", "别"} degree_words = {"非常": 2.0, "很": 1.8, "有点": 0.7, "比较": 1.2, "太": 1.5} def sentiment_score(text): score = 0 words = jieba.cut(text) prev_deny = False degree = 1.0 for word in words: if word in deny_words: prev_deny = True elif word in degree_words: degree = degree_words[word] elif word in positive_words or word in negative_words: base = 1 if word in positive_words else -1 if prev_deny: base = -base score += degree * base prev_deny = False degree = 1.0 return score这里边界要清楚:词典法判断不了反讽和反语,「这电影太好了」带着明显阴阳怪气的时候,打出来可能还是正分。毕设答辩时能说清楚这个局限,比硬吹模型准确率要加分得多。出发点不是追求完美,而是用可解释的方式让老师和同学看得懂整个分析过程。
4.3 Flask 路由与前端图表:接口怎么把数据喂给 ECharts
后端和前端之间通过 JSON 接口衔接,app.py 里注册几个路由就能串起来:
from flask import Flask, render_template, jsonify, request app = Flask(__name__) @app.route("/") def index(): return render_template("index.html") @app.route("/api/hotword") def hotword_api(): keyword = request.args.get("keyword", "微博") hotwords = extract_hotwords_by_keyword(keyword) sentiment = sentiment_stats_by_keyword(keyword) return jsonify({ "hotwords": hotwords, "sentiment": sentiment }) @app.route("/api/weibo") def weibo_api(): keyword = request.args.get("keyword", "微博") rows = query_weibo_by_keyword(keyword) return jsonify({"list": rows}) if __name__ == "__main__": app.run(host="0.0.0.0", port=5000, debug=True)前端页面在 static 目录里引入 ECharts,通过 fetch 拿到 /api/hotword 返回的数组,再画成词云和柱状图。这里有个跨域陷阱要提醒:如果前端页面和后端不在同一个端口上跑,浏览器的 fetch 会被同源策略拦住。最省事的办法是让 Flask 同时负责渲染页面和提供接口,前端页面直接请求相对路径,而不是写死 http://localhost:8080 这样的绝对地址。
5. 部署避坑:微博爬虫与 Flask 项目最常见的五个问题
5.1 现象:压缩包解压到一半报错,文件不全
原因:下载平台偶尔会出现 zip 传输不完整的情况,另外有些压缩工具生成的 zip 带有伪加密标记,解压时会误报需要密码,导致代码目录缺失。
解决:先看压缩包体积和下载页显示的是否一致,不一致就重新下载;解压工具换成 7-Zip 或新版 WinRAR,右键解压而不是双击预览。解压完成后核对 myProject 目录是否包含 app.py,如果缺少这个文件,说明解压不完整,直接换工具重来。
5.2 现象:爬虫请求全部返回 401 或者 403
原因:Cookie 过期、失效,或者某个关键词短时间内请求太频繁触发了平台风控。spider 代码里的 headers 写死了一个 Cookie,你直接运行的时候用的还是作者当时的登录态。
解决:用浏览器登录微博账号,打开开发者工具,从 Network 面板里把当前请求的 Cookie 完整复制出来,替换代码里 headers 的 Cookie 字段。替换后不要急着连续跑多个关键词,先单关键词跑 10 条试试接口通不通,通了再放开跑。跑的过程中看到 403 就停下来等几分钟,而不是加大请求量。
5.3 现象:热词排行全是「我们」「什么」「就是」,核心关键词一个看不到
原因:resource 目录里的停用词表不匹配你的数据场景,或者分词后没有过滤非中文词汇。很多毕设项目自带的停用词表是直接从网上扒的通用版本,覆盖不到口语化的微博文本。
解决:把 extract_hotwords 里 len(word) < 2 的判断改成同时过滤纯符号和数字,再补充停用词。我自己习惯的调试方式是先打印原始分词结果的前 500 个词,人工识别高频干扰词,追加到 stopwords.txt 里。追加过程重复两轮,热词表基本就干净了。
5.4 现象:Flask 页面能打开,但图表区域空白
原因:前端 fetch 接口报错或者返回的数据格式跟图表库预期不一致。最常见的是 JSON 里包含 Python 的 tuple,ECharts 收到后解析不出 [{name: '...', value: 123}] 这样的结构。
解决:先按 F12 打开浏览器开发者工具,看 Console 和 Network 标签页里的报错。如果是 tuple 序列化问题,在 Flask 接口里把返回数据强制转成 list 再交给 jsonify。另外确认 static/js 里的 ECharts 脚本有没有正确引入,路径大小写都会导致加载失败。
5.5 现象:项目标签写的是 java,以为要用 Spring Boot 和 Maven 环境
原因:资源平台上的检索标签通常是平台统一打的,java 和 python 经常混标。拿到手的人按 Java 项目部署,环境全部装错,自然跑不起来。
解决:先打开 myProject 目录看文件后缀,.py 为主就是 Python 项目,只有一个 app.py 起步文件。环境上只需要 Python 3.8 以上版本,装好 requirements.txt 里的 Flask、requests、jieba 依赖就够了,不需要装 JDK 和 Maven。
提示:依赖安装慢是另一个高频问题。pip install 超时的,换成清华源:pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。如果项目里没有 requirements.txt,手动装三个包就够:flask、requests、jieba。
6. 验证技巧:用一条热搜把整套链路冒烟测一遍
部署完项目,先别急着跑几十个关键词,我习惯用一个固定热搜词做冒烟测试,验证整条链路是通的,再放量。比如拿「郑州暴雨」这类既有明确讨论度、又容易判断情感倾向的事件,跑一遍完整流程。
第一步,用网站页面手动触发爬虫,抓 50 到 100 条微博,观察采集层是否有报错。确认数据入库后,去 SQLite 里数一下记录数,正常情况下 keywords 字段能查到对应标签的数据。
第二步,调出热词接口,看 top20 结果里是否包含事件核心词。如果核心词不在,大概率是分词或停用词表问题,回到第 4 章的方法去补词。
第三步,检查情感分析结果。拿人工看过的一批微博比对,比如明显正面的「感动」「致敬」类文本,打分应该为正;明显负面的「谴责」「愤怒」类文本,打分应该为负。我的习惯是抽 20 条人工标注结果,跟系统打分做一次粗略比对,准确率能到百分之七八十,就说明整个分析链路是能自洽的。
最后写一个最简单的接口自检脚本,把上述步骤固化:
import requests def smoke_test(keyword="郑州暴雨"): hot_resp = requests.get("http://127.0.0.1:5000/api/hotword", params={"keyword": keyword}, timeout=10) assert hot_resp.status_code == 200 hot_data = hot_resp.json() assert len(hot_data["hotwords"]) > 0, "热词为空,检查分词层" print("热词接口正常,前5个热词:", hot_data["hotwords"][:5]) weibo_resp = requests.get("http://127.0.0.1:5000/api/weibo", params={"keyword": keyword}, timeout=10) assert len(weibo_resp.json()["list"]) > 20, "微博数据少于20条,检查采集层" print("微博数据接口正常,共获取", len(weibo_resp.json()["list"]), "条记录") if __name__ == "__main__": smoke_test()这套冒烟测试脚本跑通了,再换其他关键词放量采集就有把握了。从那以后,我每次拿到这类毕设源码都会强制走一遍这个过程:先冒烟测接口,再验证分词质量,最后抽查情感打分。不少项目在单独模块上看着没问题,一旦串起来就暴露接口字段对不上、数据格式不匹配的毛病,提前跑一遍比排查到半夜强得多,希望帮到你。
本文还有配套的精品资源,点击获取