简介:面向人工智能课程设计与期末大作业场景,这是一套基于Python的网络舆情分析系统完整项目,包含源码、全部数据与文档说明,定位为高分通过的可复用交付方案。项目下载后无需修改即可运行,适合在校生快速完成课题实现、实验验证与期末答辩准备,也可作为毕业设计或课程项目的原型参考。压缩包共118个文件,整体约45.22MB;其中py源码是系统算法与界面逻辑的主体,java、class与jar包体现辅助功能与依赖支持,txt、xml、json等承载配置说明、数据脚本与结果文本,ipynb与xlsx分别记录分析过程与数据表格,目录分层合理,便于按模块检索和二次学习。目前已有1476人学习下载,项目稳定性和实用性获得较多验证,在网络舆情分析选题中具备较高参考价值。系统内含柱状图、饼图等可视化组件,可直观呈现舆情统计结果;从预览中的类名也能看出项目包含界面交互与统计图表模块,而非单纯的脚本集合。配套的完整数据集与文档说明,有助于梳理需求分析、环境配置和设计思路,支撑从代码调试、结果输出到答辩讲解的全流程,为课程设计或期末大作业提供扎实的参照范本。
1. 人工智能期末大作业:基于Python的网络舆情分析系统,真正卡人的是数据
如果把一份网络舆情分析系统的大作业拆开看,你会发现情感分析算法反而是最不伤脑筋的部分,真正让人熬到凌晨的是数据采集和清洗。这个系统解决的就是这样一件事:从新闻、微博或论坛评论区抓取文本,完成分词、清洗、情感打分和主题聚类,最后生成可视化报告给答辩老师看。它把爬虫、自然语言处理、数据可视化三个模块串在了一条流水线上,覆盖面恰好卡在课程设计的评分点上。适合两类人:一是正在做人工智能或大数据课程设计、期末大作业的学生,二是想用最短路径把“爬虫+情感分析”跑通的从业者。你拿到手的源码包包含完整代码和全部数据,但源码不会替你避开环境问题,所以这篇文章重点讲怎么把系统跑起来,以及跑起来之后哪些参数值得改。
2. 项目结构与环境搭建:拿到源码包先做这三步
源码包下载之后,大多数人会犯同一个错误:直接双击main.py看报错,然后被一堆红色异常吓退。正确顺序应该是先看目录结构,再建虚拟环境装依赖,最后才跑主程序。顺序反了,你会在依赖冲突里浪费一个晚上。
2.1 先读目录结构,搞清楚每个文件夹干什么
课程设计的源码包虽然命名习惯各不相同,但结构上大致逃不出下面这张表。先花五分钟对照自己的项目找对应关系,后面排错才知道去哪个文件里改:
| 目录/文件名 | 职责 | 对应技术点 |
|---|---|---|
spider/或crawler/ | 舆情数据采集 | requests、BeautifulSoup、Scrapy |
processor/或cleaner/ | 去重、清洗、分词 | re、jieba、pandas |
analysis/ | 情感分析与主题建模 | SnowNLP、scikit-learn |
visual/或templates/ | 图表与前端展示 | pyecharts、matplotlib、ECharts |
data/ | 采集结果与中间数据 | CSV、SQLite、JSON |
app.py或main.py | 程序入口,串起全流程 | Flask 或纯命令行 |
这套分层是课程设计最常见的“分层解耦”写法,每一层只干一件事。比如你在可视化阶段发现图表数据不对,优先查analysis/的输出文件而不是去爬虫文件夹里翻。如果源码包的目录比这张表更少——比如只有两个.py文件——那大概率是单体脚本,逻辑全挤在一起,这时候改代码要格外小心。
2.2 用虚拟环境装依赖,别污染全局 Python
很多学生的机器上同时装了 Python 3.8 和 3.12,全局环境里还有 Anaconda 带进来的一堆包。如果直接把源码包的依赖装进全局环境,轻则版本冲突,重则把其他项目的环境搞坏。我一般会先建一个独立虚拟环境,再按requirements.txt安装:
# 在项目根目录下创建虚拟环境,venv 是环境名,可自定义 python -m venv venv # 激活虚拟环境(Windows 用第一种,macOS/Linux 用第二种) venv\Scripts\activate source venv/bin/activate # 升级 pip 并安装依赖,-r 表示从文件逐行读取包名 pip install --upgrade pip pip install -r requirements.txt逻辑说明:第一条命令创建了一个与系统隔离的 Python 环境,后续pip install的包只会装进这个环境,不会干扰其他项目。第三条命令从requirements.txt读取依赖列表并安装。
参数说明:如果requirements.txt里没有锁定版本号而你装到最新版后程序报错,千万别急着删环境。最稳妥的做法是先看报错信息指向哪个包,再手动指定降级版本,比如pip install snownlp==0.12.3。另外,python -m venv venv里的第一个venv是模块名,第二个venv是文件夹名,可以改成env或myenv,但记得把启动命令里的路径同步改掉。
2.3 第一次运行:用调试模式跑通主流程
环境装好之后不要一上来就跑完整流程,先确认入口文件能启动。以 Flask 类入口为例,常见写法长这样:
# app.py 的启动入口部分 if __name__ == "__main__": # 关闭 debug 之前先确定代码能正常导入 app.run(host="127.0.0.1", port=5000, debug=True)逻辑说明:debug=True能让代码修改后自动重启服务,并且浏览器里能看到详细报错栈。课程设计阶段开着它方便排查问题,但答辩演示时务必关掉,否则停在调试器页面会很尴尬。
参数说明:host="127.0.0.1"表示只允许本机访问。如果需要让同一局域网的另一台电脑打开演示页面,改成"0.0.0.0"即可;port=5000如果被占用,换成 8080 或 5001 都行。跑通之后你会看到类似Running on http://127.0.0.1:5000的提示,用浏览器打开就能看到系统界面。
跑通这一步才算拿到“地基”。我见过太多人在这一步翻车:依赖装不上、端口被占用、Python 版本不匹配。别硬扛,优先看报错信息里最后一行,那才是真正的问题。
3. 数据采集与清洗:爬虫参数决定数据质量,决定答辩分数
说一句会被反复印证的话:舆情分析系统的数据质量是下限,算法是上限。爬虫模块如果只抓回来一堆乱码和重复内容,再漂亮的情感分析图也是空中楼阁。这一章讲清楚采集层的选型理由,以及清洗环节最容易漏掉的细节。
3.1 为什么课程设计用 requests + BeautifulSoup 而不是 Scrapy
很多教程一上来就吹 Scrapy 的异步性能,但那是生产环境的考量。课程设计的数据量级通常在几千到几万条,用 Scrapy 等于杀鸡用牛刀,还要额外学习它的 Item Pipeline 和 Middleware 机制,两周时间未必啃得下来。常见做法是用 requests 发送请求,用 BeautifulSoup 解析 HTML,这套组合的学习曲线平缓、出错时好排查,而且完全够用。
# spider/demo_spider.py 核心请求函数 import requests from bs4 import BeautifulSoup import time import random def fetch_page(url, retry=3): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "text/html,application/xhtml+xml", "Referer": "https://www.baidu.com/" } for attempt in range(retry): try: # timeout 必须设置,否则请求卡死会阻塞整个流程 resp = requests.get(url, headers=headers, timeout=10) resp.raise_for_status() resp.encoding = resp.apparent_encoding return resp.text except requests.RequestException as e: print(f"第 {attempt + 1} 次请求失败: {e}") # 指数退避:失败后等待时间随次数增加 time.sleep(random.uniform(1, 3) * (attempt + 1)) return None逻辑说明:retry参数控制重试次数;timeout=10表示 10 秒没响应就放弃,避免某个链接拖垮全部采集任务。resp.encoding = resp.apparent_encoding是关键的编码自适应策略,很多网站返回的响应头编码和实际内容不一致,加上这行能减少一堆乱码。
参数说明:User-Agent建议定期轮换,网上有很多 UA 列表可以直接抄。time.sleep(random.uniform(1, 3))是新手最容易忽略的频率控制,没有延时地连续请求很容易被网站封 IP。
3.2 解析与清洗:正则去噪,比什么都重要
拿到 HTML 之后,下一步是从中抽取正文文本。这一步看似简单,但新闻页面里混着导航、广告、相关推荐,不洗干净会影响后面所有的分析结果。我习惯先提取大块文本,再用正则做定向清理:
import re from bs4 import BeautifulSoup def extract_and_clean(html_text): soup = BeautifulSoup(html_text, "html.parser") # 去掉 script 和 style 里的内容,那些不是正文 for tag in soup(["script", "style", "header", "footer", "aside"]): tag.decompose() # 获取页面正文区域,具体选择器以实际网页为准 content = soup.select_one("div.article-content, div.content, article") text = content.get_text(separator="\n") if content else soup.get_text() # 正则清洗:去掉多余空白、特殊字符、连续换行 text = re.sub(r"\s+", " ", text) text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''()《》]", "", text) return text.strip() # 示例用法 html = fetch_page("https://example.com/news/123") clean_text = extract_and_clean(html) print(clean_text[:100])逻辑说明:tag.decompose()是从树中彻底移除噪点标签;get_text(separator="\n")把段落用换行符拼接,方便后续按句切分。正则清洗的第二行是白名单思路——只保留中英文、数字和常见标点,其余一律删掉,这比黑名单更不容易漏。
参数说明:白名单正则[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''()《》]可以按需增删标点符号,比如你要保留英文句点.和百分比%,就加到中括号里。如果清洗后内容变空,先打印原始文本看看是不是选择器没匹配到正文区域。
3.3 数据持久化:存 CSV 还是 SQLite
清洗完的数据总得有地方放。数据量在几千条量级时,SQLite 比 CSV 更合适,因为它支持 SQL 查询,而且不会被 Excel 强制打开导致编码错乱。
import sqlite3 import pandas as pd def save_to_sqlite(df, db_path="data/weibo.db"): conn = sqlite3.connect(db_path) # if_exists="append" 表示追加写入,避免重复采集时覆盖历史数据 df.to_sql("weibo_posts", conn, if_exists="append", index=False) conn.close() # 顺手输出入库条数,方便和源数据量做核对 print(f"已写入 {len(df)} 条记录到 {db_path}")逻辑说明:to_sql是 pandas 封装好的便捷方法,它会自动根据 DataFrame 的列名建表。if_exists="append"适合增量采集场景,如果你的任务是全量重抓,改成"replace"就好。
参数说明:db_path建议用相对路径,别写绝对路径,否则换台电脑跑就要改代码。字段里如果包含长文本,SQLite 不用预先定义字段长度,这点比 MySQL 省心得多。
提示:清洗时如果发现 DataFrame 里有重复值,先
df.drop_duplicates(subset=["content"])再去重,再入库,否则后面情感分析会把同一条内容算两遍,结果虚高。
4. 情感分析与主题聚类:从能跑通到能答辩的关键两步
数据就绪之后,进入系统的核心环节:给每条文本打情感分,同时把热点话题聚成类。这一章的两条路线选择会直接影响答辩时老师问你的深度。基础版本只用情感分析就能交差,但加上主题聚类,你的系统就有了“分析”的味道。
4.1 两条技术路线怎么选:SnowNLP 还是机器学习
常见做法是优先用 SnowNLP 这类基于词典和规则的工具,理由很实际:它不需要你准备标注好的训练集,开箱即用;而机器学习路线需要自己标注几千条数据,课程设计的时间根本不够。但答辩老师可能会追问“你的模型准确率多少”,所以更聪明的做法是:主体用 SnowNLP,再叠加一个 sklearn 的分类模型做对比实验。这样既保证能跑出结果,又有深入讨论的空间。
4.2 情感打分核心实现:词典规则 + 自定义扩展
SnowNLP 的默认情感词典偏向电商评论和新闻语料,对网络流行语和反问句的敏感度不够。实战中我会先做一次分数输出,再针对明显误判的语句补充自定义词典,效果立竿见影:
# analysis/sentiment.py from snownlp import SnowNLP def get_sentiment_score(text): """ 返回 0 到 1 之间的情感倾向分 分数接近 1 表示正向,接近 0 表示负向,0.5 左右为中性 """ try: s = SnowNLP(text) return s.sentiments except Exception as e: # 空文本或纯符号会触发异常,返回 0.5 表示中性 print(f"情感分析失败,默认返回中性分: {e}") return 0.5 # 批量处理 DataFrame 中的正文字段 import pandas as pd def batch_sentiment(df, text_col="content"): df["sentiment_score"] = df[text_col].apply(get_sentiment_score) # 按分数划分情感类别,方便后续统计 df["sentiment_label"] = df["sentiment_score"].apply( lambda x: "pos" if x >= 0.6 else ("neg" if x <= 0.4 else "neu") ) return df逻辑说明:apply方法把get_sentiment_score逐个作用到文本行上,返回一个新列sentiment_score。后面的sentiment_label把连续分数映射成“正向/负向/中性”三分类,这是答辩展示时最直观的维度。
参数说明:0.6和0.4这两个阈值可以调。如果你发现整体分数普遍偏高,可能是语料倾向问题,把正向阈值抬到0.7、负向降到0.3会平衡一点。另外注意 SnowNLP 对长文本会显著变慢,超过 500 字的文本建议先切片再聚合。
4.3 主题聚类:TF-IDF 向量化之后做 KMeans
情感分布只是表象,老师更想看到你能从海量舆情里找出“大家到底在吵什么”。用 TF-IDF 把文本转成向量,再用 KMeans 聚成若干主题,是一条低成本高回报的路径:
# analysis/topic_model.py from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import KMeans import jieba def jieba_tokenize(text): # 自定义分词,过滤单字和停用词 words = jieba.lcut(text) return " ".join([w for w in words if len(w) > 1]) def cluster_topics(docs, n_clusters=5, top_n=8): # docs 是清洗后的文本列表 tokenized = [jieba_tokenize(doc) for doc in docs] # max_features 控制特征维度,太大容易过拟合,太小丢信息 vectorizer = TfidfVectorizer(max_features=3000, stop_words="english") X = vectorizer.fit_transform(tokenized) km = KMeans(n_clusters=n_clusters, random_state=42, n_init=10) labels = km.fit_predict(X) # 每个聚类输出 top 关键词 terms = vectorizer.get_feature_names_out() topic_words = [] for i in range(n_clusters): center = km.cluster_centers_[i] top_indices = center.argsort()[::-1][:top_n] topic_words.append([terms[idx] for idx in top_indices]) return labels, topic_words逻辑说明:jieba_tokenize先分词再拼接成空格分隔的字符串,因为 TfidfVectorizer 默认按空格切分英文,只有把中文预先分词才能正确计算。fit_transform一步完成词频统计和 TF-IDF 权重计算。KMeans聚类的n_clusters决定分成几类主题,课程设计设 4 到 6 类比较合理,太多会让每个类里的文本太少,看不出主题。
参数说明:max_features=3000是经验值,语料里出现频率最低的词会被裁掉,既降噪又提速。n_init=10表示跑 10 次取最优,避免 KMeans 落在局部最优解上。random_state=42保证每次运行结果一致,答辩时不会出现这次聚类和上次聚不一样的情况。
提示:聚类结果如果出现大量“垃圾类”——里面的关键词全是无意义的通用词,说明清洗环节没删干净停用词。去
jieba_tokenize里加一个自定义停用词表,把“我们”“你们”“这个”“那个”全部过滤掉再跑一次,效果立刻改善。
5. 常见问题与排查:运行环境与数据质量的核心坑
这一章的内容全部来自真实的课程设计调试记录。每条都按“现象 → 原因 → 解决”来写,按出现频率排序,你可以直接照着对号入座。
5.1 Matplotlib 图表中文全部变成方框
- 现象:饼图和折线图里的中文标签渲染成一排方框,英文显示正常。
- 原因:Matplotlib 默认字体是 DejaVu Sans,不含中文字形,所以中文无法绘制。
- 解决:在绘图代码开头显式指定中文字体和负号处理:
import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "PingFang SC"] plt.rcParams["axes.unicode_minus"] = False5.2 Jieba 分词把专有名词切得七零八落
- 现象:“人工智能”被切成“人工”和“智能”,“ChatGPT”被切成“Chat”和“GPT”。
- 原因:默认词典里没有这些词,系统按通用规则切分。
- 解决:加载自定义词典,把课程设计里反复出现的关键词强制设成整词:
import jieba # add_word 的第二个参数是词频,给个大值让它在分词时优先合并 jieba.add_word("人工智能", freq=20000) jieba.add_word("ChatGPT") jieba.add_word("大数据")5.3 情感分析结果大量集中在 0.5 附近
- 现象:几千条文本的 sentiment_score 几乎都落在 0.45 到 0.55 之间,正向和负向区分不明显。
- 原因:SnowNLP 对口语化、网络化的文本不敏感,很多句子被判成中性;也可能清洗环节把语气词全部删掉导致情感信息丢失。
- 解决:先检查清洗正则是否把“不”“太”“很”这类程度副词删掉了,然后再做一轮自定义语料补充。另一个有效思路是放大文本长度阈值:把
get_sentiment_score里不足 30 字的短句不参与情感判断,因为这些短句在舆情数据里大多是标题或导航文本,名字没有情感意义。
5.4 爬虫第一次能跑,第二天被网站拒之门外
- 现象:前几天抓取正常,某天开始 requests 返回 403 或直接被重置连接。
- 原因:访问频率过高触发反爬策略,IP 被临时封禁;也可能是 User-Agent 列表没有更新,被识别为脚本。
- 解决:把
time.sleep的时间从 1 秒提高到 3 到 5 秒,并把轮换 UA 的逻辑改成随机从列表里抽取而不是顺序轮换。如果是课程设计答辩前最后一天才发现这个问题,最稳妥的处理方式是直接用源码包里的历史数据文件,别在演示现场跑爬虫。
5.5 SQLite 写入报错database is locked
- 现象:入库时报
sqlite3.OperationalError: database is locked,有时候重启程序又好了。 - 原因:有人在另一个窗口打开了数据库文件,SQLite 的写锁被占住。
- 解决:写入前设置超时时间,让等待锁的进程不要立即报错:
conn = sqlite3.connect("data/weibo.db", timeout=10)以上五条如果全部命中,说明你几乎没有按顺序调试。建议从第一条开始逐项核对,再决定改不改代码。很多时候,答辨现场翻车不是算法不够高级,而是这些基础环境问题没提前处理。
6. 进阶验证:给答辩加分的自查技巧
源码包里通常带着一份“系统说明文档”。但即使文档写得再多,演示时老师一问准确率,你支支吾吾说不上来,前面的印象分就会打折。这里教你两个低成本的自查技巧,在答辩前一天过一遍,能规避掉大部分质疑。
第一个技巧是用混淆矩阵验证情感分类的准确率。预留一百条已标注的样本,跑一遍批量情感分析,然后和人工标注对比:
from sklearn.metrics import confusion_matrix, classification_report # y_true 是人工标注类别,y_pred 是系统预测类别 # 两者都是 "pos"/"neg"/"neu" 字符串组成的列表 labels = ["pos", "neg", "neu"] cm = confusion_matrix(y_true, y_pred, labels=labels) report = classification_report(y_true, y_pred, labels=labels) print("混淆矩阵:") print(cm) print("分类报告:") print(report)逻辑说明:混淆矩阵的行是真实类别,列是预测类别,对角线上的数字就是判断正确的样本数。分类报告里重点关注macro avg那一行的 precision、recall 和 f1-score,这是老师最爱问的指标。如果 f1-score 低于 0.6,优先去扩充自定义词典,而不是换模型。
第二个技巧是画出舆情热度的时间趋势图。课程设计的舆情分析如果只有静态分布图,缺少时间维度,答辩时会被质疑“这不算趋势分析”。按日期聚合文本数量并画折线图,是很有效的补强:
import pandas as pd import matplotlib.pyplot as plt # df 里有 publish_date 列,先把日期转换成字符串格式 df["publish_date"] = pd.to_datetime(df["publish_date"]).dt.strftime("%Y-%m-%d") trend = df.groupby("publish_date").size() plt.figure(figsize=(10, 4)) trend.plot(kind="line", marker="o") plt.title("舆情热度时间趋势") plt.xlabel("日期") plt.ylabel("发文量") plt.grid(True, alpha=0.3) plt.tight_layout() plt.savefig("output/trend.png", dpi=150)逻辑说明:groupby("publish_date").size()按日期统计发文数量,再直接调用 pandas 内置的plot(kind="line")生成折线图。marker="o"让每个数据点显示为圆点,方便老师看清数值变化。savefig会同时把图片存到output目录,论文里可以直接引用。
从那以后,我每次做完课程设计都会强制走一遍这套验证流程:先跑混淆矩阵算准确率,再画时间趋势图查热度分布,如果发现统计结果和可视化对不上,先怀疑自己的清洗代码,而不是去“调”图表。这份源码包能少走很多弯路,但前提是别只盯着缺失的依赖不停重装,而是顺着数据流把每一层都验证一遍。希望帮到你。
本文还有配套的精品资源,点击获取