ARTICLE DETAIL

资讯详情

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

微博舆情分析实战:爬虫、LDA主题与情感分析全流程解析

微博舆情分析实战:爬虫、LDA主题与情感分析全流程解析 简介面向计算机、通信、人工智能、自动化等相关专业的学生、教师与从业者基于微博数据的舆情分析项目覆盖微博爬虫、LDA主题分析和情感分析三条主线既适合初学者从零上手也为有基础的读者提供了可修改扩展的框架。项目答辩评审得分98分代码经过调试测试运行稳定热度计算、分词处理、情感分析等模块的界限清晰便于按需替换语料、调整参数或接入新数据源。压缩包共39个文件包含23个Python脚本、7个Markdown说明、7个txt语料与停用词表、1个Word2Vec模型和1个Excel中间结果整体仅16.16MB其中Python脚本覆盖爬虫、清洗、建模与可视化Markdown文档给出模块使用思路txt语料涵盖正负向情感、停用词与近义词表能够支撑完整实验流程。目前已有210人学习下载对于希望走通微博舆情分析全流程、完成课程设计或毕业设计的读者这套源码与配套资料能提供清晰参照和直接可用的实践基础。1. 微博舆情分析项目不只是爬虫先想清楚“分析什么”再动手拿“基于微博数据的舆情分析项目包括微博爬虫、LDA主题分析和情感分析源码资料齐全”这个标题去搜能看到不少打包好的代码包。但项目能跑起来不代表能直接用到业务里。很多接手这类项目的人第一步陷进微博爬虫的反爬机制等数据采下来又发现LDA抽出的主题全是“哈哈”“转发”情感分析把“真的会谢”判成正面。问题不出在工具而在没把分析目标拆开是监控事件热度还是识别负面信息要做归因还是要做预警目标不一样字段设计、清洗策略和模型选型全都不一样。这篇博文就顺着一个标准微博舆情分析流程把爬虫、LDA主题分析、情感分析各自的决策点讲清楚最终是让你能把一份源码变成自己能改、能调、能解释结果的工程。2. 微博爬虫的采集边界与字段设计先解决“能采什么”再写代码2.1 采集方式选型API、移动端接口与网页端的取舍微博开放API的个人申请门槛比较高很多接口需要企业认证普通开发者拿不到。常见做法是退一步用移动端H5接口m.weibo.cn或者网页版搜索接口。m.weibo.cn返回JSON比PC端HTML好解析得多也是很多开源微博爬虫的首选。方式是否需要登录返回格式适合场景官方API需要高权限JSON稳定但门槛高m.weibo.cn接口需要CookieJSON中小规模采集推荐PC网页版需要CookieHTML不推荐解析成本高选型时还要想清楚采集量级。如果只需要几万条数据单机跑m.weibo.cn足够了如果是千万级就要考虑分布式采集但舆情项目做到最后真正稀缺的往往不是数量而是字段是否干净。2.2 最小可用爬虫一条搜索请求能带回哪些字段以搜索关键词为例m.weibo.cn的搜索接口是/search/mblog核心参数是keyword和sort。一个最小请求长这样import requests import time def fetch_weibo_search(keyword, cookie, page1): # 请求头里带上Cookie模拟登录态 headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X), Cookie: cookie, Referer: https://m.weibo.cn/search } params { containerid: f100103type1q{keyword}t, page_type: searchall, page: str(page) } resp requests.get( https://m.weibo.cn/search/mblog, paramsparams, headersheaders, timeout(3, 10) ) resp.raise_for_status() data resp.json() cards data.get(data, {}).get(cards, []) results [] for card in cards: if card.get(card_type) ! 9: # card_type9 是微博正文卡片 continue mblog card.get(mblog, {}) results.append({ id: mblog.get(id), text: mblog.get(text_text), # 已去HTML标签的纯文本 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 results if __name__ __main__: cookie 你的Cookie rows fetch_weibo_search(台风, cookie, page1) for r in rows: print(r[created_at], r[user], r[text][:50]) time.sleep(2) # 控制频率单位秒这段代码把一页搜索结果里的微博正文、作者、时间和互动数据拼成了字典列表。text_text是微博接口里已经剥离HTML标签的字段比自己去正则解析a更省事。如果字段名不叫这个说明接口版本不同先打印mblog.keys()看真实结构。参数方面containerid定义了搜索类型q台风是关键词type1表示综合搜索。翻页不要单纯累加page微博接口有时会返回since_id放在resp.json()[data][cardlistInfo][since_id]里优先用since_id翻页才能拿到去重后的增量数据。2.3 反爬与数据治理别把账号封掉才想起来限速很多人热衷构造复杂Headers来“伪装”其实微博更看重请求频率。单账号单机每秒1-2个请求一次爬几千条基本安全。更稳妥的做法是把采集任务拆成按小时分批配合Redis存已抓取的微博ID做去重。提示不要在同一秒连续请求如果返回“100005_19010004”这类错误码说明触发了访问限制停半小时以上再继续。数据落地时我一般不用一行一个JSON而是直接用CSV。字段顺序固定方便后续pandas读取。但CSV有编码坑to_csv(..., encodingutf-8-sig)才能被Excel正常打开。更规范的项目会落SQLite去重、增量更新都容易。建表语句如下CREATE TABLE IF NOT EXISTS weibo_posts ( id TEXT PRIMARY KEY, keyword TEXT, text TEXT, user_name TEXT, created_at DATETIME, reposts_count INTEGER, comments_count INTEGER, attitudes_count INTEGER );不要小看created_at的格式。微博返回的是“6分钟前”“昨天 12:10”这类相对时间入库前必须转成绝对时间戳否则后续做时间序列分析时全是乱序。处理方式用pandas.to_datetime配合floor(h)对齐到小时就能得到舆情热度的基础曲线。3. LDA主题分析从分词到调参微博短文本怎么变成可解释主题3.1 短文本的预处理难点为什么直接跑LDA全是噪声LDALatent Dirichlet Allocation假设每篇文章是多个主题的混合分布这个假设对新闻、论文很成立但微博单条只有几十到一两百字同一个句子里“转发”“表情”“哈哈”混乱出现直接丢给LDA主题词经常是“转发”“分享”“哈哈”这些功能词。原因有三个一是语料太稀疏词共现矩阵大量是零二是口语化严重“绝绝子”“yyds”这类新词没有语义约束三是话题标签#和 用户 会干扰分词。所以预处理不是简单去停用词而是要主动合并短文本。常见做法是把同一个用户在同一时间段内的微博拼成一条或者按小时/按事件窗口把多条微博聚合成“伪文档”。聚合窗口一般取1小时数据量少时可以放宽到6小时。聚合后再做分词、去停用词、过滤长度小于2的词最后只保留动词和名词主题质量会明显改善。3.2 用jieba和gensim跑通最小LDA流程下面是一段可复现代码输入是pandas的text列输出是训练好的LDA模型和每个文档的主题分布import pandas as pd import re import jieba from gensim.corpora.dictionary import Dictionary from gensim.models.ldamodel import LdaModel # 读取采集结果 df pd.read_csv(weibo_posts.csv, encodingutf-8-sig) stopwords set(open(stopwords.txt, encodingutf-8).read().split()) def preprocess(text): # 去掉话题标签和用户保留中文和字母 text re.sub(r#(.?)#, r\1, text) text re.sub(r[\w\u4e00-\u9fff], , text) words jieba.lcut(text) return [w for w in words if w not in stopwords and len(w) 1 and not w.isdigit()] # 按小时聚合created_at是datetime df[hour] df[created_at].dt.floor(h) grouped df.groupby(hour)[text].apply(lambda x: .join(x)).reset_index() texts grouped[text].apply(preprocess).tolist() # 构建词典并过滤低频词 dictionary Dictionary(texts) dictionary.filter_extremes(no_below5, no_above0.5) corpus [dictionary.doc2bow(t) for t in texts] # 训练LDA主题数先取5 lda LdaModel(corpuscorpus, id2worddictionary, num_topics5, alphaauto, etaauto, passes20) for i, topic in lda.print_topics(num_words10): print(f主题{i}: {topic})filter_extremes的no_below5表示词频低于5的直接忽略no_above0.5表示在超过一半文档中出现的词忽略这两个参数能把“微博”“转发”这类泛化词剔掉。alpha和eta设为auto让模型自己估计主题稀疏度数据量小的时候比固定值效果好。passes是训练轮数微博数据通常15-30轮就够太大会过拟合。训练完先看每个主题的关键词。如果某个主题的词是“哈哈”“哈哈哈”“笑死”说明聚合和过滤不够回去加大no_below或扩充停用词表。3.3 主题数的确定困惑度、一致性还有人工看主题数num_topics是LDA里最伤脑筋的参数。困惑度perplexity理论上越低越好但对短文本不灵敏经常出现主题数越大困惑度一直降的情况。所以我一般用主题一致性coherence score来选常用c_v指标from gensim.models.coherencemodel import CoherenceModel scores [] for k in range(3, 11): model LdaModel(corpuscorpus, id2worddictionary, num_topicsk, alphaauto, etaauto, passes20) cm CoherenceModel(modelmodel, textstexts, dictionarydictionary, coherencec_v) scores.append((k, cm.get_coherence())) print(sorted(scores, keylambda x: x[1], reverseTrue))注意texts必须是原始分词后的列表不是corpus。运行这段会比较慢K从3算到10每次都要重新训练。经验值是微博经过聚合后主题数在4-7之间比较合适超过10个很难解释。另一个实用技巧是把主题可视化。pyLDAvis可以生成交互式HTML鼠标悬停能看到每个主题的词分布import pyLDAvis.gensim_models as gensimvis import pyLDAvis vis_data gensimvis.prepare(lda, corpus, dictionary) pyLDAvis.save_html(vis_data, lda_vis.html)人工看两个点主题间的重叠程度和主题内词的语义。两个主题完全重叠说明主题数太多某个主题里出现两个明显对立的子事件说明主题数太少。最后把每条微博的主题归属导回原DataFrame后续才能和情感分析做交叉。4. 情感分析混合方案词典、机器学习与多模态扩展4.1 情感词典做基础判断先把“一部分”结果跑出来情感分析在舆情项目里最简单、也最快见效的方式是基于情感词典打分。常见词典有BosonNLP情感词典、知网情感词库。做法是加载正向词和负向词对分词后的微博逐词加减分最后汇总成情感分pos_words set(open(positive.txt, encodingutf-8).read().split()) neg_words set(open(negative.txt, encodingutf-8).read().split()) def sentiment_score(text): words preprocess(text) score 0 for w in words: if w in pos_words: score 1 elif w in neg_words: score - 1 return score df[sentiment_dict] df[text].apply(sentiment_score) df[sentiment_dir] df[sentiment_dict].apply( lambda s: pos if s 0 else (neg if s 0 else neu))这种方式对“质量不错但是价格贵”这种转折句无能为力因为只看词袋没有考虑否定词和程度副词。可以加一层规则统计“不”“没”“太”等否定词反转其邻近词的情感极性。我一般会在预处理时保留一个词序列表然后滑动窗口扫描否定词这是动用规则增强的最低成本方案。词典跑出来的结果适合作为基线。不要直接拿它做最终输出因为对网络新词“破防”“离谱”完全失效。此时需要更重的模型。4.2 用SnowNLP与BERT做对比哪种更适合微博口语SnowNLP是一个轻量中文情感分析库默认模型偏向电商评论直接用在微博上效果一般。但API很友好适合小样本快速实验from snownlp import SnowNLP def snownlp_sentiment(text): return SnowNLP(text).sentiments # 0到1之间 df[sentiment_nlp] df[text].apply(snownlp_sentiment)sentiments大于0.5认为是正面小于0.5是负面正好等于0.5很难出现。问题在于SnowNLP对微博式反讽很迟钝比如“真的会谢”会被识别成正面。如果对准确率要求高就得用预训练模型。BERT系列目前在中文情感分析里是性价比最高的方案。用transformers库加载一个中文BERT情感分类模型from transformers import pipeline classifier pipeline(sentiment-analysis, modeluer/roberta-base-finetuned-jd-binary-chinese) def bert_sentiment(text): result classifier(text[:512])[0] return result[label], result[score] df[bert_label], df[bert_score] zip(*df[text].apply(bert_sentiment))注意这个模型是在京东评论上微调的领域和微博有差异。如果有标注数据更好的做法是在BERT基础上继续微调。标注500条微博评论就能显著提升效果800条以上基本稳定。没有标注时用多模型投票也是一种补救词典、SnowNLP、BERT三个结果取多数票。方法速度准确率微博可解释性适用阶段情感词典快中高基线冷启动SnowNLP快中低低快速原型BERT微调慢高中有标注后上线4.3 把情感分析接到舆情时间线上从单条评论到整体趋势单条微博的情感极性没有意义舆情分析要的是分布。把每条微博落在时间窗口内按小时聚合正向比例、负向比例和情感分均值就能画出随事件演变的情绪曲线。结合第3章得到的主题归属还可以进一步做“主题-情感”交叉df[hour] df[created_at].dt.floor(h) trend df.groupby([hour, sentiment_dir]).size().unstack(fill_value0) trend[pos_ratio] trend.get(pos, 0) / (trend[pos] trend[neg] trend[neu])这里的unstack会把正负中性变成三列pos_ratio表示每小时正面占比。如果某小时负面占比突然升高再结合那个小时的主题词就能定位是哪个子事件引爆的。另外微博自带的表情也是情感信号。现在多模态情感分析很热可以把图片表情和文本情感做融合但工程量大一般项目先把文本做好。5. 从源码到可复现结果舆情分析项目的目录组织与验证技巧5.1 项目目录怎么分别人拿到源码能直接跑一个标题里带“源码资料齐全”的项目别人拿到手第一件事是看它能不能跑。我建议把目录拆成四块spider、preprocess、analysis、scripts。spider里只放爬虫preprocess负责清洗、分词、聚合analysis放LDA和情感分析scripts放一次性的评估脚本。数据文件单独放data/不要散落在代码目录里。weibo_analysis/ ├── spider/ │ ├── search_spider.py │ └── comment_spider.py ├── preprocess/ │ ├── clean.py │ ├── aggregate.py │ └── stopwords.txt ├── analysis/ │ ├── lda_train.py │ ├── lda_visualize.py │ ├── sentiment_dict.py │ └── sentiment_bert.py ├── scripts/ │ ├── evaluate_lda.py │ └── evaluate_sentiment.py └── data/ ├── raw/ └── processed/raw目录放爬虫原始产物processed放聚合和清洗后的表。这样谁拿到都能从README.md里看到一条完整命令链先跑 spider再跑 preprocess最后跑 analysis。5.2 评估与分析的小技巧用混淆矩阵和主题-情感交叉表我见过有人跑半年LDA却拿不出一个数证明主题质量。验证主题最简单的方式是“人工标注-优先级”随机抽100条微博让两个人判断每条属于哪个主题然后和LDA结果对一下算F1。规模小但比只看困惑度可信得多。情感分析验证建议用混淆矩阵。如果标注了100条正负样本可以用这样一小段代码from sklearn.metrics import classification_report # y_true是人工标注, y_pred是模型结果 y_true [neg, pos, pos, neg, neu] y_pred [neg, neg, pos, neg, neu] print(classification_report(y_true, y_pred, target_names[neg, pos, neu]))classification_report会输出精确率、召回率和F1但注意y_true和y_pred必须按同样顺序排列。算完之后给交叉表加一个维度按主题分组看情感分布。pd.crosstab(df[topic], df[sentiment_dir])就能输出一张表某个主题如果负面占比显著高于平均水平那才是需要重点关注的子事件。这张交叉表也是舆情报告里最直观的输出。本文还有配套的精品资源点击获取
返回列表