☰
舆情热点分析毕设源码:网易新闻评论爬虫与Python可视化平台全解析
2026/10/3 2:55:50 网站建设 项目流程

简介:基于网易新闻与评论构建的舆情热点分析平台完整源码,适合Python学习者完成毕业设计或课程设计,覆盖爬虫抓取、MySQL存储、NLP文本分析和可视化展示全流程。资源共1402个文件,压缩包22.23MB,其中js文件多达1019个,配合95个css、29个html及6个scss构成前端交互界面,80个gif与38个png等提供图表图标素材;30个pyc、25个py为系统核心逻辑,sql为建库脚本与初始数据,另有txt部署说明、docx文档等,便于快速运行和二次开发。平台包含动态加载评论抓取策略、关键词抽取、情感倾向分析和主题分类算法,并以趋势线、热点图等形式直观展示舆情变化。已有84人参与学习。通过本包可获得一套可运行的舆情分析系统,并理解动态评论加载处理、数据库设计、关键词抽取与情感倾向分析等关键环节,为毕业设计或课设提供完整参考。

1. 舆情热点分析毕设:这套源码解决什么问题?

如果你正在找一份能直接跑起来的毕业设计源码,基于网易新闻+评论的舆情热点分析平台源代码(python毕业设计完整源码+LW).zip大概率是你在搜索框里见过很多次的名字。它要解决的是毕设里最实际的一串需求:抓取网易新闻的资讯和用户评论,用 Python 对评论文本做分词、情感计算和热点排序,最后在网页上展示什么话题正在发酵。这个题目不要求高深算法,但把爬虫、数据库、NLP 和前后端串了起来,刚好满足本科毕设对一个“完整系统”的验收标准。

适合三类人:第一是代码基础一般、想在短时间内拿出一套可演示项目的学生;第二是想把文本挖掘流程复用去微博、知乎、新闻客户端评论区的同学;第三是准备用这个方向做课程设计、想搞清每个模块边界的人。如果你已经能熟练写爬虫,这套源码给你的价值更多在数据分析与展示的衔接方式。下面按我拿到这类项目后实际会走的排查和改造顺序来讲。

2. 舆情平台的数据链路:爬虫、存储与热度计算怎么拆

2.1 网易新闻和评论的抓取策略:先找接口再看页面

常见做法是抓两个东西:新闻标题和该新闻下的热门评论。新闻列表可以直接用 requests 拉一个频道页面再交给 BeautifulSoup 解析;评论则要去找网易新闻前端的评论接口,一般会通过页面上某个 docid 拼出 JSON 地址。直接看源码时,先用这两段代码理解抓取思路:

import requests from bs4 import BeautifulSoup HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" } def fetch_news_list(channel="tech", pages=2): """抓取网易新闻某个频道的标题和链接。 channel: 频道名,常见的有 tech、society、auto、money pages: 抓取前几页,调试时先写 1 """ news_items = [] for page in range(1, pages + 1): url = ( "https://news.163.com/special/00011K6L/" f"{channel}_rank{page}.html" ) resp = requests.get(url, headers=HEADERS, timeout=10) resp.encoding = "utf-8" soup = BeautifulSoup(resp.text, "html.parser") for li in soup.select(".newsList li a"): title = li.get_text(strip=True) href = li.get("href", "") if title and href: news_items.append({"title": title, "url": href}) return news_items

这里选择页面解析而不是官方接口,是因为频道列表页的 HTML 结构相对稳定,而且不需要签名参数,适合第一次跑通。header 里必须带浏览器 UA,否则很容易被服务器拒绝。resp.encoding = "utf-8"这句容易被忽略,网易页面里有多处编码声明,加上它至少能让中文标题不乱码。频道参数可以在源码 config 里配置,别把所有频道抓一遍,毕设数据量不是越大越好,够分析就行。

评论部分比新闻页麻烦。拿到的新闻 URL 形如https://www.163.com/dy/article/JK2P3ABC0519QIK0.html,要从里面取出docid,然后调用评论接口:

def fetch_comments(news_url, limit=30): # docid 一般是 URL 里最后一个不带后缀的文件名 docid = news_url.split("/")[-1].replace(".html", "") comment_api = ( "https://comment.news.163.com/api/v1/products/" "a2869674571f77b5a0867c3d71db5856" f"/threads/{docid}/comments/newList?limit={limit}&offset=0" ) resp = requests.get(comment_api, headers=HEADERS, timeout=10) payload = resp.json() comments = [] # 评论列表一般在 payload["comments"],有的版本叫 data.comments for item in payload.get("comments", []) or []: comments.append({ "user_id": item.get("user", {}).get("userId", ""), "content": item.get("content", ""), "like_count": item.get("vote", 0), "created_at": item.get("createTime", "") }) return comments

这段代码的容错比新闻页更重要。payload.get("comments", []) or []是为了防止评论接口返回空键或者被反爬时返回{"code": 1},代码不至于直接抛 KeyError。limit参数一般最多 30 或 50,调大反而可能触发风控。评论接口的 product id 和域名会随网易前端改版而变化,如果跑不出数据,第一件事不是怀疑反爬,而是打开浏览器开发者工具的 Network 面板,在评论加载请求里复制真实接口地址替换进去。

2.2 存储层设计:两张表和一个时间索引

爬下来的数据要落库。毕设平台最常见的存储方式是 MySQL,不是 MongoDB。原因是答辩时需要讲清楚关系结构,MySQL 的教务老师都认。表可以精简为两张:新闻表和评论表,必要时加一张关键词热度日统计表。下面是初始化脚本:

CREATE DATABASE IF NOT EXISTS yq_news DEFAULT CHARSET utf8mb4; USE yq_news; CREATE TABLE news ( news_id VARCHAR(64) PRIMARY KEY, title VARCHAR(255) NOT NULL, url VARCHAR(500), channel VARCHAR(50), publish_time DATETIME, comment_count INT DEFAULT 0, crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ENGINE=InnoDB; CREATE TABLE comments ( comment_id BIGINT AUTO_INCREMENT PRIMARY KEY, news_id VARCHAR(64) NOT NULL, content TEXT NOT NULL, like_count INT DEFAULT 0, created_at DATETIME, sentiment TINYINT DEFAULT 0, crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, KEY idx_news_time (news_id, created_at) ) ENGINE=InnoDB;

news_id直接用新闻 URL 里的 docid,天然唯一,不需要另外生成自增主键。comments表里的sentiment字段用来存情感分析结果,-1 负面、0 中性、1 正面,分析任务在抓取完成后批量回填。idx_news_time (news_id, created_at)这个联合索引是热点时间窗口查询的关键;没有它,当评论量到几万条时,按天分组的 SQL 会明显变慢。所有字符字段统一utf8mb4,特别是content,因为网易评论里经常出现 Emoji 和特殊符号,用utf8mb3会在插入时报Incorrect string value错误。

写入逻辑可以放到一个db.py里统一管理。常见做法是爬虫只负责取数据,分析进程负责回填情感,两个进程共用同一个连接配置。批量插入时用executemany,避免一条条INSERT把 CPU 和数据库连接都打满。时间字段要存成 DATETIME,而不是用VARCHAR存原始字符串,否则后面做按小时聚合时会发现转换格式能把人逼疯。

2.3 热点计算引擎:从分词到热度分值

舆情热点不能只看新闻标题,因为标题数量少、用词讲究,真正体现“民意”的是评论中反复出现的关键词。所以热点分析引擎的第一步是把评论拼成全文,交给 jieba 做关键词提取:

import jieba.analyse def extract_hot_words(comments_text, top_k=20): # allowPOS 只保留名词、动词和地名,过滤“真的”“觉得”这类副词 words = jieba.analyse.extract_tags( comments_text, topK=top_k, withWeight=True, allowPOS=("n", "ns", "nt", "nz", "v") ) return [{"word": w, "weight": float(wt)} for w, wt in words]

extract_tags默认算法是 TF-IDF,源码里也可以换 TextRank。两者差别很直接:TF-IDF 看重词在当篇文本里的稀有度,适合找一条新闻下的焦点词;TextRank 看重词与词之间的共现关系,适合跨多条新闻做主题词。毕设建议默认 TF-IDF,因为它的结果对答辩更直观,每个词能给出一个权重,画词云时直接拿权重当 size。

光有词频还不够,平台要有“热度值”。常见做法是把声量、情感偏转和时间衰减合起来:

hot_score = log(comment_count + 1) * sentiment_bias * recency
  • comment_count是参与讨论的评论绝对数量,取对数抑制头部效应;
  • sentiment_bias是正面评论占比和负面评论占比之差的绝对值,争议性话题的偏转会更明显;
  • recency是时间衰减系数,例如1 / (1 + 0.1 * hours_from_last_comment)。

这段公式不一定在原始源码里完全一致,但整体思路是通用的。代码实现时,把每个热点词关联到它出现的新闻,汇总新闻的评论数和情感值,计算出分值后写入热点统计表。你会发现单纯按词频排序时,“什么”“这个”一类虚词会冲进 Top20,allowPOS参数把这部分压掉了;如果分析的是“某平台回应”这类新闻,地名和机构名权重高,可以再叠加一个自定义停用词表,把“网易”“新闻”“资讯”这类词排除。

3. 用 Flask + ECharts 把舆情结果变成可视化平台

3.1 Web 框架与前后端交互的最小实现

热点平台的前端不需要重。常见做法是 Flask 提供 JSON 接口,页面用 ECharts 渲染,省去 Vue/React 的构建步骤。对毕业设计来说,维护成本低,答辩时现场改参数也方便。app.py 里最核心的两个接口是热词统计和情感分布:

from flask import Flask, jsonify, request from db import query_hot_words, query_sentiment app = Flask(__name__) @app.route("/api/hot_words", methods=["GET"]) def hot_words(): days = int(request.args.get("days", 1)) top = int(request.args.get("top", 30)) rows = query_hot_words(days=days, top=top) return jsonify({"code": 0, "data": rows}) @app.route("/api/sentiment", methods=["GET"]) def sentiment(): days = int(request.args.get("days", 7)) rows = query_sentiment(days=days) return jsonify({"code": 0, "data": rows}) if __name__ == "__main__": app.run(host="127.0.0.1", port=5000, debug=True)

接口参数和业务需求是对齐的:days控制时间窗口,top控制词云展示数量。这里不把时间窗口写死在 SQL 里,是因为舆情趋势对比需要反复调整;比如看某条突发新闻,就要把窗口从 7 天缩到 6 小时。debug=True在开发期能热重载,但若爬虫和分析模块同样在跑,重启用 debug 可能会导致重复任务,建议联调时改成debug=False。

3.2 热词云、情感占比和趋势曲线的数据接口

页面端用 ECharts 5 渲染词云和情感饼图。词云不是 ECharts 核心包自带的功能,需要引入echarts-wordcloud扩展,这点在源码的 HTML 里一般会写清楚。核心代码是拿到/api/hot_words后把数据映射到series:

<div id="wordCloud" style="height:420px;width:100%;"></div> <script src="https://cdn.jsdelivr.net/npm/echarts@5/dist/echarts.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/echarts-wordcloud@2/dist/echarts-wordcloud.min.js"></script> <script> fetch('/api/hot_words?days=1&top=30') .then(r => r.json()) .then(res => { const chart = echarts.init(document.getElementById('wordCloud')); chart.setOption({ series: [{ type: 'wordCloud', shape: 'circle', width: '90%', height: '90%', data: res.data.map(d => ({name: d.word, value: d.weight})), textStyle: { color: () => `hsl(${Math.floor(Math.random() * 360)}, 70%, 50%)` } }] }); }); </script>

width和height用百分比字符串而不是像素数字,是为了适应不同答辩屏幕。词云里颜色随机生成足够应付演示;如果想让视觉更统一,可以按权重区间给深浅色。情感占比接口返回的是positive / neutral / negative三类条数,前端画一个饼图,把占比最高的类放到第一位,因为答辩时评委扫一眼就能说出“这个平台主要抓到了多少负面情绪”。

趋势曲线需要另一个时间粒度接口,常见实现是按小时聚合评论量,再把热点词频叠加上去。实现方式不复杂,SQL 里用DATE_FORMAT(created_at, '%Y-%m-%d %H:00:00')做 GROUP BY。时间跨度大时,聚合出来的点非常多,接口里要限制返回点数量,或者在 SQL 里按%Y-%m-%d聚合。这个边界要提前想好,否则演示时直接把 30 天数据全查出来,前端会卡。

3.3 定时更新与增量抓取:调度参数怎么调

平台要体现“实时”,不能只跑一次数据。常见做法是给爬虫和分析任务加上定时调度,使用 APScheduler 比在 Flask 里开线程稳定得多:

from apscheduler.schedulers.blocking import BlockingScheduler def crawl_job(): from crawl import crawl_channels crawl_channels(["tech", "society"], pages=1) def analyze_job(): from analyze import run_analysis run_analysis(days=1) scheduler = BlockingScheduler() # 每 30 分钟抓一次,避免请求太频繁 scheduler.add_job(crawl_job, "interval", minutes=30) # 每天凌晨 2 点做一次全量情感回填 scheduler.add_job(analyze_job, "cron", hour=2, minute=0) if __name__ == "__main__": scheduler.start()

interval和cron的选择是这台“平台”最值得调的参数。新闻更新频度高,用 interval 30 分钟;情感分析是全量任务,放凌晨跑。抓取间隔不要太短,网易对评论接口的访问频率比较敏感,1 分钟内连续请求超过几十次就可能返回空 JSON。如果源码自带调度器但参数写成了minutes=1,建议改成 15 到 30,保数据量也保账号。增量抓取不能只靠调度,入库前要用news_id做INSERT IGNORE或先 SELECT 判重,否则第二次启动会把同一批新闻重复写入,热点统计就会失真。

4. 带你跑通这份 python 毕业设计源码:安装、配置与启动

4.1 解压、创建虚拟环境和安装依赖

拿到 zip 后,第一步不要急着双击看代码,先把它解压到全英文路径。很多 Python 库在中文用户名路径下会出现诡异的ModuleNotFoundError,到时候很难排查。解压命令在 Linux/macOS 下是这样:

mkdir -p ~/work/yq_project unzip "基于网易新闻+评论的舆情热点分析平台源代码(python毕业设计完整源码+LW).zip" -d ~/work/yq_project cd ~/work/yq_project python3 -m venv venv source venv/bin/activate pip install --upgrade pip pip install -r requirements.txt

Windows 下直接右键“全部解压”,或使用tar -xf也能解 zip。然后创建虚拟环境:

python -m venv venv venv\Scripts\activate pip install -r requirements.txt

Python 版本优先选 3.9 或 3.10。3.11 之后部分旧版 jieba 和 lxml 的二进制 wheel 可能装不上;3.8 又对pymysql和Flask新版本支持不够完整。如果不确定机器上有没有 Python,先在命令行跑python --version,看到版本号再继续。requirements.txt里通常有requests、beautifulsoup4、flask、pymysql、jieba、snownlp、apscheduler,这些包都不需要编译。若某一步提示“Microsoft Visual C++ 14.0 is required”,说明当前 Python 版本太新或太旧,换成 3.9 往往直接消掉。

4.2 数据库初始化和配置文件修改

需要把数据库连接改成你自己的。一般项目里会有一个config.py或.env,没有的话就新建一个config.py:

DB_HOST = "localhost" DB_PORT = 3306 DB_USER = "root" DB_PASSWORD = "your_password" DB_NAME = "yq_news" DB_CHARSET = "utf8mb4" CRAWL_CHANNELS = ["tech", "society"] CRAWL_PAGES = 2 CRAWL_INTERVAL_MINUTES = 30

用 2.2 小节的建表脚本创建数据库和表。如果你安装的是 MySQL 8.0 zip 包,不是安装器,记得初始化路径不能含中文,并在 my.ini 里配置character-set-server=utf8mb4。连接测试可以这样跑:

import pymysql conn = pymysql.connect( host="localhost", port=3306, user="root", password="your_password", database="yq_news", charset="utf8mb4" ) with conn.cursor() as cur: cur.execute("SELECT COUNT(*) FROM news") print(cur.fetchone()[0]) conn.close()

如果输出 0,表是空的,正常;如果报Unknown database 'yq_news',说明建表脚本没执行成功,回 2.2 节复制完整 SQL 到 Navicat 或命令行执行。很多源码会把 SQL 放在sql/init.sql里,运行方式也在 LW 文档里写了,优先用项目自己的脚本,避免字段名对不上。

4.3 爬虫、分析和 Web 服务的启动顺序与验证

跑通一次的最小顺序是:先爬新闻,再爬评论,然后做热点分析,最后启动 Web。三个任务分多个模块,根目录常见有crawl.py、analyze.py、app.py。逐个启动:

python crawl.py --channels tech --pages 1 python analyze.py --days 1 python app.py

如果源码入口不认--channels,那就直接改成python crawl.py,以文件中__main__的逻辑为准。爬虫跑完,验证数据有没有进去:

mysql -u root -p yq_news -e "SELECT COUNT(*) FROM news; SELECT COUNT(*) FROM comments;"

评论条数至少要几百条,热点分析才有意义。如果 comments 是 0,多半是 docid 解析逻辑失效,回到 2.1 节看接口。Web 启动后浏览器打开http://127.0.0.1:5000,能看到首页、词云和趋势图就说明基本通了。FLask 的默认端口可能被占用,改成app.run(port=5001)再试。不要跳过验证直接改功能,先拿到一份能跑的数据,后续所有改造都在“有基线”的前提下做。

5. 避坑:网易评论爬虫和毕设答辩里最常见的 5 个翻车现场

5.1 解压、环境与密码问题

现象:拿到基于网易新闻+评论的舆情热点分析平台源代码(python毕业设计完整源码+LW).zip后,用自带解压工具提示“文件损坏”或突然要密码,实际上并没有设置密码。原因:很多分享的 zip 包被二次压缩时开启了 zip 伪加密,也就是标记了加密位但没真正加密,解压工具读取到标志位就要求输密码。解决:不要以为文件坏了,用 7-Zip 打开,如果 7-Zip 能直接看到内容,说明只是伪加密;也可以写一小段 Python 用zipfile去读,如果能读出文件列表,基本可以确认是伪加密。把解压后的文件夹移动到C:\work\yq_project这种纯英文路径,再继续安装依赖。

另一个高发问题:pip install -r requirements.txt时报lxml或gevent需要编译。原因往往是 Python 版本太高,3.12 的官方 wheel 不全。解决:装 Python 3.9.13,重新创建虚拟环境,再用清华镜像源安装:pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple。顺带一提,用 VSCode 调试这类项目时,右下角选择解释器必须选到刚才创建的venv,否则装了包还是报No module named flask。

5.2 反爬、空评论与乱码

现象:评论爬虫能爬新闻列表,但所有的comments表都是零条;偶尔跑出几千条后突然全部为空。原因:评论接口不是稳定不变的,网易前端改版会换域名和 product id;另外连续高频请求到了阈值后,接口不再返回comments,而是返回{"code": 1}或直接 403。解决:抓包看当前真实接口,或者降频:在requests.get后加time.sleep(2);抓包发现接口变了就把 URL 改成新版。不要一上来就上高并发,评论接口的限频比新闻列表严格得多。

现象:新闻标题和评论内容写入 MySQL 后显示成问号或乱码。原因:建库时字符集不是utf8mb4,或者pymysql.connect少了charset="utf8mb4"。解决:把库、表、连接三处字符集统一;已经乱码的数据删掉重爬。另外,评论接口返回的 JSON 里经常有\u003d这类转义字符,解析后用json.loads会自动还原,不需要手写替换逻辑,手写反而会把本来正常的内容换成乱码。

5.3 热点分析结果不可解释与 LW 对不上

现象:词云 Top10 里全是“觉得”“一个”“真的”这类词,看上去完全不像热点。原因:jieba 提取时没过滤词性,也没加停用词表。解决:把allowPOS限制为名词、动词、地名,再叠加一个不少于 30 个词的停用词表。别只在代码里改,要把停用词表存成文本文件,源码里一般叫stopwords.txt,在答辩时可以现场展示排查过程,这是加分项。

现象:LW 文档里的系统截图和你跑出来的页面不一致,比如截图有“舆情预警”功能,代码里没有。原因:这类源码包里的 LW 常常是从另一套项目复制的,文档和代码对不上。解决:答辩前把 LW 里提到的所有功能逐项对照代码,没有的要么补一个小实现,要么在文档里删除。千万不要当着评委的面说“源码里没有这个功能”,会很被动。我的习惯是先跑一个冒烟测试:启动爬虫抓一条新闻,点开详情看评论是否在页面上展示,情感标签有没有写回数据库,这三个动作都通过,再开始改功能。

6. 让源码更像一个“平台”:批量评估、增量更新和效果验证的方法

到了最后一个阶段,代码能跑,但想让它更有竞争力。我的建议是给热点分析加一个“突发词检测”模块,原理很简单:比较某个词在当前时间窗口的词频和它在过去一周的平均词频,计算突发比例。用代码实现:

def burst_score(word, current_window_docs, history_window_docs, alpha=2.0): cur = count_word_freq(word, current_window_docs) hist = max(count_word_freq(word, history_window_docs), 1) return (cur + 1) / (hist + 1), cur > alpha * hist

alpha默认 2.0,意思是当前频率超过历史平均的两倍就标记为突发热词。这个指标比单纯 TF-IDF 更贴近“热点”的定义,而且答辩时你可以展示某条新闻在 6 小时内突然冲上来的过程。另一个值得做的事是人工标注验证情感分析:随机抽 100 条评论,手工标成正面/负面,和 SnowNLP 的结果做对比,算一个准确率。我当年做类似项目时就是没做这个,答辩时被评委一句“你的情感分析准吗”问住了。先跑一天数据,抽几条新闻比对词云和评论原文,如果发现词云里的词点开看不到对应评论,再检查关键词提取和评论的关联逻辑。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询