
简介面向人工智能课程设计与期末大作业场景这是一套基于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、Scrapyprocessor/或cleaner/去重、清洗、分词re、jieba、pandasanalysis/情感分析与主题建模SnowNLP、scikit-learnvisual/或templates/图表与前端展示pyecharts、matplotlib、EChartsdata/采集结果与中间数据CSV、SQLite、JSONapp.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 snownlp0.12.3。另外python -m venv venv里的第一个venv是模块名第二个venv是文件夹名可以改成env或myenv但记得把启动命令里的路径同步改掉。2.3 第一次运行用调试模式跑通主流程环境装好之后不要一上来就跑完整流程先确认入口文件能启动。以 Flask 类入口为例常见写法长这样# app.py 的启动入口部分 if __name__ __main__: # 关闭 debug 之前先确定代码能正常导入 app.run(host127.0.0.1, port5000, debugTrue)逻辑说明debugTrue能让代码修改后自动重启服务并且浏览器里能看到详细报错栈。课程设计阶段开着它方便排查问题但答辩演示时务必关掉否则停在调试器页面会很尴尬。参数说明host127.0.0.1表示只允许本机访问。如果需要让同一局域网的另一台电脑打开演示页面改成0.0.0.0即可port5000如果被占用换成 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, retry3): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: text/html,application/xhtmlxml, Referer: https://www.baidu.com/ } for attempt in range(retry): try: # timeout 必须设置否则请求卡死会阻塞整个流程 resp requests.get(url, headersheaders, timeout10) 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参数控制重试次数timeout10表示 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_pathdata/weibo.db): conn sqlite3.connect(db_path) # if_existsappend 表示追加写入避免重复采集时覆盖历史数据 df.to_sql(weibo_posts, conn, if_existsappend, indexFalse) conn.close() # 顺手输出入库条数方便和源数据量做核对 print(f已写入 {len(df)} 条记录到 {db_path})逻辑说明to_sql是 pandas 封装好的便捷方法它会自动根据 DataFrame 的列名建表。if_existsappend适合增量采集场景如果你的任务是全量重抓改成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_colcontent): 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_clusters5, top_n8): # docs 是清洗后的文本列表 tokenized [jieba_tokenize(doc) for doc in docs] # max_features 控制特征维度太大容易过拟合太小丢信息 vectorizer TfidfVectorizer(max_features3000, stop_wordsenglish) X vectorizer.fit_transform(tokenized) km KMeans(n_clustersn_clusters, random_state42, n_init10) 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_features3000是经验值语料里出现频率最低的词会被裁掉既降噪又提速。n_init10表示跑 10 次取最优避免 KMeans 落在局部最优解上。random_state42保证每次运行结果一致答辩时不会出现这次聚类和上次聚不一样的情况。提示聚类结果如果出现大量“垃圾类”——里面的关键词全是无意义的通用词说明清洗环节没删干净停用词。去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(人工智能, freq20000) 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, timeout10)以上五条如果全部命中说明你几乎没有按顺序调试。建议从第一条开始逐项核对再决定改不改代码。很多时候答辨现场翻车不是算法不够高级而是这些基础环境问题没提前处理。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, labelslabels) report classification_report(y_true, y_pred, labelslabels) 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(kindline, markero) plt.title(舆情热度时间趋势) plt.xlabel(日期) plt.ylabel(发文量) plt.grid(True, alpha0.3) plt.tight_layout() plt.savefig(output/trend.png, dpi150)逻辑说明groupby(publish_date).size()按日期统计发文数量再直接调用 pandas 内置的plot(kindline)生成折线图。markero让每个数据点显示为圆点方便老师看清数值变化。savefig会同时把图片存到output目录论文里可以直接引用。从那以后我每次做完课程设计都会强制走一遍这套验证流程先跑混淆矩阵算准确率再画时间趋势图查热度分布如果发现统计结果和可视化对不上先怀疑自己的清洗代码而不是去“调”图表。这份源码包能少走很多弯路但前提是别只盯着缺失的依赖不停重装而是顺着数据流把每一层都验证一遍。希望帮到你。本文还有配套的精品资源点击获取