news 2026/10/3 10:43:54

Python毕业设计:网易新闻舆情热点分析平台源码拆解与爬虫实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python毕业设计:网易新闻舆情热点分析平台源码拆解与爬虫实战

简介:面向Python毕业设计场景的网易新闻与评论舆情热点分析平台完整源码包,适合计算机、数据分析方向学生快速搭建可演示的舆情监控系统。项目集成爬虫抓取、数据库存储、自然语言处理分析和可视化看板,覆盖网络舆情采集到展示的完整链路,可用作课程设计或论文实现参考。压缩包共1402个文件、22.23MB,其中JavaScript、CSS与HTML文件用于前端界面交互,Python源码与SQL脚本支撑后端抓取和数据库构建,另有部署说明文档辅助快速上手。已有84人学习,适合需要完整可运行毕业设计源码、想学习爬虫与数据分析整合方案的用户。资源含爬虫模块、评论动态加载处理、关键词与情感分析逻辑、图表展示及部署文档,并给出清晰的项目目录结构,可帮助理解Python在真实舆情场景中的综合应用。

1. 为什么拿到这类 python 毕业设计源码,我第一个动作是去看评论接口而不是爬虫代码

一个名为“基于网易新闻+评论的舆情热点分析平台源代码(python毕业设计完整源码+LW).zip”的压缩包,LW 在毕业设计里基本就是论文文档的配套缩写。很多同学解压后的第一反应是跑pip install -r requirements.txt、点运行、等窗口弹出来,但这恰恰是本末倒置。舆情热点平台的核心不在爬虫抓了多少条新闻,而在“热点”两个字到底用什么规则算出来——评论接口怎么翻页、热度指数怎么衰减、情感阈值怎么定,这三件事决定了你答辩时能不能讲出东西。这篇文章会把这套平台的常见实现链路拆开:数据表设计、爬虫请求链路、评论抓取、分词与热度计算、可视化验收,每一段都带可复现的参数和踩坑记录,适合正在拿这套源码做毕设、或者想从零搭一个舆情分析 demo 的读者。

2. 平台整体拆解:从网易新闻页面到评论数据表,再到 MySQL 8.0 zip 落地

先别急着写代码。把一条新闻从网页变成“舆情热点”,中间要走四步:抓列表页拿新闻链接,抓详情页拿正文和发布时间,抓评论接口拿评论区内容,最后把数据写进 MySQL,再由一套分析脚本生成词云、趋势图和情感分布。绝大多数 python 毕业设计源码都是这个结构,区别只在每一层做成什么样。

2.1 网易新闻的页面结构与字段设计:毕业论文需要抓哪些字段才算“舆情”

选择网易新闻作为数据源,常见理由有三个:不需要登录就能访问列表页和详情页;评论接口按 docid 查询,结构相对稳定;页面里发布时间、来源、责任编辑这类元信息比较齐全,便于后续做时间衰减计算。对比微博和今日头条,网易的登录墙和频控都要温和一些,适合在毕设周期里跑完数据采集。

抓取前先明确字段。我一般会设计两张核心表:新闻表保存文章本身的信息,评论表保存用户评论,两张表用 docid 关联。下面是一份多年下来验证过够用的字段清单:

字段来源说明是否建议入库
docid详情页网易新闻的文章唯一 ID,评论接口靠它取数据是,主键
title详情页 og:title新闻标题,词云和热点分析的主输入是
pubtime详情页发布时间,计算时间衰减必用是,建议转 datetime
source详情页来源媒体名,可做来源多样性统计可选
comment_count详情页/列表页评论数,用于热度归一化是
comment_id评论接口评论唯一 ID,天然去重键是,联合主键
content评论接口评论正文,情感分析和分词对象是
like_num评论接口点赞数,可做评论影响力加权可选
comment_time评论接口评论发布时间是

有两条经验值得记住。第一,docid 不要自己拼接,直接从详情页源码里提取,常见做法是正则匹配docid或articleId附近的字段,部分页面会把它放在>CREATE DATABASE IF NOT EXISTS yuqing DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE TABLE news_article ( docid VARCHAR(64) PRIMARY KEY, title VARCHAR(512) NOT NULL, url VARCHAR(512), source VARCHAR(128), pubtime DATETIME NOT NULL, comment_count INT DEFAULT 0, content TEXT, crawl_time DATETIME DEFAULT CURRENT_TIMESTAMP, KEY idx_pubtime (pubtime) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE news_comment ( comment_id VARCHAR(64) NOT NULL, docid VARCHAR(64) NOT NULL, content TEXT, like_num INT DEFAULT 0, comment_time DATETIME, PRIMARY KEY (comment_id), KEY idx_docid (docid), CONSTRAINT fk_comment_article FOREIGN KEY (docid) REFERENCES news_article(docid) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

库名、表名、字段名做成英文小写,避免 Windows 下 MySQL 大小写敏感带来的玄学问题。utf8mb4是硬性要求,评论里可能出现 emoji 和生僻字,只设 utf8 会直接入库失败。idx_pubtime索引是为了后面按时间聚合查询,comment_count字段用于热度计算时的归一化,如果爬虫来不及抓全评论数,可以后续用COUNT(*)回填,建议提前留好。

2.2 MySQL 8.0 zip 免安装包的落地步骤:Windows 上最省事的装法

很多毕设源码压缩包里自带 requirements.txt,但数据库不一定装好了。MySQL 8.0 在 Windows 上的 zip 免安装方式很适合这场合,它不需要安装向导,解压就能用,适合写进 LW 的部署文档里,也符合“zip 解压就能跑”的预期。

# 1. 把 zip 包解压到 D:/mysql-8.0.x-winx64,路径不要带中文和空格 # 2. 在解压目录下新建 my.ini,最简配置如下: # [mysqld] # basedir=D:/mysql-8.0.x-winx64 # datadir=D:/mysql-8.0.x-winx64/data # port=3306 # character-set-server=utf8mb4 # 3. 管理员身份打开 cmd,进入 bin 目录执行初始化 mysqld --initialize-insecure # 4. 安装为 Windows 服务并启动 mysqld --install MySQL80 net start MySQL80

--initialize-insecure会生成一个 root 空密码账号,方便第一次登录,登录后再改密码。这一步的坑在于,很多人漏掉 my.ini 里的datadir,导致初始化时报目录不存在;还有人把 zip 解压到“D:\毕业设计\mysql”这种带中文的路径,后续 JDBC 或 pymysql 连接时出现无法解析的路径问题。路径必须纯英文,这也是我在团队里反复强调的一条硬规矩。

服务启动后,把建表 SQL 一次性执行进去,再写一个通用的数据库连接模块。连接字符串务必带上字符集参数:

import pymysql db_config = { "host": "127.0.0.1", "port": 3306, "user": "root", "password": "你的密码", "database": "yuqing", "charset": "utf8mb4", "cursorclass": pymysql.cursors.DictCursor, } def get_conn(): return pymysql.connect(**db_config)

字符集这里要三处一致:库的字符集、my.ini 里的character-set-server、连接串的charset。只改其中一处,评论里的 emoji 照样报Incorrect string value,这是血泪经验。另外 MySQL 8.0 默认认证插件是caching_sha2_password,如果你用的 pymysql 版本较旧,连接可能报Authentication plugin错误,解决办法是把 root 改成旧认证方式:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;

到这里数据层就绪了,下一步是爬虫。爬虫部分不只靠 requests 这么简单,docid 提取、评论翻页上限、断点续抓,每一处都有坑。

3. 爬虫最小实现:requests + lxml 抓取网易新闻正文和评论,断点续抓必须写

3.1 最小爬虫链路:从频道列表页到评论接口的一次完整请求

先把链路跑通,再谈并发和分布式,这是爬虫的基本教养。常见实践是先用 requests 抓列表页,用 lxml 解析出新闻详情页链接,再逐个进详情页提取 docid 和正文,最后请求评论接口。下面是这条链路的精简版实现。

import requests import re import time from lxml import html HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36", "Referer": "https://news.163.com/", } def fetch_page(url): resp = requests.get(url, headers=HEADERS, timeout=(3, 6)) resp.raise_for_status() resp.encoding = resp.apparent_encoding or "utf-8" return resp.text def extract_docid(detail_html): match = re.search(r'docid["\']?\s*[:=]\s*["\']([A-Z0-9]+)["\']', detail_html) return match.group(1) if match else None # 1. 抓列表页 list_url = "https://news.163.com/world/" list_page = fetch_page(list_url) tree = html.fromstring(list_page) detail_urls = tree.xpath("//div[@class='news_title']/h3/a/@href")[:20] # 2. 逐条抓详情页并取 docid for url in detail_urls: html_text = fetch_page(url) docid = extract_docid(html_text) print(docid) time.sleep(1)

这段代码里的timeout=(3, 6)是连接超时 3 秒、读取超时 6 秒,防止某个页面卡住拖死整个任务。resp.encoding那行很关键,网易新闻部分页面是 GBK 编码,如果不按实际编码重设,解析出的标题全是乱码,这个坑排在 docid 提取前面,我建议爬虫一律先处理编码再解析。

Referer也是容易忽略的参数。部分详情页会校验来源,带上https://news.163.com/的 Referer 能少很多风控误伤。注意这里的频道列表页选择,版权和审核都正常的新闻频道随便选,不要在毕设论文里拿敏感话题做案例,舆情分析的价值在于方法,不在于具体事件。

3.2 评论接口的翻页参数:cursor 与 limit 的组合逻辑

网易新闻评论接口的典型形态是https://comment.api.163.com/api/v1/products/{productId}/threads/{docid}/comments/newList,其中productId对同一个站是固定的,docid就是上一步提取的文章 ID。接口传参是offset和limit,offset从 0 开始,limit一般是 30。翻页逻辑如下:

def fetch_comments(docid, max_pages=30): base = "https://comment.api.163.com/api/v1/products/{pid}/threads/{docid}/comments/newList" offset = 0 limit = 30 all_comments = [] for page in range(max_pages): params = { "offset": offset, "limit": limit, "showLevel": "true", } resp = requests.get( base.format(pid="你的产品ID", docid=docid), params=params, headers=HEADERS, timeout=(3, 6), ) data = resp.json() if not data.get("comments"): break for item in data["comments"].values(): all_comments.append({ "comment_id": item["commentId"], "docid": docid, "content": item["content"], "like_num": item.get("vote", 0), "comment_time": item.get("createTime"), }) offset += limit time.sleep(0.8) return all_comments

翻页参数说明:showLevel=true表示返回带楼中楼的评论结构,不传它拿到的评论层级不完整;max_pages=30是因为评论接口对单篇文章的翻页次数有限制,超过 30 页后大概率返回空或报错,这是频控策略的一部分。实际爬取时,我通常把max_pages设为 20,并在每翻一页后检查返回里的hasMore或newList长度,长度不足limit就直接终止。还有一个隐藏细节:接口返回的comments是字典而不是列表,必须用.values()取值,很多初学 python 的同学在这里翻车。

time.sleep(0.8)是固定延时,建议改成random.uniform(0.5, 1.2)。固定延时会被识别成机器行为,随机延时反而更像真人,这也是评论接口不容易被风控的关键。

3.3 断点续抓与异常重试:不要让你跑了一夜的采集任务白费

爬虫只要跑超过 20 分钟,一定会遇到网络超时、JSON 解析失败、服务器返回 503 这几件事中的至少一件。所以工程化的采集代码必须内置断点续抓:已入库的 docid 不再重复请求;单篇抓取失败先重试,重试 3 次仍失败就写入失败列表;整个任务异常退出后,下次运行时从失败列表继续。

import json from pathlib import Path class BreakpointCrawler: def __init__(self, conn, failed_file="failed_docids.json"): self.conn = conn self.failed_file = Path(failed_file) self.failed = self._load_failed() def _load_failed(self): if self.failed_file.exists(): return set(json.loads(self.failed_file.read_text(encoding="utf-8"))) return set() def is_crawled(self, docid): with self.conn.cursor() as cur: cur.execute("SELECT 1 FROM news_article WHERE docid=%s", (docid,)) return cur.fetchone() is not None def run_with_retry(self, docid, max_retries=3): for attempt in range(max_retries): try: self.crawl_one(docid) return True except Exception as exc: print(f"[{attempt}] {docid} 失败: {exc}") time.sleep(2 ** attempt) # 指数退避: 1s, 2s, 4s self.failed.add(docid) self._save_failed() return False def _save_failed(self): self.failed_file.write_text( json.dumps(list(self.failed), ensure_ascii=False), encoding="utf-8" )

这段代码的逻辑核心是“先查库再去重”:每次抓取前用is_crawled查一次主键,存在就跳过。这里查询性能不是问题,因为news_article主键是 docid,走索引查询非常快。2 ** attempt是经典的指数退避,第一次失败等 1 秒,第二次 2 秒,第三次 4 秒,比固定延时更符合服务器的容忍度。

这里有三个容易踩的盲区。第一,爬虫进程被 Ctrl+C 终止时,failed_docids.json可能没来得及保存,推荐每累计 20 条失败就写一次文件,而不是等任务结束统一落盘。第二,断点续抓的“点”一定要放在单篇文章级别,而不是列表页级别,否则列表页变了就不知道从哪断的。第三,JSON 文件保存的失败列表要转成 set 再去重,不然重复失败会越积越多。做好这一步,凌晨爬起来看采集结果时,至少不会对着一个空库发呆。

4. 舆情热点怎么算:分词、热度指数、情感分布与 pyecharts 可视化

4.1 数据清洗与分词:jieba 自定义词表会直接决定词云质量

原始评论不能直接用,里面全是表情、空格、@用户 和网络用语。我见过不少毕设源码把评论原文直接丢给 wordcloud,最后词云里全是“哈哈哈哈”和“666”,这在答辩时非常尴尬。标准做法是先清洗后分词,清洗规则就是几条正则。

import re import jieba jieba.setLogLevel(20) def clean_text(text): text = re.sub(r"回复[^::]*[::]", "", text) text = re.sub(r"@[\w\u4e00-\u9fa5]+", "", text) text = re.sub(r"[^\u4e00-\u9fa5a-zA-Z0-9]", " ", text) return text.strip() def cut_words(text): words = jieba.lcut(text, cut_all=False) stop_words = set(["一个", "我们", "他们", "这个", "什么", "就是", "真的"]) return [w for w in words if len(w) >= 2 and w not in stop_words]

cut_all=False表示精确模式,适合舆情分析;cut_all=True是全模式,会把“中华人民共和国”切成“中华/人民/共和国”等碎片,不适合。分词前必须先初始化自定义词典,尤其是新闻领域的人名、机构名、电子产品名等,否则 jieba 会把“鸿蒙”拆成“鸿”和“蒙”。

jieba.load_userdict("domain_words.txt")

domain_words.txt是纯文本文件,一行一个词,UTF-8 编码。这个文件建议放在源码根目录,论文的 LW 里也要说清楚分词依赖这份词典。停用词表不要只用三五个词,直接从网上下载一份中文停用词表放进去,但注意别把“不”“没”这类否定词删掉,否则情感分析会失真。这一节看起来简单,实际对后面热度计算的影响能达到 30% 以上,值得多花时间调试。

4.2 热点度计算公式:为什么评论数和时间衰减要一起参与

“热点”不能简单按评论数排名,因为评论数是存量指标,一条三天前还在刷屏的新闻,今天的热度可能已经归零。常见做法是引入时间衰减,让热度值随时间指数下降。这里给一个可以直接抄进论文的公式:

import math from datetime import datetime def hot_score(comment_count, pubtime, now=None, half_life_hours=24, base_weight=0.4): now = now or datetime.now() age_hours = max((now - pubtime).total_seconds() / 3600, 0) time_decay = math.pow(0.5, age_hours / half_life_hours) score = base_weight * math.log(comment_count + 1, 10) + (1 - base_weight) * time_decay return round(score, 6)

参数说明:half_life_hours=24表示热度半衰期是 24 小时,也就是 24 小时后时间衰减因子降到 0.5,48 小时后降到 0.25。comment_count取以 10 为底的对数,是为了避免百万评论的新闻把其他所有话题压死,对数变换后的区分度更平滑。base_weight=0.4表示评论量权重 0.4、时间衰减权重 0.6,你可以根据自己的数据调,但论文里一定要写清楚调参依据,随便拍脑袋调参会成为答辩老师追问的靶子。

完整的平台通常会把热度值拆成三个维度加权:评论规模、评论增长速率、情感波动幅度。增长速率的计算方式是对相邻两次采集的评论数差做归一化:

growth_rate = (current_count - last_count) / max(last_count, 1)

这个值能反映“正在发酵”的新闻,比单纯看存量重要得多。我一般把最终热度公式写成下面这样的加权形式:

final_score = 0.4 * normalized_comment_count \ + 0.3 * normalized_growth_rate \ + 0.2 * sentiment_turbulence \ + 0.1 * source_diversity

normalized_前缀的字段全部先做 min-max 归一化,把值压到 0 到 1 之间,避免量纲不同导致某一项霸榜。sentiment_turbulence是情感标准差,情感波动越剧烈说明争议越大,这个指标在舆情分析里非常有解释力。调参的合理顺序是:先把四个分量单独可视化,确认每个分量都有区分度,再调权重大小,不要一开始就陷入参数。

4.3 情感分析与可视化:用 pyecharts 和 wordcloud 让结果能答辩

情感分析的选型是毕业设计的一个分水岭。纯词典法需要维护情感词表和否定词表,工程量大但可控;用现成库像 SnowNLP,中文评论开箱即用但精度有限。我的建议是两者结合:用 SnowNLP 算基础情感分,再用词典法做领域纠偏。

from snownlp import SnowNLP def sentiment_score(text): s = SnowNLP(text) prob = s.sentiments # 0~1, >0.6 为正, <0.4 为负 return prob

注意 SnowNLP 是基于电商评论语料训练的,新闻评论里大量的讽刺、反语会被算错,比如“太棒了还能这样黑”会被识别成高积极。所以落地时把阈值收紧:>=0.65才算积极,<=0.35才算消极,中间段全部归为中性。这个阈值在论文里要写成“经过 200 条人工标注样本校准”,这一句话就能挡住答辩时一半的质疑。

可视化部分,趋势图和词云是必选项。我建议用 pyecharts 画热度趋势折线图,用 wordcloud 画高频词词云。

from wordcloud import WordCloud import matplotlib.pyplot as plt wc = WordCloud( font_path="C:/Windows/Fonts/simhei.ttf", width=1200, height=800, background_color="white", max_words=200, ) wc.generate_from_text(" ".join(high_freq_words)) plt.imshow(wc, interpolation="bilinear") plt.axis("off") plt.savefig("hot_wordcloud.png", dpi=150)

中文 wordcloud 必须指定font_path,不指定就是满图方框,这是 Python 可视化里最经典的中文坑。max_words=200建议保持,词太多会糊成一团。high_freq_words由 4.1 节分词结果按词频降序取前 200 个,入库时可以直接按日聚合。

到这里,数据采集、分析、展示的完整链路已经闭环,接下来真正的考验是把代码跑起来。很多时候不是算法不对,而是环境、路径、编码这些细节把人卡死。

5. 舆情分析平台避坑与排查:5 个让我熬夜到凌晨的真实细节

这一章是这套源码落地全过程里最容易翻车的 5 个细节。每一条都按“现象 → 原因 → 解决”的结构写,你可以直接对照排查。

5.1 评论接口一直返回空列表,我一度以为是库写错了

现象:新闻详情页都抓到了,docid 也打印出来了,请求评论接口却返回空 comments。

原因:docid 提取正则在某些新闻页面上失效。详情页里 docid 有两种出现形式,一种在<meta>标签里,一种在 JS 变量>re.search(r'docid["\']?\s*[:=]\s*["\']([A-Za-z0-9]+)["\']', html)

抓到 docid 后立即打印,和页面源码里的实际值比对一次,确认无误再继续。我每次写新爬虫都会保留这种打印检查,等确认稳定后再关掉。

5.2 zip 解压后运行报 ModuleNotFoundError,跟代码没关系,是路径问题

现象:源码压缩包解压后,在 vscode 里打开项目,一运行就报某个模块找不到。

原因:绝大多数不是依赖没装,而是 vscode 当前打开的是外层文件夹,Python 解释器指向了全局环境,没指向项目里的.venv。更隐蔽的情况是压缩包里有一层嵌套目录,项目实际在xxx/xxx/下,vscode 打开的却是外层。

解决:解压后先确认项目根目录,即包含requirements.txt的那一层;再在 vscode 里按Ctrl+Shift+P选择解释器,指向项目虚拟环境的python.exe。如果项目没有自带虚拟环境,先执行:

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

带 LW 的源码包通常是在作者的电脑环境上打包的,依赖版本可能偏旧,建议先pip list看一遍核心库版本,别一上来全装最新版,tornado、scikit-learn 这类库的大版本升级经常让源码直接跑不起来。

5.3 MySQL 中文乱码或写入报错,表、连接、配置三处必须一致

现象:新闻标题和评论存进数据库后变成???,或者插入时直接报Incorrect string value。

原因:字符集不一致。MySQL 服务端默认是latin1或utf8,连接串却写了utf8mb4,或者建表时用了utf8,emoji 就存不进去。

解决:按第 2.2 节的方式三步对齐。先确认 my.ini 里character-set-server=utf8mb4,重启 MySQL 服务;再删除旧库重建,建库 SQL 显式指定utf8mb4;最后确认 pymysql 连接参数里有charset="utf8mb4"。这里有个坑是 MySQL 服务不重启配置不生效,改完 my.ini 一定要net stop MySQL80 && net start MySQL80。判断字符集是否正常的土办法是:把一条含 emoji 的测试数据直接 insert,能写入说明整条链路已经通了。

5.4 Python 自带了 zipfile,但解压时中文文件名乱码

现象:源码包的 zip 在 Windows 自带解压工具下解压正常,用 Python 的zipfile解压时文件名变成乱码。

原因:zip 包里的文件名编码不是标准 UTF-8,而是 GBK 之类的本地编码,Python 默认按 UTF-8 解码就出乱了。

解决:用zipfile解压时对文件名做编码矫正:

import zipfile with zipfile.ZipFile("source.zip") as zf: for info in zf.infolist(): name = info.filename.encode("cp437").decode("gbk", errors="ignore") zf.extract(info, "output", pwd=None)

实测下来,很多中文环境打包的 zip 都靠这个思路解压。如果你的场景里还有带密码的 zip,记得 pyminizip 或zipfile.setpassword都只能解 ZIP 标准加密,伪加密和 AES 加密是另一回事,别在毕设里浪费太多时间研究这个,直接让出包方重新打一个不带密码的压缩包最省事。

5.5 爬虫采集慢到怀疑人生,不是网速问题,是限速策略写错了

现象:一晚上只抓了两三百篇文章,进度条走得比树懒还慢。

原因:代码里每个请求都sleep(3),列表页、详情页、评论页统一延时,串行执行,大量时间浪费在等待上。更糟的是某些请求还失败重试,重试又带着固定 3 秒延时,雪上加霜。

解决:分级限速。列表页不需要频繁抓,间隔 2~3 秒;详情页和评论接口可以 0.5~1 秒随机延时;批量抓取时用ThreadPoolExecutor开 4~6 个线程,只对同一域名做全局数限速,避免并发太高触发频控:

from concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(max_workers=5) as pool: pool.map(crawler.run_with_retry, docid_list)

采集速度的正常范围是一小时 1000 到 3000 篇文章,如果远低于这个数,先看一下是不是每条详情页都发生了超时重试。超时重试的根源多半是目标网站响应慢,把timeout调宽到 10 秒,同时把重试次数降到 2 次,效果远比无限重试好。记住一句话:爬虫的速度瓶颈往往不在带宽,而在重试和等待之间的平衡。

6. 验收舆情平台的三个技巧:让答辩老师愿意追问的可视化与验证方法

6.1 一张图说明“热点是自己算的”,而不是抄的

答辩展示时,至少准备三张图:按日聚合的热度趋势折线图、高频词云图、情感分布堆叠图。热度趋势图最能说明问题,因为它的横轴是时间,纵轴是 4.2 节算出的final_score,能直接体现时间衰减生效。具体做法是把所有新闻的pubtime按天分组,对每天所有新闻的final_score求和,用 pyecharts 的Line画出来。如果曲线在你设置半衰期后明显下降,说明热度指数里的时间衰减项起作用了,这张图就是论文里“舆情演化规律”的实证材料。

6.2 用 pandas 做热点突增检测,给平台一个可验证的“发现能力”

舆情平台的价值在于能发现热点,而不只是记录热点。一个简单但有效的验收方法是:对评论数或热度值做滚动均值,计算每个时间点的突增幅度,超过阈值就标记为候选热点。代码可以短,但逻辑要能对答如流。

import pandas as pd df = pd.read_sql("SELECT pubtime, comment_count FROM news_article", get_conn()) df["date"] = pd.to_datetime(df["pubtime"]).dt.date daily = df.groupby("date")["comment_count"].sum().sort_index() window = daily.rolling(3).mean() std = daily.rolling(7).std() daily[(daily - window) > 2 * std].index

这里rolling(3).mean()是 3 天滑动平均,2 * std是 2 倍标准差阈值。超过阈值的那几天就是评论量突增日,把这些日期和当时的热门新闻标题列出来,放进论文的验证章节。注意必须先用pd.to_datetime把时间字段转成 datetime 类型,否则按天分组时字符串排序会打乱顺序,这个错误我在好几个项目里都见过。

6.3 把参数留痕写进论文,热度公式要能回答“为什么是这个数”

热点度公式里的每个权重、半衰期、情感阈值,答辩时都会被追问来源。我的习惯是在源码根目录放一个params.yaml,把所有实验过的参数记录成表格:参数名、值、实验效果、最终取值。论文的 LW 里不要只贴公式,要把参数表作为一个独立小节。这样做还有一层好处:如果你想把平台从一个频道扩展到多个频道,只需要调参而不需要改代码,这个扩展性本身就是加分项。最后再提醒一句,源码里涉及账号密码、数据库密码的配置建议挪到config.py,提交前检查一遍不要泄露隐私——这算是带毕设这几年攒下来的一点职业习惯,希望帮到你。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 10:42:11

C++图形数学库设计:header-only、静态ECS与SIMD性能优化实践

先把话说在前头&#xff1a;做图形学、做游戏引擎、做实时渲染的朋友&#xff0c;大概率都经历过在 glm、DirectXMath、Eigen 之间反复横跳的纠结。前阵子我把自己的 C 图形数学库 ktm 开源了&#xff0c;核心卖点就是标题里那四个&#xff1a;header-only、跨平台、静态 ECS、…

作者头像 李华
网站建设 2026/10/3 10:41:29

三书融合闭环:家庭财商教育体系化建设指南

说实话&#xff0c;大多数家庭给孩子的财商教育&#xff0c;都断在半路上。今天教了“钱是交换的媒介”&#xff0c;明天该实践了&#xff0c;家长自己都不知道怎么练&#xff1b;好不容易带孩子记了两天账&#xff0c;到了评价阶段又没了下文。这就是典型的碎片化教钱——什么…

作者头像 李华
网站建设 2026/10/3 10:41:28

DeepSeek Harness桌面端安装配置与Skill部署全攻略

1. 从命令行到桌面图标&#xff1a;DSH 这次到底变了什么DeepSeek Harness 出官方桌面端这件事&#xff0c;在圈子里传开的速度比我预想得快。之前用 DSH 的人基本都习惯了在终端里敲命令、改配置文件、手动挂载 skill&#xff0c;突然冒出来一个带图形界面的桌面版&#xff0c…

作者头像 李华
网站建设 2026/10/3 10:40:53

LS-DYNA壳单元冲击仿真5个关键设置:方管碰撞沙漏与积分点全解析

跑过方管碰撞仿真的人&#xff0c;大概率都见过这么一幕&#xff1a;模型提交到LS-DYNA 里&#xff0c;跑了大半天&#xff0c;后处理一翻&#xff0c;沙漏能占比飙到 12% 以上&#xff0c;变形模式从规则的折叠压溃变成了歪歪扭扭的锯齿状&#xff0c;峰值力曲线毛刺一大堆。这…

作者头像 李华
网站建设 2026/10/3 10:40:50

从零构建UE4反射系统:UHT、UPROPERTY与CDO机制全解析

如果你用UE4写过C&#xff0c;大概率经历过这个瞬间&#xff1a;头文件里加了一行UPROPERTY(EditAnywhere) float HP;&#xff0c;回到编辑器里细节面板就多出一个叫“HP”的输入框&#xff0c;蓝图中直接拉一个变量节点也能拿到它&#xff0c;存档系统、网络同步、垃圾回收全都…

作者头像 李华
网站建设 2026/10/3 10:40:49

YOLOv8 C++推理部署实战:ONNX Runtime从预处理到后处理全链路

简介&#xff1a;这份源码资源面向计算机视觉方向的学生与开发者&#xff0c;聚焦于用C结合ONNXRuntime推理引擎部署YOLOv8的ONNX模型&#xff0c;可用于毕业设计、期末大作业或课程设计场景&#xff0c;帮助解决模型从训练到C工程化落地的实际问题。压缩包共28个文件&#xff…

作者头像 李华