news 2026/10/1 4:55:48

微博舆情分析系统:Python数据采集清洗、情感分析与可视化全流程实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微博舆情分析系统:Python数据采集清洗、情感分析与可视化全流程实战

简介:基于Python微博舆情分析系统的毕业设计项目,面向计算机、通信、人工智能、自动化等专业学生及从业者,可作为毕设、课程大作业或期末项目参考。整套项目包含完整源码与论文文档,代码经调试可运行,整体围绕微博数据采集、舆情分析与可视化等模块展开,体现实际工程化设计思路。压缩包共60个文件,以Python源码、HTML页面、XML配置及Excel数据表为主,另有论文doc文档与说明文件,包体约44.27MB,目录结构清晰,便于按模块学习或二次开发。目前已有277人学习,高答辩评分使其具有较强参考价值,既能帮助入门者理解完整项目流程,也能为进阶改造提供可扩展基础。

1. 微博舆情分析系统:不是爬虫加画图,而是一条完整数据流水线

微博舆情分析系统在Python毕业设计里是常青题目。但多数交上来的成品是爬虫脚本加几张图表,数据没清洗、情感阈值靠拍脑袋,论文实验一截图就露馅。这套源码和论文文档的价值,是把“采集-清洗-分析-可视化-验证”整条链路跑通,而不是单点脚本。 它针对一个关键词抓取微博正文、时间、转评赞,做jieba分词、SnowNLP情感打分、TF-IDF热词、LDA主题聚类,再用Flask+ECharts输出趋势折线、情感饼图、热词词云组成的舆情仪表盘;论文文档附了实验章节写法,适合直接复现。 适合正在做Python期末大作业或毕业设计的人,也适合借真实数据学python数据分析与可视化的新手。

2. 系统架构与采集层:用requests抓JSON接口,为什么不用Scrapy

拿到这套项目先看目录,代码按 data/、spider/、analysis/、web/ 分层,这是毕设答辩最好讲的部分。整体是三层:采集层抓原始数据,分析层把文本转成指标,展示层把指标画成图表,论文里的系统架构图基本同构,评审老师一眼就能看懂数据流向。

采集层是整条流水线的地基。选型原则很直接:能用 requests 直接拿 JSON 接口,就不要上 Scrapy。Scrapy 在毕设环境里容易因为 Twisted 版本、系统依赖问题折腾一整天,而微博移动端搜索接口返回的是结构化 JSON,字段干净,还省去了网页解析这一步。

2.1 采集方案:requests + 微博移动端搜索接口

网页版微博搜索是动态渲染,直接 requests 拿不到列表数据,硬上 Selenium 又慢又容易被识别。但 m.weibo.cn 的容器搜索接口直接返回 JSON,字段里自带 text、reposts_count、comments_count,做舆情分析需要的原始信息全都有,这是这套源码爬虫主模块用的方案。

import requests import time def fetch_weibo(keyword: str, page: int = 1) -> dict: url = "https://m.weibo.cn/api/container/getIndex" params = { "containerid": f"100103type=1&q={keyword}", "page_type": "searchall", "page": page, } headers = { "User-Agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15", "Referer": f"https://m.weibo.cn/search?containerid=100103type%3D1%26q%3D{keyword}", "X-Requested-With": "XMLHttpRequest", "Cookie": "换成自己的Cookie", } resp = requests.get(url, params=params, headers=headers, timeout=10) resp.raise_for_status() return resp.json()

这段代码有几个容易忽略的点。containerid里的100103type=1&q=是微博搜索流的固定格式,type=1 表示综合搜索,q 后面跟关键词;page_type=searchall表示取搜索结果全部卡片而不是单条微博详情,这个参数写错会直接返回空列表。Cookie 从源码里默认留空能跑前几页,但多翻几页后会被频控,我习惯从浏览器登录态拷一份到配置文件里,不写死在代码中——换网络环境时不用改 py 文件,只改配置。

返回值里真正要取的是data下的cards字段,每一条 card 可能是单条微博,也可能是分组卡片。解析时得先判断卡片类型,避免把分组卡片当成微博数据结构去取字段。下一节就把解析和入库合在一起。

2.2 数据解析与入库:SQLite建表、id去重、增量抓取

建表时我直接用微博 id 作主键,它天然唯一,重复抓取时用INSERT OR IGNORE就能去掉重复行,省掉一次查询判断。

-- 主键直接用微博id字符串,天然去重 CREATE TABLE IF NOT EXISTS weibo_data ( mid TEXT PRIMARY KEY, created_at TEXT NOT NULL, user_name TEXT, text TEXT, reposts_count INTEGER DEFAULT 0, comments_count INTEGER DEFAULT 0, attitudes_count INTEGER DEFAULT 0, source TEXT ); -- created_at上的索引,给按天分组趋势图用 CREATE INDEX idx_weibo_created ON weibo_data(created_at);

created_at上的索引很关键,数据量到几千条后,按天分组的GROUP BY substr查询不会卡顿。解析入库函数单独写,方便爬虫主循环调用:

import sqlite3 def parse_and_save(cards: list, db_path: str = "weibo.db"): conn = sqlite3.connect(db_path) cur = conn.cursor() for card in cards: if card.get("card_type") != 9: continue mblog = card.get("mblog", {}) mid = mblog.get("mid") text = mblog.get("text", "") created_at = mblog.get("created_at", "") if not mid: continue cur.execute( "INSERT OR IGNORE INTO weibo_data(mid, created_at, user_name, text, reposts_count, comments_count, attitudes_count, source) VALUES(?,?,?,?,?,?,?,?)", (mid, created_at, mblog.get("user", {}).get("screen_name", ""), text, mblog.get("reposts_count", 0), mblog.get("comments_count", 0), mblog.get("attitudes_count", 0), mblog.get("source", ""))) conn.commit() conn.close()

card_type等于 9 才是真正的单条微博,11 是分组卡片。如果不做类型判断,直接取card["mblog"]会在分组卡片上报 KeyError,这是解析层最常见的崩溃点。INSERT OR IGNORE是去重核心,重复抓同一页不会产生重复行,这也让系统天然支持增量更新:每次只翻最新几页,旧数据自动被忽略。

需要注意,微博接口返回的created_at是“刚刚”“5分钟前”这类相对时间。如果直接入库,后面按天做趋势图时日期全是错的。常见做法是写一个时间转换函数,把中文单位映射成绝对时间:

from datetime import datetime, timedelta def parse_time(raw: str) -> str: now = datetime.now() if "刚刚" in raw: return now.strftime("%Y-%m-%d %H:%M:%S") if "分钟前" in raw: minutes = int(raw.replace("分钟前", "")) return (now - timedelta(minutes=minutes)).strftime("%Y-%m-%d %H:%M:%S") if "小时前" in raw: hours = int(raw.replace("小时前", "")) return (now - timedelta(hours=hours)).strftime("%Y-%m-%d %H:%M:%S") if "昨天" in raw: return (now - timedelta(days=1)).strftime("%Y-%m-%d %H:%M:%S") return raw

这段处理虽然基础,但直接影响后面所有时间聚合逻辑。从“刚刚”到“昨天”覆盖了微博搜索页面最常见的相对时间跨度,更早的时间接口会返回“2023-06-01”这种绝对字符串,直接落库即可。

2.3 Cookie与限速:从被识别到连续抓取不中断

微博接口没有 Cookie 时能返回前几页数据,但到第三四页就会出现 status=0、data 为空,这不是代码坏了,是被后台标记了。我的处理原则有三条:第一,用移动端接口降低被识别的概率;第二,每次请求之间 sleep 随机 2-5 秒;第三,失败时指数退避,连续失败就停 10 分钟再继续。

import random import time def safe_fetch(keyword: str, max_page: int = 10): result_cards = [] for page in range(1, max_page + 1): try: data = fetch_weibo(keyword, page=page) cards = data.get("data", {}).get("cards", []) if not cards: print(f"page {page} 返回空,停止") break result_cards.extend(cards) time.sleep(random.uniform(2, 5)) except requests.HTTPError as e: if e.response.status_code == 418: print("被标记了,休息60秒") time.sleep(60) continue raise return result_cards

random.uniform(2,5)比固定 sleep 3 秒更像个真实用户,固定时间间隔反而容易被访问模式识别。418 是微博返回的趣味状态码,实际含义是拒绝服务,出现时说明 IP 或 Cookie 已经被识别为脚本,继续硬翻只会加大被拦的概率。Cookie 失效的表现不是直接报错,而是前两页正常、第三页开始空数据,所以safe_fetch里空 cards 就 break 很重要,别硬着头皮翻下去。爬虫层 90% 的翻车都发生在频率和 Cookie 上,解析反而次之,这套项目跑下来我对这点体会特别深。

3. 文本挖掘层实现:分词、情感打分与主题聚类的配合

数据落库后进入分析层。这套源码用了三个工具:jieba 做分词和关键词提取,SnowNLP 做情感打分,gensim 做 LDA 主题聚类,全部是纯 Python 库,没有引入深度学习模型。毕设答辩现场能打开电脑就跑,比配置 CUDA 和 PyTorch 可靠得多。分析层的执行顺序是固定的:先清洗分词,再情感打分,最后做热词和主题,顺序反了结果会互相污染。

3.1 清洗与分词:正则去噪、自定义词典、停用词表

微博文本里带 @ 用户、URL、表情符号、话题#词#,直接拿去分词会得到一堆噪声。清洗顺序固定为:去掉 HTML 标签、去掉 URL、去掉 @ 用户名、保留话题内容、压缩空白。

import re def clean_text(raw: str) -> str: raw = re.sub(r"<[^>]+>", "", raw) # 去掉微博接口里的HTML标签 raw = re.sub(r"https?://\S+", "", raw) # 去掉URL raw = re.sub(r"@[\w\-]+", "", raw) # 去掉@用户名 raw = re.sub(r"#(.+?)#", r"\1", raw) # 保留话题词本身 raw = re.sub(r"\s+", " ", raw).strip() return raw

保留话题词是因为舆情实体往往藏在 #考研上岸# 这类标签里,直接删会把核心词丢掉。空白压缩是为了防止分词把换行符当成一个独立词。清洗后接 jieba 分词:

import jieba import jieba.analyse jieba.load_userdict("words.txt") stopwords = set() with open("stopwords.txt", encoding="utf-8") as f: for line in f: stopwords.add(line.strip()) def segment(text: str) -> list: words = jieba.lcut(text) return [w for w in words if w.strip() and w not in stopwords and len(w) > 1]

words.txt里放微博流行词,比如“yyds”“破防”“躺平”,一行一个。jieba 默认词表是新闻语料,这些网络词默认会被切碎,加载自定义词典后分词质量立刻提升。len(w) > 1是为了过滤单字碎片,把“的”“了”“是”这类尾部噪声去掉。停用词表直接影响热词统计,把“我们”“真的”“一个”加进去,TF-IDF 的结果就不会被废话占满。但停用词别加太多,我最初抄了一份超大停用词表,结果“芯片”“疫苗”这种核心词也被滤掉了,后来换回通用表才正常。

3.2 情感分析:SnowNLP打分、阈值设定、表情先验

SnowNLP 的sentiments属性返回一个 0 到 1 的浮点数,分数越接近 1 越积极。它的训练语料是电商评论,对微博里的反讽、网络梗判断不理想。但这套项目做了一个很实际的处理:不追求单条文本判准,而是把大量文本聚合后看分布。舆情分析关注的本来就是整体偏向和变化趋势,单条误判会被均值抵消一部分。

打分函数我写成这样:

from snownlp import SnowNLP EMOJI_MAP = { "[怒]": 0.1, "[泪]": 0.2, "[伤心]": 0.15, "[哈哈]": 0.9, "[笑]": 0.85, "[赞]": 0.95, } def sentiment_score(text: str) -> float: for emoji, score in EMOJI_MAP.items(): if emoji in text: return score * 0.6 + 0.2 return SnowNLP(text).sentiments

EMOJI_MAP是纯先验规则:文本里出现明确情绪符号时,不依赖模型,直接给一个基准分并叠加偏移。比如“哈哈”在 SnowNLP 里很可能只判成 0.55 的中性,但它实际是强积极信号;映射后至少 0.74,区分度一下就出来了。阈值方面,我不建议用默认的 0.5 一刀切。微博里“还行”“一般”这类模糊态度特别多,我一般用 0.6 作为积极阈值、0.4 作为消极阈值,中间段归为中性,这样情感饼图会多出“中性”一类,分析价值更大。SnowNLP 输出的分数分布非常集中,大量中性文本落在 0.45 到 0.55 之间,按 0.5 切等于把模型没把握的噪声当成明确情感。

3.3 热词与主题聚类:TF-IDF关键词提取、LDA主题模型

热词是舆情报告里的高频需求。最朴素的做法是统计词频,但“微博”“转发”这种无信息量词会排在最前面,所以用 jieba 内置的 TF-IDF 算法,按权重挑出真正能代表这批文本的词。

all_words = [] for text in cleaned_texts: all_words.extend(segment(text)) tfidf_words = jieba.analyse.extract_tags(" ".join(all_words), topK=20, withWeight=True) print(tfidf_words[:10])

topK=20表示取前 20 个词,毕设展示这个数量足够;withWeight=True会返回每个词的 TF-IDF 权重,做词云时wordcloud的generate_from_frequencies可以直接吃这个格式,权重映射成字号。LDA 主题聚类是拉开分数的地方,情感分析只能回答“正面还是负面”,导师追问“这批舆论到底在吵什么”时,靠主题聚类来回答。gensim 的 LDA 流程固定四步:分词列表、构建词典、转词袋、训练模型。

from gensim import corpora, models seg_list = [segment(clean_text(t)) for t in raw_texts] dictionary = corpora.Dictionary(seg_list) corpus = [dictionary.doc2bow(text) for text in seg_list] lda = models.LdaModel(corpus, num_topics=5, id2word=dictionary, passes=20) for i, topic in lda.print_topics(num_words=6): print(f"主题{i+1}: {topic}")

num_topics=5是经验值。微博文本很短,单条不超过两三百字,主题数设太多每个主题只剩两三个词,人工无法命名。passes=20表示整个语料迭代 20 次,这个数据量下已经收敛,再大只是拉长训练时间。一个常见误区是直接把原始字符串传给 LdaModel,它接收的是词袋向量,必须先用dictionary.doc2bow转换。跑出主题后,把每个主题的前 6 个词填进论文的“主题分布实验”表,人工命名成“产品吐槽”“政策讨论”“段子玩梗”,实验章节就有了实打实的模型输出。

4. 可视化与报告:python数据分析与可视化怎么落地成舆情仪表盘

分析层产出的是指标,直接给导师看列表没有说服力。这套项目用 Flask 做后端接口、ECharts 做前端图表,整体是一个轻量仪表盘。选 Flask 而不是 Django,原因很简单:这个场景不需要 Django 的 Admin 和 ORM 全家桶,Flask 的路由装饰器就能覆盖所有接口需求,部署也轻。为什么强调仪表盘而不是 Excel 表格?舆情分析的本质是比较,比较不同时间段的情感走向、对比关键词之间的热度差距。表格能让人看到数,但要快速传递“趋势在变差”这个结论,折线图比表格直观得多。这也是评审老师在答辩现场最常盯的部分——前端有没有、图表能不能交互。

4.1 Flask接口设计:把分析结果整理成JSON

仪表盘只需要三个接口:趋势接口按天返回微博数量和情感均值,情感分布接口返回积极/中性/消极条数,热词接口返回 TF-IDF 权重。我统一写在一个views.py里,每个接口对应一个 SQL 查询。

from flask import Flask, jsonify import sqlite3 app = Flask(__name__) DB_PATH = "weibo.db" @app.route("/api/trend") def api_trend(): conn = sqlite3.connect(DB_PATH) rows = conn.execute( "SELECT substr(created_at,1,10) AS day, COUNT(*) FROM weibo_data GROUP BY day ORDER BY day" ).fetchall() conn.close() return jsonify([{"day": r[0], "count": r[1]} for r in rows])

substr(created_at,1,10)是这里的关键:入库时间统一转成YYYY-MM-DD HH:MM:SS后,按天聚合只需要截前 10 个字符,不需要再调 date 函数。GROUP BY day里的 day 是列别名,SQLite 支持按别名分组。这个接口返回的 JSON 数组可以直接喂给 ECharts 的 xAxis 和 series。注意 Flask 默认会把 JSON 里的中文转成\uXXXX形式,浏览器能正常解析,但如果你要把接口返回结果写进文件,记得先设置app.json.ensure_ascii = False。

4.2 ECharts仪表盘:趋势折线、情感饼图、热词表格

前端就一个index.html,引入 ECharts CDN,页面布局是顶部折线、左下饼图、右下热词。折线图用 fetch 调用/api/trend,核心配置节选:

fetch('/api/trend') .then(r => r.json()) .then(data => { const chart = echarts.init(document.getElementById('trend')); chart.setOption({ xAxis: { type: 'category', data: data.map(d => d.day) }, yAxis: { type: 'value' }, series: [{ type: 'line', data: data.map(d => d.count), smooth: true }] }); });

smooth: true让折线变成平滑曲线,视觉上更接近舆情趋势的波动感。category 轴必须给 data 数组,否则只显示裸坐标轴。饼图用/api/sentiment返回的三分类计数,ECharts 只需要在 series 里写type: 'pie',data 是格式为[{name: '积极', value: 335}, {name: '中性', value: 421}, {name: '消极', value: 102}]的数组,图表会自动计算占比。热词这块我实际传的是带权重的结果,用 echarts-wordcloud 插件按 weight 映射字号,视觉效果比条形表强,但需要额外下载一个 JS 插件。论文截图建议用词云,答辩现场演示用表格——表格不依赖字体渲染,在投影仪上不会出现词云乱码。另外,CDN 在答辩现场没网时会翻车,这套源码的静态目录里放了一份本地 echarts.min.js,演示前一定要检查这个文件在不在。

4.3 定时任务:APScheduler让系统自动增量更新

毕设答辩有一个加分项:系统不是手动跑一次的脚本,而是能自动更新的服务。APScheduler 是 Python 里最轻的定时方案,用 BlockingScheduler,每天固定执行一次增量抓取。

from apscheduler.schedulers.blocking import BlockingScheduler from datetime import datetime def job(): cards = safe_fetch("芯片", max_page=5) parse_and_save(cards) print(f"增量更新完成,共{len(cards)}条新数据") scheduler = BlockingScheduler() scheduler.add_job(job, 'interval', hours=24, next_run_time=datetime.now()) scheduler.start()

hours=24表示固定间隔一天跑一次;next_run_time=datetime.now()让任务启动后立即先跑一遍,否则第一批数据要等到第二天才有。APScheduler 也有 cron 触发器,可以精确到每天凌晨 1 点,但演示场景里 interval 足够,还不受系统时区影响。这个模块让系统从一次性脚本变成了自动持续运行的服务,论文里写“系统具备定时增量采集能力”时,对应的就是这段代码。

5. 避坑常见问题:爬虫封禁、数据清洗、编码异常的四次翻车记录

复现过程中真正耗时的地方,不是算法,而是这些不起眼的坑。我按现象、原因、解决方式整理四个最高发的问题,每一条都对应一次实际翻车。

5.1 现象:爬十几页后接口返回正常但cards为空

前面几页数据正常,大概 30 分钟后,fetch_weibo依然返回 HTTP 200,JSON 里的 status 也是 1,但 data.cards 是空列表。此时再继续翻页,就会遇到 HTTP 418。

原因是微博对低频 Cookie 做了频控,返回的是“软封禁”成功响应——它不直接拒绝你,而是给你一个没有内容的成功结果,让请求方误以为关键词没有更多数据。

解决方式是在safe_fetch里把空 cards 视为终止信号,并在连续两次空返回后强制 sleep 60 秒再试。源码里默认的 Cookie 只是一个起步值,要替换成自己的浏览器 Cookie 才能顺利抓完 10 页。关键点在于:不要在同一秒内发多个请求,移动端接口对频率的容忍度比网页版高,但也不是无限高。

5.2 现象:导出CSV用Excel打开全乱码,Notepad打开正常

这个翻车最容易在写报告时出现。用open("result.csv", "w", encoding="utf-8")写的文件是 Python 默认的无 BOM UTF-8,Excel 默认按 GBK 或 UTF-8-SIG 解读,读不到 BOM 就按 ANSI 解析,中文全变成乱码。

解决方式有两种。第一种最省事:写文件时用encoding="utf-8-sig",Python 会自动在文件头写入 BOM,Excel 就能正确识别。第二种是导出时用 pandas 的to_excel,但要额外引入 pandas 和 openpyxl,依赖变重,我不推荐。这个坑在 Linux 下完全看不出来,Vim 和 VS Code 都按 UTF-8 读,一拷到 Windows 演示就瞬间露馅。从那以后我所有 Python 项目里导出 CSV 都固定用utf-8-sig参数,成了肌肉记忆。

5.3 现象:情感分析结果全在0.45~0.55之间,饼图三块面积几乎均分

如果你看到情感饼图积极和消极各占一半,先别急着分析“舆论撕裂”,这大概率是模型失灵。SnowNLP 基于朴素贝叶斯分类器,对没有明显情感词的文本会输出接近 0.5 的分数,而微博文体短、口语化、表情符号多,恰好最容易落到这个不确定区间。

解决方式是把阈值从 0.5 改成 0.6/0.4,中间段单独归为中性;同时按前面的表情先验方法,把带明确情绪符号的微博从“不确定”组里拉出来。如果愿意折腾,可以下载基于微博语料微调的模型,但这条路线对毕设来说性价比不高——加了表情映射后,情感分布已经能看出区分度。记住一个原则:舆情分析里“中性”不是一个失败状态,它和积极、消极一样是有效分类结果。

5.4 现象:分词和热词里大量出现“转发微博”“分享图片”

抓取结果里很多条的 text 字段内容是“转发微博”或者空白,这些数据进到分词和情感分析后,会制造大量无意义记录,还会稀释情感比例的准确性。

原因是微博的转发微博在接口里 text 可能为空字符串,或者只显示“转发微博”这种占位文本,真实内容存放在retweeted_status对象里。

解决方式是在parse_and_save里加回退逻辑:text 为空或等于“转发微博”时,去取mblog["retweeted_status"]["text"]作为内容;如果转发内容里有历史文本,就把retweeted_status.text取出来后清洗入库。还有一种情况是转发的微博只有图片没有文字,取不到文本时我选择丢弃这条,因为分析层处理空文本没有意义。真实舆情数据里转发占比特别高,不做这层清洗,热词榜顶部会被“转发微博”四个字占据,论文里的热词结果就完全没法看。

6. 验证方法与进阶技巧:用50条人工标注和混淆矩阵证明系统不是黑匣子

毕设论文里“实验与分析”章节最容易被导师追问的点,就是你凭什么说情感分析准。空口说“模型知名所以准”过不了答辩。低成本的做法是:人工标注 50 条,计算宏平均 F1。这套源码里附了verify.py,就是这个用途。

6.1 人工标注50条:小样本也能证明流程闭合

从数据库随机抽 50 条文本,自己打标:1 表示消极,0 表示非消极。抽样用ORDER BY RANDOM() LIMIT 50,尽量覆盖带表情的、转发的、只有话题的多种文本形态,导出到 CSV 人工标注后合回程序。

import sqlite3 conn = sqlite3.connect("weibo.db") rows = conn.execute("SELECT text FROM weibo_data ORDER BY RANDOM() LIMIT 50").fetchall() with open("sample_for_label.csv", "w", encoding="utf-8-sig") as f: for r in rows: f.write(f'"{r[0]}",\n')

这里用utf-8-sig写本身就是上一节避坑习惯的应用,标注表发到 Windows 上打开不会乱码。50 条不是一个完美评测集,人工标注的目的只是让实验章节有可数字化的结果。50 条样本下 F1 的置信区间大约在 ±0.14,在毕设语境里足够支撑结论。

6.2 混淆矩阵与F1计算:别只报准确率

准确率在舆情场景里有欺骗性。假设 100 条里只有 10 条消极,模型全部判积极,准确率是 90%,但消极召回率是 0。所以要用 sklearn 的混淆矩阵和分类报告,尤其盯住消极类别的召回率。

from sklearn.metrics import confusion_matrix, classification_report y_true = [...] # 人工标注结果 y_pred = [...] # 系统打分按阈值0.4映射成1或0 print(confusion_matrix(y_true, y_pred)) print(classification_report(y_true, y_pred, target_names=["非消极", "消极"]))

target_names必须和标签顺序对应,confusion_matrix 默认按类别升序排列,0 在前 1 在后,所以["非消极", "消极"]才是正确对应。拿到 F1 值后,把这三行输出直接贴到论文里作为实验数据,比任何文字描述都有说服力。

从那以后,我每次把微博舆情分析项目交出去之前,都强制先跑一遍这 50 条标注集,确认 F1 不低于 0.7。这个习惯救过我一次:加了retweeted_status回退逻辑后 F1 反而掉了 0.08,排查发现是拼接转发文本后长文本覆盖了短文本、情感被稀释,改成优先取原始转发内容才恢复回来。流程虽然小,但它让系统从“能跑”变成了“结果可说清”,希望帮到你。

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

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

车载以太网转换板开发实录:百兆正常千兆CRC错误的排查与修复

前阵子给车载以太网测试平台做一块转换板&#xff0c;需求看起来特别简单&#xff1a;能把车上的 100BASE-T1/1000BASE-T1 转到标准以太网&#xff0c;输出端百兆、千兆可手动切换。真正动手之后才发现&#xff0c;“可切换”这三个字几乎每一个字都是坑。板子跑起来之后&#…

作者头像 李华
网站建设 2026/10/1 4:55:31

软件工程实战复习:从需求到测试的全链路建模

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/1 4:55:22

AI日报系统:日粒度热词捕获与结构化摘要工程实践

1. 项目概述&#xff1a;这不是一份“新闻简报”&#xff0c;而是一套可复用的AI日更内容生产系统你点开这个标题&#xff0c;第一反应可能是——又一个信息过载时代的碎片化产物&#xff1f;但作为连续三年每天产出结构化AI领域动态内容的从业者&#xff0c;我得说清楚&#x…

作者头像 李华
网站建设 2026/10/1 4:54:48

AIGC图片编辑实战指南:局部重绘、ControlNet与批量工作流全解析

AIGC系列的图片编辑篇拖到现在才写&#xff0c;不是没东西写&#xff0c;是内容多到不知道从哪下刀。上一篇聊完文生图的基本功&#xff0c;这次把图片编辑这条线彻底捋一遍&#xff1a;局部重绘、画布扩展、ControlNet精准控制、老照片修复、AI写真、批量工作流&#xff0c;每…

作者头像 李华
网站建设 2026/10/1 4:54:44

如何从GitHub Trending挖掘优质开源项目?关注这三个新方向

今天早上照例把 GitHub 日榜趋势速报刷了一遍&#xff0c;今天的列表比上周有意思不少。Trending 页面本身没什么魔法&#xff0c;它只是把 Star 增长最快的仓库按天排列&#xff0c;但看久了你会发现&#xff0c;它其实是开源圈注意力的晴雨表&#xff1a;某个概念突然冒头、某…

作者头像 李华
网站建设 2026/10/1 4:54:31

树状数组+区间贪心+并行归约:库存批处理性能优化实战

execution并行归约、区间贪心、树状数组&#xff0c;这三样东西单独拿出来都不算冷门&#xff0c;但放在同一个项目里&#xff0c;意味着你遇到的一定是那种“数据量很大、执行窗口很短”的批处理场景。我最近在重构一个库存调度服务时&#xff0c;把这三者硬生生凑到了一起&am…

作者头像 李华