ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

微博舆情分析实战:爬虫、LDA主题与情感分析全链路

微博舆情分析实战:爬虫、LDA主题与情感分析全链路 简介这是一套基于微博数据的舆情分析项目源码涵盖微博爬虫、LDA主题分析与情感分析等关键环节面向计算机、通信、人工智能、自动化等专业的学生、教师及从业者可用于课程设计、期末大作业或毕业设计。压缩包共39个文件含23个Python脚本、7个Markdown说明、7个txt语料/词表、1个词向量模型和1个xlsx数据表整体16.16MB目录按热度计算、相似度、情感分析、爬虫、LDA等模块划分其中py为程序主体md为说明文档txt为语料与停用词表model为词向量模型xlsx为中间数据。目前已有211人学习下载。项目为高评分毕业设计代码均经过调试可直接运行除爬虫与常规分析外还提供情感分析API版与SDK版两套实现、LDA超参调节、分词处理、正向/负向语料及近义词表等补充材料学习切入点丰富基础强者也可自行改造适配不同舆情场景。1. 微博舆情分析先解决数据来源再谈主题和情绪一条微博在半夜冲上热搜两小时后的舆情报告就要交到业务手里。很多第一次做这个方向的人以为难点在情感分析模型实际做下来会发现一个完整的基于微博数据的舆情分析项目八成时间耗在爬虫稳定性和数据清洗上。这个项目要解决的核心问题很直接把微博上非结构化的短文本变成可量化的主题分布和情绪趋势输出给决策者看。适合三类人——数据分析师想补全舆情监测链路、算法工程师需要一个有业务闭环的练手案例、毕业论文选题涉及自媒体文本挖掘的学生。源码和资料的存在意义是让你不用从零趟一遍接口、编码、反爬的坑但能不能快速用起来取决于你是否清楚每一步的内在逻辑。2. 微博爬虫落地Cookie 从哪来、搜索接口怎么翻页、增量去重怎么写2.1 微博爬虫的选型思路为什么主流方案都走 m.weibo.cn 搜索接口微博的数据采集有多个入口pc 端 weibo.com 页面结构复杂数据嵌套在 HTML 里解析成本高而且登录态校验严格移动端 m.weibo.cn 的接口返回 JSON字段干净成为大多数人做微博爬虫的首选。这个接口的路径是https://m.weibo.cn/api/container/getIndex通过containerid参数指定搜索类型page参数控制翻页返回的数据里带card_type、mblog等结构可直接提取微博正文、发布时间、点赞数、评论数、转发数。爬虫方案的取舍集中在两个点一是登录态怎么保二是翻页逻辑怎么写。登录态方面模拟用户名密码登录会遇到滑块验证和短信验证风险高且不稳定更常见的做法是手动从浏览器复制 Cookie注意SUB和SUBP这对关键字段前者是登录凭证后者是用户标识。翻页方面m.weibo.cn 搜索接口的翻页参数有page单页 10 到 20 条数据当爬取时间跨度较大的历史数据时还要配合starttime和endtime限定时间窗口否则默认只返回最近一段时间的微博。采集频率控制是爬虫能否长期运行的分水岭。微博对单账号的请求频率有限制常见阈值是每分钟 30 到 60 次请求超过就会触发暂时封禁。安全做法是每次请求后 sleep 2 到 3 秒并随机化间隔避免规律性请求被识别为机器行为。还要准备一个备用 Cookie主 Cookie 被封时切换保证采集任务不中断。2.2 最小可用爬虫代码从搜索关键词到抓起微博正文下面这段代码实现的是按关键词搜索微博并提取核心字段是舆情项目里最常见的数据入口。注意替换代码中的 Cookie 为你自己浏览器登录后的值关键词和时间范围按项目需求改。import requests import time import random import json import pandas as pd # 从浏览器复制 Cookie关键字段是 SUB 和 SUBP COOKIE _T_WMxxx; SUBxxx; SUBPxxx HEADERS { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X), Referer: https://m.weibo.cn/search?containerid100103type%3D1%26q%3D%E5%A4%A7%E4%BC%97%E5%AE%A1, Cookie: COOKIE, } def fetch_weibo_search(keyword, page, starttime, endtime): # 搜索接口的 containerid 固定前缀q 参数是 URL 编码后的关键词 containerid f100103type1q{keyword} url https://m.weibo.cn/api/container/getIndex params { containerid: containerid, page: page, starttime: starttime, # 格式: 20240101 endtime: endtime, # 格式: 20240131 } resp requests.get(url, headersHEADERS, paramsparams, timeout10) if resp.status_code ! 200: print(f请求失败, 状态码: {resp.status_code}) return [] data resp.json() cards data.get(data, {}).get(cards, []) rows [] for card in cards: if card.get(card_type) ! 9: continue mblog card.get(mblog, {}) # 只保留有正文的微博过滤广告卡片 if not mblog.get(text): continue rows.append({ mid: mblog.get(mid), text: clean_html(mblog.get(text)), user: mblog.get(user, {}).get(screen_name), created_at: mblog.get(created_at), reposts_count: mblog.get(reposts_count), comments_count: mblog.get(comments_count), attitudes_count: mblog.get(attitudes_count), }) return rows def clean_html(raw): # 微博正文是 HTML 片段需要去掉 a、span 等标签和 emoji 的 span 包裹 import re text re.sub(r[^], , raw) return text.strip()请求参数里有两个容易踩的点。starttime和endtime只在搜索接口的部分场景生效如果你不传这两个参数接口默认返回的是按热度排序的结果同一关键词的热门微博可能全是旧内容对舆情分析会造成时间偏差。page参数最大能翻到 50 页左右超过之后接口会返回空数据这是服务端限制不是你的代码问题。采集得到的数据默认是热度排序不是时间排序。社区里有人通过在关键词后拼接scopeallsorttime来强制按时间排序但我在实际项目里发现这个参数在部分账号下不生效更稳妥的做法是缩小时间窗口比如按天切分采集再合并结果。每天一个窗口每个窗口翻 10 页基本能覆盖当天该关键词的主要微博。2.3 把爬虫改造成增量采集用 SQLite 做去重和断点续爬舆情项目不是跑一次就结束而是每天定时抓取新增数据。累计抓取的数据量大了之后重复采集是必然的如果不做去重下游的 LDA 主题分析和情感分析会重复计算同一批微博导致舆情趋势失真。常见的去重方案有两种一种用 Redis 的 set 存已见过的 mid速度快但多一个依赖另一种用 SQLite 的 unique 约束零部署成本适合单机项目。我一般用 SQLite理由是这个项目的数据量级在百万以下SQLite 足够支撑而且断点续爬的实现更直观。CREATE TABLE IF NOT EXISTS weibo_posts ( mid TEXT PRIMARY KEY, text TEXT, user_name TEXT, created_at TEXT, reposts_count INTEGER, comments_count INTEGER, attitudes_count INTEGER, crawl_time TEXT DEFAULT (datetime(now, localtime)) );把 mid 设为主键后插入时使用INSERT OR IGNORE重复的微博会被自动跳过。这样即使爬虫中途崩溃重启后从崩溃页码继续跑也不会产生重复数据。增量采集的逻辑变化不大只是把上面的请求函数放进一个循环每次抓完一页就检查返回条数如果某页返回 0 条说明已经抓到时间窗口的边界终止循环。这里要说一个血泪经验只去重 mid 是不够的。微博支持编辑后重新发布同一 mid 的正文内容可能变化但主键去重会把更新后的内容丢掉。如果是追踪突发事件建议在去重表之外再加一张内容变更表记录 mid 对应的多版本正文做舆情演变分析时会发现这条数据的价值很大。3. LDA 主题分析从分词到选 K微博短文本为什么不能直接套用开源教程3.1 微博短文本预处理分词、去停用词与自定义词典的边界LDA 主题模型基于词袋假设输入的质量直接决定主题输出的质量。微博文本有两个天然短板一是短单条微博通常只有几十个字词共现信息稀疏二是噪声大网络用语、表情符号、提及、话题标签混在一起。直接拿开源教程里的 LDA 脚本跑微博数据产出的主题往往是几个高频词堆在一起没有业务含义。预处理阶段要把每个环节做透。分词工具通常选 jieba配合中文微博语料的自定义词典使用。微博里大量存在的话题标签#xxx#、用户名称以及“yyds”“绝绝子”“破防”这类网络热词需要单独维护一个词典文件。如果不加自定义词典jieba 会把“yyds”切成单字LDA 的主题词表里就会出现一堆无意义字符。import jieba import jieba.analyse import re # 加载自定义词典每行一个词格式: 词 词频 词性 jieba.load_userdict(weibo_words.txt) def preprocess_text(text): # 去掉话题标签中的 # 字符但保留标签文字本身 text re.sub(r#(.?)#, r\1, text) # 去掉 用户名、URL、emoji 的 Unicode 范围 text re.sub(r[\w\-], , text) text re.sub(rhttps?://\S, , text) text re.sub(r[\U0001F000-\U0001FA99], , text) # 只保留中文、英文字母和数字微博里的标点符号对主题建模无增益 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9], , text) words jieba.lcut(text) return [w for w in words if len(w.strip()) 1]停用词表要针对微博语境扩充。通用停用词表处理不了“哈哈哈哈”“转发微博”“展开全文”这类微博特有噪声。展开全文是微博长文的截断标记必须在预处理阶段干掉否则 LDA 会把它当成一个高频主题词所有主题里都带着它主题区分度直接变差。3.2 构建词典与语料gensim 的完整流程和参数说明预处理好之后进入 LDA 的标准流程用 gensim 的corpora构建词典和语料然后训练模型。这里要注意两个细节一是过滤掉出现次数过少的词微博数据里大量出现只出现一次的罕见词这些词对主题建模是纯噪声二是过滤掉出现在过多文档中的词比如“微博”“中国”这类贯穿所有语料的词会让主题之间难以区分。from gensim import corpora, models from collections import Counter # tokenized_docs 是预处理后的分词列表的列表 # 例如 [[微博, 舆情, 分析], [热搜, 爆了, 转发], ...] tokenized_docs [...] # 上一步的输出 # 过滤低频和高频词no_below 过滤出现在少于5篇文档中的词 # no_above 过滤出现在超过50%文档中的词 dictionary corpora.Dictionary(tokenized_docs) dictionary.filter_extremes(no_below5, no_above0.5) # 词典建立后把每篇文档转成词袋向量 corpus [dictionary.doc2bow(doc) for doc in tokenized_docs] # 训练LDA模型 lda_model models.LdaModel( corpuscorpus, id2worddictionary, num_topics8, # 主题数选K的方法见3.3节 passes20, # 迭代遍数微博短文本语料建议15-30 alphaauto, # 让模型自动估计主题文档分布 etaauto, # 让模型自动估计词主题分布 random_state42 # 固定随机种子保证结果可复现 )filter_extremes的三个参数是微博数据里最需要调的。no_below设太小低频噪声词进入词典主题词表里会混进“哈哈哈”等语气词设太大本来就是稀疏的短文本语料会被过滤得所剩无几。no_above0.5的含义是去掉出现在超过一半文档中的词这个阈值对微博语料偏保守可以试 0.6 到 0.7。passes决定模型收敛程度微博语料短、噪声大20 次一般够用再多训练时间翻倍但对主题质量提升有限。3.3 选 K 的正确姿势困惑度会误导你一致性才是短文本的关键指标LDA 的num_topics参数是最难定的一个。很多教程教你看困惑度曲线perplexity 越低模型越好但这个标准在微博短文本场景下会翻车。短文本的困惑度随主题数增加几乎单调下降曲线在 K10 处没有拐点你选出来的 K 往往偏大主题之间边界模糊。更靠谱的指标是一致性分数 coherence它评估主题词之间的语义相关性对短文本效果明显更好。from gensim.models import CoherenceModel coherence_scores [] perplexity_scores [] topic_range range(4, 15, 2) # 测试4到14个主题 for k in topic_range: model models.LdaModel( corpuscorpus, id2worddictionary, num_topicsk, passes20, alphaauto, etaauto, random_state42 ) cm CoherenceModel( modelmodel, textstokenized_docs, dictionarydictionary, coherencec_v ) coherence_scores.append(cm.get_score()) perplexity_scores.append(model.log_perplexity(corpus)) # 输出对比取 coherence 最高或开始收敛的主题数 for k, c, p in zip(topic_range, coherence_scores, perplexity_scores): print(fK{k}, coherence{c:.4f}, perplexity{p:.4f})coherencec_v基于滑动窗口计算词共现对短文本敏感度好c_uci和c_npmi也可以试但 c_v 在社区实践中表现最稳定。另外注意一个操作细节texts参数要传预处理后的原始分词列表不是词袋向量这也是容易出错的地方。选 K 还要结合业务判断。舆情项目里主题数是服务汇报对象的K8 意味着报告里展示 8 个主题如果业务方只关心突发事件、政策讨论、消费者反馈这三类K 可以适当调小增加主题的可解释性。纯靠指标选出来的 K 可能和业务口径对不上我的习惯是先用 coherence 选一个范围再人工查看每个主题的高频词调整 K 值到主题词表能一眼看懂为止。4. 情感分析在微博上的三种路线词典基线、语料微调与多模态边界4.1 词典法、传统机器学习与深度学习微博场景怎么选情感分析在微博舆情项目里承担的是情绪量化任务——判断每一条微博是正面、负面还是中性然后按时间统计情绪曲线。技术路线上有三类选择基于情感词典的方法、基于传统机器学习的分类器、基于预训练模型的深度学习方法。词典法用 SnowNLP 或 BosonNLP 这类带有情感分值的词库计算整条微博的情感极性得分。优点是快、可解释性强缺点是微博里大量反讽、网络梗和夸张用语会骗过词典。传统机器学习路线把 TF-IDF 或词向量作为特征用朴素贝叶斯、SVM 分类效果取决于标注语料规模标注成本高且泛化能力一般。深度学习路线用 BERT 或 TextCNN 做文本分类准确率最高但训练成本和对标注语料的要求也是最高的。舆情项目的典型做法不是一步到位上模型而是先用词典法跑一个基线看错误分布再用少量标注语料对模型做微调。原因很现实项目最早期的需求是快速上线后续才有数据积累和优化空间。如果一上来就用 BERT光是标注 1 万条微博和调训练脚本就要两周业务方等不起。4.2 SnowNLP 跑基线与自定义训练一段可复现的代码SnowNLP 是词典法里上手最快的库它的默认模型针对电商评论训练直接用在微博上会系统性偏差。比如“这也太绝了吧”在电商语境是好评在微博语境可能是吐槽。所以基线跑完之后必须用你的领域语料重新训练。from snownlp import SnowNLP from snownlp import sentiment # 第一步先用默认模型打分看基线效果 def sentiment_score(text): s SnowNLP(text) return s.sentiments # 0到1之间的浮点数越接近1越正面 # 第二步准备自定义训练语料 # neg.txt 和 pos.txt 每行一条微博编码为 utf-8 # 示例 neg.txt: 这也太垃圾了吧 无语死了 真的 # 示例 pos.txt: 今天运气太好了 这条微博写得真棒 sentiment.train(neg.txt, pos.txt) sentiment.save(weibo_sentiment.marshal) # 第三步加载自定义模型并打分 from snownlp import sentiment as new_sentiment new_sentiment.load(weibo_sentiment.marshal) def custom_sentiment_score(text): s SnowNLP(text, sentimenternew_sentiment) return s.sentimentssentiment.train的底层逻辑是贝叶斯分类训练集规模建议正负各 1000 条以上太少会导致模型过拟合到训练集。标注语料的来源可以从爬虫抓到的数据里抽样按正面、负面、中性三分类标注然后合并正负做二分类训练。中性微博怎么办SnowNLP 没有中性类别实际项目中 0.4 到 0.6 之间的分数当作中性处理这是一个工程妥协但够用。4.3 多模态情感分析是微博舆情的下一个边界但不是现在的核心热词里有多模态情感分析它确实和微博舆情相关——微博正文里经常带图片、视频文本之外的表情包、视频人物表情本身就是情绪载体。做纯文本情感分析会遇到一个瓶颈一条微博写着“太好看了”配图是一个翻白眼的表情包文本是正面的实际情绪是讽刺。多模态情感分析就是要把图片、视频里的人物情感识别和文本情感识别融合解决这类矛盾样本。但从落地角度看这个项目的主干还是文本舆情分析。多模态部分的数据获取、预处理、模型融合复杂度成倍上升而且标注成本极高。我的建议是先把文本情感分析做到位在报告里标注“未覆盖图片和视频情感”的边界等数据量积累到一定程度再单独立项做多模态增强。5. 舆情分析项目避坑爬虫封禁、LDA 翻车与情感误判排查5.1 爬虫跑了一个小时后突然停止返回 414 状态码现象爬虫正常运行一段时间后请求返回 414 URI Too Long之后即使切换代理也无法恢复。原因m.weibo.cn 搜索接口的 URL 参数containerid中的关键词是 URL 编码的如果关键词里有特殊字符比如 #、、且请求参数被错误地拼接在 URL 而不是通过params传参每次请求会累积历史参数导致 URL 超长。解决把containerid的值提前用urllib.parse.quote编码把请求参数放进params字典交给 requests 处理requests 会正确处理编码和拼接。另外检查爬虫代码里是否有把 Cookie 放入 URL 的错误写法Cookie 应该只放在 header 里。5.2 LDA 主题全是数字和“哈哈哈”实际业务主题一个都看不出来现象打印 LDA 主题词表每个主题的前几个词都是“哈哈哈”“转发”“微博”“看到”这类词业务相关的词排在十名开外。原因预处理阶段没有去掉口语化的高频噪声词同时filter_extremes的no_above阈值设得太大导致“哈哈哈”这类出现在大量文档中的词没被过滤。解决扩充停用词表加入“哈哈哈”“嘿嘿”“啊啊啊”“转发微博”“展开全文”等微博特有词把no_above从 0.5 降到 0.3即去掉出现在超过 30% 文档中的词同时提高no_below阈值把只出现一次或两次的罕见词去掉。5.3 情感分析把“这操作真是绝了”判成正面现象一条明显是吐槽的微博SnowNLP 给出的情感分是 0.85归类为正面。原因SnowNLP 默认词典是电商语料训练的在电商语境里“绝了”通常是好评微博语境里“绝了”常常是反讽或负面感叹。这是跨领域迁移导致的系统性偏差。解决准备领域标注语料把微博中常见的反讽表达抽出来加入 pos.txt 和 neg.txt 的对抗样本。关键是要让训练语料里出现“哪怕文字表面是褒义词、实际是贬义”的例子比如“这操作绝了吐槽”“太棒了翻白眼”让模型学到上下文特征。5.4 舆情爆发事件抓不到数据关键词搜索全是旧微博现象一个话题当天爆了用关键词搜不到当天新发的微博搜索结果里全是几周前的旧内容。原因搜索接口默认按热度排序热门微博可能会霸榜数周按时间窗口采集时如果不显式排序新发布的微博反而排在后面翻页很难翻到当天数据。解决把时间窗口缩小到单天然后按天循环同时对同一关键词在一天内多次采集分别抓第 1 页到第 10 页再合并去重。凡是舆情追踪主题都建议保留原始created_at字段后续做时间序列分析时按小时聚合。5.5 数据库里微博数量没变但业务说报告里的数据量和微博 App 看到的对不上现象爬虫报抓取成功SQLite 里也有数据但统计每天的微博量比手机 App 上看到的少很多。原因m.weibo.cn 搜索接口返回的是综合搜索卡片包含普通微博、话题页、用户推荐等不同类型的卡片代码里只解析了card_type9的微博卡片漏掉了部分转评赞特别低的普通微博以及某些推广卡片实际也是微博正文。解决检查返回的 cards 列表里其他 card_type 的含义比如card_type9是微博卡片card_type11是热搜话题卡片。如果主题是舆情分析话题卡片也应该纳入数据源。另外要检查分页是否真的翻到底了有些关键词的搜索结果不足一页时接口会返回空数据代码里要加一个最小条数判断防止提前中断。6. 把爬虫、LDA、情感分析串成日报输出格式、验证方法与交付习惯舆情项目最容易被低估的是输出环节。很多做了爬虫和建模的人最后卡在怎么把主题分布和情感分数变成一份业务方能直接看的日报。我习惯的输出格式是三张表加一张趋势图第一张表是当日关键词微博量 Top 10第二张表是各主题占比及主题关键词第三张表是各主题的情感倾向分布趋势图展示七日情感指数变化。验证方法要可量化否则日报会被质疑。我一般抽 100 条微博人工标注主题归属算主题预测的准确率目标 70% 以上情感分析按正面、负面、中性三类各抽 50 条计算准确率和召回率。模型调参的依据是这些数字不是感觉。LDA 的主题名由人命名命名规则建议用“主题关键词 业务口径”的组合比如“消费维权-退款/投诉/客服”。交付习惯上每天定时任务跑完爬虫后先自动跑 LDA 的增量训练再跑情感分析最后生成日报发送到工作群。这里有一个我踩过的坑LDA 模型不要每天全量重训微博语料每天增长快全量重训会导致主题编号漂移周一叫主题 0 的话题周三变成主题 5。我一般每周重训一次模型日常增量数据用旧模型推理只在周报时统一重训并人工审核主题词表。这个项目的技术栈不复杂难点在数据质量和工程细节。希望你从这套链路起步时先把爬虫跑稳、把预处理的噪声滤干净再考虑上大模型。文本舆情做到位后多模态情感分析是自然延伸但前提是这套单模态链路已经稳定运行并有人工审核兜底。希望帮到你。本文还有配套的精品资源点击获取
返回列表