简介:基于Python的微博舆情数据爬取与情感分析可视化系统,面向毕业设计、课程实践及爬虫入门学习者。系统覆盖数据采集、情感倾向分析与可视化展示完整链路,包含爬虫模块、自然语言处理单元与交互式图形界面,代码分模块组织并配有详细注释。资源包共175个文件,约3.85MB,以19个py脚本为程序主体,配合14个html及css/js文件构成前端界面,csv/sql存放采集结果,jpg/png记录界面效果,另有pyc、zbak等辅助文件,结构清晰便于按模块阅读。数据可视化组件支持情感分布图谱、话题演化趋势及热点传播路径展示,部署过程已标准化,可快速完成环境配置与系统初始化。已有119人浏览学习,适合作为计算机专业毕业设计参考或文本挖掘综合实践案例,从爬虫到分析再到界面呈现,各环节均有对应实现,便于对照源码掌握完整项目思路。
1. 一条热搜到一张图:这套舆情分析系统到底解决什么问题
基于Python的微博舆情数据爬取与情感分析可视化系统,概念不复杂,真正跑上半年还能持续输出结论的却不多。多数人第一次做舆情项目,卡住的往往就是三件事:微博数据能不能稳定拿回来、情感判断到底可不可信、图表画完能不能讲清楚发生了什么。这篇按一条完整落地路径展开,先解决数据获取,再解决情绪判断,最后把结果变成一块能说明问题的看板。适合做品牌口碑监测、事件趋势研究或舆情研判参考的人,不求量大面全,但求可控、可改、可复现。
下面所有做法都以最小可运行系统为前提,不引入重型框架。数据量在几万条级别时,这套方案的维护成本低到可以忽略。如果你正在被“爬虫写好了但情感分析不准”之类的问题卡住,直接照着后面章节逐段排查就好。
2. 技术栈选型:先想清楚这三个模块,再写第一行代码
2.1 数据源选择:官方开放平台不是默认答案,m.weibo.cn 接口成本最低
写舆情爬虫,第一反应是申请微博官方开放平台接口。实际做过一轮就会发现,搜索类接口的申请审核周期长,权限分批开放,等着审批下来,一个热点事件已经过了发酵期。更现实的起点是微博移动端网页接口,也就是 m.weibo.cn。它在浏览器里访问搜索页就可以抓包看到,返回的 JSON 结构清晰,没有 OAuth 签名流程,维护一个登录后的 Cookie 就能持续获取搜索结果。
常见的数据采集路径有三种,我在实际项目里做过对比:
| 方案 | 获取难度 | 数据完整度 | 适合场景 |
|---|---|---|---|
| 官方开放平台 API | 高,需认证审核 | 高,字段规范 | 长期正式项目,预算充足 |
| m.weibo.cn 网页接口 | 低,抓包即可 | 中,含正文与互动数 | 个人研究与原型验证 |
| 第三方舆情数据服务 | 收费,依赖供应商报价 | 不等 | 不差钱、急于出数 |
个人和小团队先用 m.weibo.cn 网页接口跑通全流程,数据量大了、需求明确了再考虑官方 API 或第三方服务。选择它的理由不止是门槛低,还因为响应体是标准 JSON,字段直接对应微博正文、发布时间、转发评论点赞数,解析成本极低。
搜索接口的关键参数是 containerid,常见格式是100103type=1&q=关键词。type=1 综合搜索,type=2 正文搜索,type=3 用户搜索。新手最容易踩的坑是直接在 URL 里拼中文,请求经常返回乱码或空结果。正确做法是把参数交给 requests 的 params,它会按 URL 编码规则自动处理中文。
2.2 情感分析选型:先用 SnowNLP 跑基线,不要一上来就微调大模型
情感分析是这套系统的核心,选型时容易走极端。一种只用规则词典,遇到反讽和上下文转折直接失效;另一种直接上 BERT 微调,结果标注语料没有、GPU 也没有,模型根本训不起来。折中的、也是大多数个人项目最合理的起点,是 SnowNLP。
SnowNLP 内置的情感分析基于朴素贝叶斯,训练语料来自电商购物评论,它把一个句子的情感倾向映射到 0 到 1。优势是离线可用、调用简单,pip 安装后两行代码就能打分。缺陷也很明确:微博文风是短句、反讽、表情和网络新词混杂,直接用电商语料训练的模型,经常把“哈哈哈我裂开了”判成低落,把“这是人干的事”判成正向。所以我的做法是:第一版用 SnowNLP 跑基线,同时留出自定义词典和重训练接口,等标注样本攒到一定量再提升。
SnowNLP 自带重训练入口,准备好正向和负向两个纯文本语料文件,逐句一行,调用训练接口保存模型即可:
from snownlp import sentiment # neg.txt 和 pos.txt 是逐句一行的标注语料 sentiment.train("neg.txt", "pos.txt") sentiment.save("sentiment.marshal")逻辑说明:train 读取两个语料文件,重新估算朴素贝叶斯的先验概率与条件概率,save 把训练结果写成本地 marshal 文件。之后 SnowNLP 构建时会优先加载新模型。参数要点是语料质量远比数量重要,500 条准确的微博情绪标注,效果往往好过 5000 条来自电商评论的脏数据。
2.3 量化与调度:Pyecharts 负责出图,Flask 负责串联,APScheduler 负责定时
可视化用 Pyecharts 是最短路径,它是 ECharts 的 Python 封装,调用简单,生成的 HTML 图表可离线打开,不需要部署 Node 服务。相比直接写前端,Pyecharts 把配置项封装成 Python 对象,适合不擅长前端的分析人员。
调度这一步容易被忽略。舆情采集要做成定时任务,每天固定几个时间点跑一次,否则数据只有事件发生当时那批,没法看趋势。常见做法是 Linux 上配 crontab,或者在 Python 进程里内置 APScheduler。APScheduler 的好处是任务状态、下次执行时间都在代码里,排错直观:
from apscheduler.schedulers.blocking import BlockingScheduler from collector import run_all sched = BlockingScheduler() # 每30分钟执行一次全量采集,错开整点避免服务端压力集中 sched.add_job(run_all, "interval", minutes=30, id="opinion_job") sched.start()逻辑说明:BlockingScheduler 会占住当前进程,适合单独跑采集服务;如果同进程还要提供 Flask 页面,要改用 BackgroundScheduler。间隔频率不建议低于 5 分钟,微博搜索接口不是为高频轮询设计的。
这三个决定做完,系统骨架就定了:网页接口采集、SnowNLP 打分、Pyecharts 可视化、Flask 搭 Web 入口。下一章开始写能直接跑的代码。
3. 微博舆情数据爬取实战:从 m.weibo.cn 搜索接口到 SQLite 落库
3.1 用 requests 打通搜索接口:登录 Cookie 与 containerid 参数
真正动手写爬虫时,卡点往往不是请求代码,而是 Cookie。m.weibo.cn 的搜索接口未登录状态能返回少量数据,翻几页就会被要求登录。第一步先把浏览器里的 Cookie 复制出来,在浏览器登录微博,打开开发者工具的网络面板,找到 m.weibo.cn 的请求头,把整段 Cookie 复制到代码里。
import requests import time import random SEARCH_URL = "https://m.weibo.cn/api/container/getIndex" HEADERS = { "User-Agent": ("Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/120.0.0.0 Safari/537.36"), "Cookie": "你的登录Cookie", # 用浏览器登录后复制,过期需手动更新 "Referer": "https://m.weibo.cn/search?containerid=100103type%3D1%26q%3D新能源汽车", } def fetch_page(keyword, page=1): params = { "containerid": f"100103type=1&q={keyword}", "page_type": "searchall", "page": page, } resp = requests.get(SEARCH_URL, params=params, headers=HEADERS, timeout=10) data = resp.json() if data.get("ok") != 1: print("返回异常:", data.get("msg") or data.get("ok")) return [] cards = data.get("data", {}).get("cards", []) return cards逻辑说明:requests.get 传入 params 后,中文 keyword 会自动做 URL 编码,不需要手动 quote。返回的 JSON 里最外层 ok 是状态码,ok=1 才是成功,data.cards 是微博卡片列表。很多教程直接拿 resp.text 当 JSON 解析,一旦遇到登录失效,返回 HTML 或错误页,json.loads 直接抛异常;用 resp.json() 配合 ok 判断可以提前发现问题。
参数说明:page 从 1 开始累加,单页大约 10 到 20 条结果;page_type=searchall 是综合页,会混入用户卡片和话题卡片,后面解析时必须用 card_type 过滤。timeout 设 10 秒比较合理,微博接口偶发慢响应,超时后 catch 住重试即可。
3.2 解析 cards 并清洗文本:把 HTML 标签、短链和占位词挡在库外
返回的卡片不全是微博正文。card_type=9 是微博内容,card_type=11 是推荐用户,还有话题聚合卡片。解析时只取 card_type=9 的卡片,避免把用户昵称当正文入库。
import re def clean_text(raw_text): if not raw_text: return "" text = re.sub(r"<[^>]+>", "", raw_text) # 去掉HTML标签 text = text.replace("分享图片", "").replace("分享视频", "") text = re.sub(r"#([^#]+)#", r"\1", text) # 去掉话题符,保留话题词 text = re.sub(r"https?://\S+", "", text) # 去掉短链 text = re.sub(r"\s+", " ", text).strip() return text def extract_rows(cards): rows = [] for card in cards: if card.get("card_type") != 9: continue mblog = card.get("mblog") or {} if not mblog.get("mid"): continue rows.append({ "mid": mblog.get("mid"), "user": (mblog.get("user") or {}).get("screen_name", ""), "created_at": mblog.get("created_at", ""), "text": clean_text(mblog.get("text", "")), "reposts_count": mblog.get("reposts_count", 0), "comments_count": mblog.get("comments_count", 0), "attitudes_count": mblog.get("attitudes_count", 0), }) return rows逻辑说明:clean_text 里的替换顺序不能乱。先去掉 HTML 标签,因为微博正文里的表情和话题会被包在 span 标签里;再去掉“分享图片”“分享视频”这类占位文案;最后去 URL,否则短链会进入词频统计,把词云带偏。话题“#新能源汽车#”提取后保留“新能源汽车”这个词,它恰恰是分析主体。
这里的边界坑是很多卡片字段缺失,上面全部用 get() 兜底,遇到少字段的卡片不会中断整个采集循环。采集中断最难受的不是重试,而是前面翻过去的页要重新翻,产生重复数据。
3.3 增量去重与落库:SQLite 够用,主键设计从第一天做对
数据规模在几万条以内,SQLite 完全够用,单文件备份方便,不需要额外维护数据库服务。多人协作或数据量到百万级再考虑 MySQL。表结构里 mid 做唯一键,keyword 单独一列,因为一张表要存多个关键词的采集结果。
import sqlite3 conn = sqlite3.connect("weibo_opinion.db") cursor = conn.cursor() cursor.execute(""" CREATE TABLE IF NOT EXISTS weibo_post ( mid TEXT PRIMARY KEY, keyword TEXT, user TEXT, created_at TEXT, timestamp INTEGER, text TEXT, reposts_count INTEGER DEFAULT 0, comments_count INTEGER DEFAULT 0, attitudes_count INTEGER DEFAULT 0, crawl_time TEXT ) """) def save_rows(rows, keyword): import datetime now = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") for row in rows: cursor.execute(""" INSERT OR IGNORE INTO weibo_post (mid, keyword, user, created_at, timestamp, text, reposts_count, comments_count, attitudes_count, crawl_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) """, ( row["mid"], keyword, row["user"], row["created_at"], row["ts"], row["text"], row["reposts_count"], row["comments_count"], row["attitudes_count"], now )) conn.commit()逻辑说明:mid 来自微博平台,同一条微博在不同关键词搜索中会出现多次,比如同时爬“新能源汽车”和“特斯拉”,一条微博会被搜到两次。以 mid 为主键并用 INSERT OR IGNORE 写入,重复数据直接跳过。
timestamp 列是为时间趋势统计准备的。微博接口返回的 created_at 多是“3分钟前”“昨天 15:20”这类相对时间,入库时最好解析成绝对时间戳,否则跨天统计会乱。解析函数放到第五章专门讲,那是一个容易踩的一整类坑。
4. 情感分析与可视化落地:从 SnowNLP 打分到 Flask 舆情看板
4.1 SnowNLP 批量打分:短文本过滤与正负阈值怎么定
情感打分这一步,很多新手直接对每条文本循环调用 SnowNLP,数据到两万条就跑得很慢,还会把空文本、纯表情文本也送去打分。先做两个前置过滤:文本长度小于 2 的跳过,纯 URL 组成的文本跳过。
import pandas as pd from snownlp import SnowNLP def score_one(text): text = (text or "").strip() if len(text) < 2: return None return SnowNLP(text).sentiments df["score"] = df["text"].apply(score_one) df = df.dropna(subset=["score"]) POS_TH, NEG_TH = 0.6, 0.4 df["emo_label"] = df["score"].apply( lambda s: "positive" if s >= POS_TH else ("negative" if s <= NEG_TH else "neutral") ) print(df["emo_label"].value_counts())逻辑说明:score_one 返回 SnowNLP 计算的正面概率,越接近 1 代表越正向,越接近 0 代表越负向。阈值用 0.6 和 0.4,是因为电商语料训练的模型输出普遍偏向中间值,直接以 0.5 为界会把大量中性微博硬分成正负,反而失真。跳过短文本也很关键,微博里常见的“哈哈哈”三个字,SnowNLP 经常给出莫名其妙的低分。
更稳妥的做法是对分类结果做人工抽查,随机抽几十条看标签和文本对不对得上,再决定阈值是 0.6 还是 0.55。阈值是项目参数,不是真理。
4.2 用 Pyecharts 做情绪趋势折线和关键词词云
可视化第一步是拿数据说话,不要一上来就追求花哨交互。最常用的三张图是情绪折线图、情感占比饼图、关键词词云。折线图用 Line,X 轴是日期,Y 轴是正向和负向微博数量,能清楚看到事件爆发的拐点。
from pyecharts.charts import Line, Pie, WordCloud from pyecharts import options as opts def draw_trend(date_list, neg_counts, pos_counts): line = ( Line(init_opts=opts.InitOpts(width="1000px", height="480px")) .add_xaxis(date_list) .add_yaxis("负向", neg_counts, is_smooth=True) .add_yaxis("正向", pos_counts, is_smooth=True) .set_global_opts( title_opts=opts.TitleOpts(title="舆情情绪趋势"), tooltip_opts=opts.TooltipOpts(trigger="axis"), legend_opts=opts.LegendOpts(pos_left="center"), ) ) return line.render("trend.html")逻辑说明:add_yaxis 的 is_smooth=True 让曲线更平滑,数据点稀疏时也能看出走势,不会因为个别波动误导判断。tooltip 设为 axis 模式,鼠标悬停能同时看到同一天的正面和负面数量。render 直接输出 HTML 文件,浏览器打开即用。
词云图要注意数据格式,Pyecharts 的 WordCloud 接收的是 [(词, 权重), ...] 列表,不能用字典直接传。做词频统计时去掉无意义的停止词,比如“微博”“转发”“链接”这类高频但不携带情绪的噪音词。
from collections import Counter import jieba stopwords = {"微博", "转发", "链接", "视频", "网页"} def word_freq_from(texts): words = [] for t in texts: words += [w for w in jieba.lcut(t) if len(w) > 1 and w not in stopwords] return Counter(words).most_common(100)逻辑说明:jieba.lcut 是精确分词,返回词列表。过滤条件里的 len(w) > 1 去掉单字,stopwords 去掉分析噪音。前面清洗阶段保留的“新能源汽车”会作为完整词进入词频,成为词云里显眼的焦点词。
4.3 Flask 极简看板:后端只提供 JSON,前端组装图表
舆情看板不必搞成复杂单页应用,Flask 加一个模板就够。后端提供 JSON 接口,前端页面里用 Pyecharts 生成的图表串起来。省事的做法是后端把图表单独 render 成 HTML 片段,前端页面直接嵌入。
from flask import Flask, jsonify, render_template import sqlite3 app = Flask(__name__) def fetch_trend(keyword, days): conn = sqlite3.connect("weibo_opinion.db") rows = conn.execute(""" SELECT date(timestamp, 'unixepoch', 'localtime') as d, SUM(CASE WHEN emo_label='negative' THEN 1 ELSE 0 END) as neg, SUM(CASE WHEN emo_label='positive' THEN 1 ELSE 0 END) as pos FROM weibo_post WHERE keyword=? AND timestamp >= ? GROUP BY d ORDER BY d """, (keyword, days * 86400)).fetchall() conn.close() return rows @app.route("/") def index(): return render_template("index.html") @app.route("/api/trend") def api_trend(): rows = fetch_trend("新能源汽车", 7) return jsonify([{"date": r[0], "neg": r[1], "pos": r[2]} for r in rows])逻辑说明:route 接口把 SQL 执行结果转成 JSON 交给前端,前端拿到后塞进 Pyecharts 的 data。日期处理用了 unixepoch 和 localtime 两个修饰符,确保按本地时区而不是 UTC 时区分组,否则凌晨的微博会被算到前一天。这是可视化阶段最容易出错的地方,对应第五章第四条坑。
后端和前端分离还有一个好处:定时任务重建数据时,页面刷新就能看到新结果,不用重启 Flask 进程。
5. 微博舆情系统常见问题与避坑清单:五个高频翻车点及对策
5.1 搜索接口返回 ok=-100:登录 Cookie 失效还是请求太密
现象:程序没有报错,requests 请求正常返回,但 data 里没有 cards,ok 字段是 -100 或 0,msg 提示“请先登录”。
原因:两种可能。一是 Cookie 过期,二是同一 Cookie 短时间请求量太大,触发了服务端的频率限制。
解决:先看时间点。如果开机第一次请求就失败,多半是 Cookie 过期,重新登录浏览器复制新 Cookie。如果是连续翻页到第 5 页之后开始失败,说明单 Cookie 请求过猛,把单页间隔从 1 秒调大到 5 到 8 秒,并限制单次任务最多翻 10 页。再不够就把关键词拆开,按小时分多次采集,比如“新能源汽车 销量”和“新能源汽车 故障”分开搜索,单次请求压力小很多。
提示:Cookie 是敏感信息,自己本地研究没问题,切勿把它提交到公开仓库,否则可能被冒用。
5.2 情感分析把微博反讽当正面:硬负面规则先于模型
现象:一条“这操作真是666,建议直接申遗”被标成正向,但人眼一眼看出是嘲讽。
原因:SnowNLP 的语料来自商品评价,模型不识别微博语境里的反讽和网络新词。电商评论里少见的语用表达,在微博上是日常操作。
解决:在跑模型之前,先过一层硬负面规则。规则命中直接判负,不进模型。规则词要精不要多,避免误伤。
HARD_NEGATIVE = ["翻车", "离谱", "无语", "避雷", "气死", "被坑"] def hard_rule(text): for w in HARD_NEGATIVE: if w in text: return "negative" return None逻辑说明:hard_rule 只做短文本匹配,命中返回 negative,未命中返回 None。主流程里先调用它,有结果直接赋值,没有结果再交给 SnowNLP 打分。
参数调整:规则词不是字典越大越好。把“拆”“慢”这种多义词加进去,会把“拆快递”和“慢慢变好”误判成负面。负面词表每加一个词,至少用 50 条历史数据做一次回测确认误伤率。
5.3 词云里全是表情和噪音词:清洗顺序必须固定
现象:词云图里出现大量“—”“>”“分享图片”“超话”等无意义词。
原因:微博正文里充满 HTML 标签、短链、表情符和运营位文案,清洗时只剥了标签,没有做完整过滤。
解决:把清洗顺序固定成一条流水线:HTML 标签、短链接、分享占位词、连续空白、emoji。其中 emoji 要不要保留取决于分析目标。做词频统计时建议删除,做情感分析时建议保留,表情符号本身携带情绪,但进了词云就是噪声。
import re def cleaner(text, keep_emoji=True): text = re.sub(r"<[^>]+>", "", text) text = re.sub(r"https?://\S+", "", text) text = text.replace("分享图片", "").replace("分享视频", "") if not keep_emoji: emoji_pattern = re.compile( "[\U0001F300-\U0001F64F\U0001F680-\U0001F6FF" "\U0001F900-\U0001F9FF\U00002600-\U000027BF]+" ) text = emoji_pattern.sub("", text) return re.sub(r"\s+", " ", text).strip()逻辑说明:emoji 的正则范围覆盖了 Unicode 的几大 emoji 区段,能去掉大部分常见表情。keep_emoji 参数让清洗阶段先保留,词频统计阶段再决定是否过滤,比清洗时无条件删除灵活得多。
注意:这个正则的字符区间在 Python 3.7 以上正常,老版本 Python 或某些 Windows 控制台环境下运行可能报 Unicode 编码错误,脚本文件头记得声明 utf-8。
5.4 按天统计偏移八小时:相对时间转时间戳的解析坑
现象:每天凌晨 0 点前后爬到的数据,在趋势图里被归到前一天。
原因:微博 mblog 里的 created_at 返回的是“3分钟前”“昨天 15:30”这类相对时间,入库时直接存了字符串。或者解析成时间戳时没有对齐东八区,SQLite 分组时错位。
解决:建立统一的相对时间解析函数,入库前把 created_at 统一转成时间戳。
import re from datetime import datetime, timedelta def parse_weibo_time(created_at, now=None): now = now or datetime.now() if "刚刚" in created_at: return now m = re.search(r"(\d+)分钟前", created_at) if m: return now - timedelta(minutes=int(m.group(1))) m = re.search(r"(\d+)小时前", created_at) if m: return now - timedelta(hours=int(m.group(1))) m = re.search(r"昨天 (\d+):(\d+)", created_at) if m: return (now - timedelta(days=1)).replace( hour=int(m.group(1)), minute=int(m.group(2)), second=0, microsecond=0 ) m = re.search(r"(\d{4})-(\d{2})-(\d{2})", created_at) if m: return datetime(int(m.group(1)), int(m.group(2)), int(m.group(3))) return now逻辑说明:匹配顺序有讲究,先匹配“分钟前”,再“小时前”,再“昨天 xx:xx”,最后是完整日期。不能反过来,因为“昨天 15:30”里的“30”如果先被“分钟前”正则捕获,日期就错了。return now 兜底,拿不准的文本不把程序搞挂。入库时把返回值转成 int 时间戳存到 timestamp 字段,之后 SQLite 分组和 Flask 接口就都能对齐。
5.5 重跑采集后统计数据翻倍:主键去重与多关键词计数
现象:当天第一次跑,负面数是 200,第二次定时任务又跑,负面变成 400,趋势图每天跳变。
原因:同一个关键词重复采集,中间产生的数据没有正确去重。或者之前清库重跑,旧数据没清理干净。
解决:主键是底线,遇到多种情况,统一按 mid 做 INSERT OR IGNORE。多关键词场景还有另一种翻倍:同一条微博在“新能源汽车”和“特斯拉”两个关键词下都被采集,按关键词分组统计时这条微博在两组里都会计入,总情绪占比加一块超过 100%。统计时只按 mid 去重后再分关键词,或者最外层查询用 SELECT DISTINCT mid。
另一个教训是采集脚本和统计脚本不要共用同一个“最近 N 天”过滤条件。采集脚本用“最近 1 小时新增”抓增量,统计脚本用“最近 7 天全量”做分析,两者逻辑分开,比在采集端反复纠结是否重复友好得多。
6. 进阶验证:给舆情系统加一把尺子,再谈扩展
6.1 先做 30 条人工标注,算算真实准确率
系统搭完,第一件事不是加功能,而是验证输出可不可信。随机抽最近三天采集的文本,按正负中性各抽一部分,手工标注后与系统输出对比。准确率、召回率、F1 直接用 sklearn 算:
from sklearn.metrics import classification_report # y_true 是手工标注,y_pred 是系统输出,两者都是字符串列表 print(classification_report(y_true, y_pred, target_names=["negative", "neutral", "positive"]))如果总体准确率不到 70%,不建议急着调阈值,先看错误样本集中在哪类。常见错误分布在反讽句、语气词密集的短句和带表情符号的句子。找到主要错误模式,再针对性加规则。
6.2 用否定词反转做规则修正,精度有明显提升
规则先于模型这条,实际改进比想象中大。一条微博无论 SnowNLP 给什么分数,命中硬负面词就强制标负。否定词的修正是进阶操作,字符串匹配会翻车,分词后找否定词相邻词更稳,只在“不、没、别”后面跟着正面词时翻转一次。
neg_prefix = {"不", "没", "别", "无", "莫"} def flip_negation(words): for i, w in enumerate(words[:-1]): if w in neg_prefix and words[i + 1] in POSITIVE_DICT: words[i] = "否定" return words逻辑说明:POSITIVE_DICT 是自己维护的正面词表,命中正面词且前一个词是否定词,就把该词替换成“否定”。这个技巧比简单否定匹配准确得多,但依旧处理不了“你说得没毛病”这类口语,别指望规则解决全部问题。
6.3 养成先跑一周再扩展的习惯
每次做完这类系统,我都会先拿一个自己熟悉的领域跑一周,每天看它报出来的趋势和情绪占比。哪条数据明显不合理,立刻追查是清洗环节还是打分环节出了问题。系统的可信度不是靠复杂模型撑起来的,是这些最朴素的核对习惯一层层垒出来的。等一周后,你对自己系统的边界有了具体感知,再谈多模态扩展、引入预训练模型或者做事件检测,都会踏实很多。希望帮到你。
本文还有配套的精品资源,点击获取