ARTICLE DETAIL

资讯详情

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

微博数据抓取与LDA主题树图:爬虫、文本分析到可视化全流程

微博数据抓取与LDA主题树图:爬虫、文本分析到可视化全流程 简介面向毕业设计、期末大作业及课程设计场景的Python实战项目聚焦微博数据抓取、文本分析与可视化并以LDA主题模型树图呈现分析结果。项目代码注释完整结构清晰适合具备一定Python基础但需完整项目参考的初学者进阶使用。资源共53个文件压缩包约66.36MB涵盖20个Python源码如数据预处理、LDA建模、情感分析、聚类、可视化等模块、6个HTML可视化页面、模型文件与词典文件以及文档说明便于直接部署与二次开发目前已有179人学习下载。项目经严格调试可运行无忧除核心代码外还包含README说明、停用词表、微博评论与正文JSON数据、情感分析参考实现等覆盖从数据采集、清洗、建模到图表输出的完整链路。界面美观、操作简单能帮助快速完成高分毕设或课设并理解主题模型与文本可视化的实际落地方法。1. 微博数据抓取与LDA主题树图一条串起爬虫、文本分析、可视化的完整链路一条微博只有一百多字但几万条微博放在一起就能看出一个话题下面有几股完全不同的声音。这个项目做的正是这件事用Python把微博数据抓下来清洗分词后用LDA主题模型提炼话题结构最后用树图把主题-关键词-权重的层级关系画出来。它不只是一个课程作业舆情复盘、品牌声量监测、热点事件话题聚类底层逻辑都是这套链路。适合三类人有微博数据需求但还在手动复制粘贴的运营想用主题模型替代人工读帖的分析师以及需要把爬虫、文本分析、可视化串成完整闭环的Python学习者。整条链路踩坑点密集下面按采集、建模、可视化、排错的顺序拆开讲。2. 微博采集层从移动端接口到增量存储的落地写法2.1 为什么选移动端接口而不是网页版解析做微博采集第一反应往往是打开weibo.com看DOM结构然后上requests加BeautifulSoup去抓HTML。这个路径不是不行但网页版微博的HTML里内容渲染依赖大量动态脚本新闻流、侧边栏、推荐位混在一起解析成本高页面改版一次选择器就全废。常见做法是优先用微博移动端m.weibo.cn的JSON接口它返回的就是干净的结构化字段微博正文、发布时间、转发数、评论数、点赞数都在同一层。移动端接口的另一个优势是字段完整。一套cards返回里除了微博正文还包括转发数、评论数、点赞数、发布时间、来源客户端、是否含图片视频。这些字段在文本分析阶段用不到但在后续做主题热度排序时是现成素材比如互动量交叉分析就需要这些数字。如果一开始用网页版解析这些数值散落在各种class和data属性里想凑齐还得再写好几个正则。这里要说清楚一个边界移动端接口需要有效的登录Cookie而且搜索接口对深翻页有限制。以文本分析为目的的项目单个关键词抓几百到几千条就够建模使用这个量级移动端接口完全可以支撑。如果你是冲着把全站某关键词所有微博抓完去的那属于分布式爬虫的范畴得换Cookie池和代理池的方案和LDA文本分析这个目标是两回事。2.2 带Cookie的requests采集最小可跑通的代码先给出一段能直接改参数运行的采集代码目标是从搜索结果页拉取指定关键词的微博import requests import time import json # 登录微博网页版后从浏览器开发者工具里复制Cookie请求头 COOKIE 你的Cookie粘贴到这里 KEYWORD 芯片 def fetch_weibo(keyword: str, page: int 1) - list: 拉取移动端搜索接口的一页微博返回清洗后的字段 url https://m.weibo.cn/api/container/getIndex params { containerid: f100103type1q{keyword}, page: str(page) } headers { User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.0, Referer: https://m.weibo.cn/search?containerid100103type%3D1%26q%3D keyword, Cookie: COOKIE } resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() if data.get(ok) ! 1: print(接口返回异常:, data.get(msg)) return [] cards data.get(data, {}).get(cards, []) items [] for card in cards: # card_type9 是正常的微博内容卡片其余是广告位或推荐位 if card.get(card_type) ! 9: continue mblog card.get(mblog, {}) items.append({ id: mblog.get(id), text: mblog.get(text), created_at: mblog.get(created_at), reposts_count: mblog.get(reposts_count, 0), comments_count: mblog.get(comments_count, 0), attitudes_count: mblog.get(attitudes_count, 0) }) return items if __name__ __main__: all_data [] for page in range(1, 6): page_data fetch_weibo(KEYWORD, pagepage) all_data.extend(page_data) print(f第{page}页抓到{len(page_data)}条) time.sleep(2) # 控制频率避免触发风控 with open(weibo_raw.json, w, encodingutf-8) as f: json.dump(all_data, f, ensure_asciiFalse, indent2)代码逻辑说明fetch_weibo函数用requests.get访问移动端搜索接口containerid是接口约定的搜索容器格式q参数是关键词page是页码。返回的JSON里data.cards是一组卡片card_type9代表一条完整微博内容其余类型是广告位、热门推荐位等直接跳过。mblog就是微博正文对象取id、text、created_at和三个互动数字。每翻一页用time.sleep(2)做延时这是最基础的频率控制避免短时间高频请求触发风控。参数说明KEYWORD是搜索词中文词直接传requests会做URL编码timeout10建议保留超过10秒不返回就放弃这次请求避免网络抖动时线程卡死。注意不要无脑把page拉大搜索接口对深翻页有限制超过三十页左右返回的cards会变空这是接口策略不是代码问题。抓取量不够时优先换更多关键词而不是死磕一个词的深翻页。2.3 增量采集与字段清洗什么时候存JSON什么时候上数据库采集脚本跑完之后第一件事是去重。微博搜索接口翻页时偶尔会重复返回微博ID去重逻辑很简单维护一个id集合新id不在集合里才写入。增量采集的关键是把相对时间转成绝对时间因为搜索接口返回的created_at是x分钟前x小时前这类相对时间不转换没法判断新旧。import re from datetime import datetime, timedelta from html import unescape def parse_weibo_time(raw: str) - str: 把微博返回的相对时间转成绝对时间字符串 now datetime.now() raw raw.strip() if 刚刚 in raw: return now.strftime(%Y-%m-%d %H:%M:%S) if 分钟前 in raw: minutes int(re.search(r\d, raw).group()) return (now - timedelta(minutesminutes)).strftime(%Y-%m-%d %H:%M:%S) if 小时前 in raw: hours int(re.search(r\d, raw).group()) return (now - timedelta(hourshours)).strftime(%Y-%m-%d %H:%M:%S) if 昨天 in raw: return (now - timedelta(days1)).strftime(%Y-%m-%d %H:%M:%S) # 已经是绝对时间的直接返回 return raw def clean_weibo_text(raw_html: str) - str: 把mblog.text里的HTML标签、用户、链接清掉返回纯文本 text re.sub(r[^], , raw_html) text unescape(text) text re.sub(r[\u4e00-\u9fa5\w-], , text) text re.sub(rhttps?://\S, , text) text re.sub(r\s, , text).strip() return text代码逻辑说明parse_weibo_time用正则从相对时间里抽出数字再按分钟、小时、天折算成当前时间往回退的绝对时间。注意微博的刚刚有可能跨越午夜严格做法要读发布时间戳但对文本分析来说分钟级误差可以接受。clean_weibo_text先剥掉HTML标签再unescape还原实体然后删除用户和短链接最后折叠空白。存储层面的选择我一般遵循两个原则。数据量在几万条以内、只做一次性分析直接存JSON文件最省事脚本结束就能读如果是长期监控比如每天跑一次增量抓取就上SQLite用id做唯一索引重复写入靠INSERT OR IGNORE兜底。SQLAlchemy当然也能用专门管理爬虫数据的表结构但微博文本分析项目的重点在建模和可视化存储层用SQLite自带的标准库就够引入ORM反而增加依赖成本。2.4 采集频率与异常重试不要等封了再想办法移动端接口的风控阈值没有官方文档社区经验大致是单IP每秒不超过1次请求连续翻页超过五十页建议停几分钟。如果你的抓取任务量不大最简单的方案就是每页sleep 2秒这个速度一页十几条一分钟抓三百多条足够LDA建模用了。import time def fetch_with_retry(keyword: str, page: int, max_retry: int 3) - list: 带指数退避的采集函数网络抖动时自动重试 for attempt in range(max_retry): try: return fetch_weibo(keyword, pagepage) except requests.exceptions.RequestException as e: wait 2 ** attempt print(f第{attempt1}次请求失败: {e}, {wait}秒后重试) time.sleep(wait) return []代码逻辑说明requests.exceptions.RequestException覆盖了超时、连接错误、HTTP错误码等网络层异常每次失败后sleep时间按2的幂增长第一次2秒、第二次4秒、第三次8秒。参数说明max_retry3意味着最多尝试3次三次都失败就放弃这一页避免在接口已经拒绝服务时反复撞墙。注意这个重试只处理网络异常不处理业务层的ok0返回值——业务层的拒绝需要换Cookie或停下来不是重试能解决的。3. 文本预处理与LDA主题建模把微博变成主题分布3.1 jieba分词与停用词微博文本的三个特殊性微博文本和新闻稿、论文摘要最大的区别是短、碎、噪声多。一段八十字的微博可能包含emoji、话题词、用户、缩写、网络新词甚至表情包的图片占位符号。直接用通用停用词表处理你会得到大量真的、觉得、就是这类口语虚词混进主题里。分词环节我一般用jieba配合自定义词典和精确模式。jieba默认词典对网络新词的覆盖很差绝绝子yyds破防这类词会被切成单字需要在加载阶段挂上自定义词典。自定义词典的常见做法是把微博语料里高频出现的词单独收集成一个txt每行一个词再用jieba.load_userdict加载。停用词表建议在通用表基础上追加微博特有的噪声词转发、分享、视频、链接、网页链接、话题词占位符等。注意LDA对停用词非常敏感停用词不干净主题会被高频噪声词占据后面避坑章会展开讲。import jieba import jieba.analyse # 加载自定义词典文件格式词 权重 词性权重可省略 jieba.load_userdict(weibo_dict.txt) def tokenize(text: str, stopwords: set) - list: 对单条文本分词过滤停用词和单字 words jieba.lcut(text) result [] for w in words: w w.strip() if not w: continue if w in stopwords: continue if len(w) 2: continue result.append(w) return result代码逻辑说明jieba.lcut精确模式返回分词列表比paddle模式快适合批量处理stopwords是提前加载的集合从通用停用词表扩展而来过滤单字这个动作专门针对微博场景短文本里的单字绝大多数是虚词或分词残片丢弃后主题质量提升明显。jieba默认分词是一行行跑几万条文本大概十几秒不需要额外并行。3.2 gensim LDA建模主题数和迭代次数的调参逻辑分词完成后进入LDA建模。Python生态里gensim的LdaModel是文本分析项目的主流选择API稳定、支持中英文混排语料、导出主题分布方便。建模前需要构建字典和词袋语料。from gensim import corpora, models def build_lda(tokenized_docs: list, num_topics: int 8, passes: int 10): 从分词文档列表构建LDA模型返回模型、字典和语料 dictionary corpora.Dictionary(tokenized_docs) # 过滤极端词出现少于2次或出现在超过70%文档里的词 dictionary.filter_extremes(no_below2, no_above0.7) corpus [dictionary.doc2bow(doc) for doc in tokenized_docs] lda_model models.LdaModel( corpuscorpus, id2worddictionary, num_topicsnum_topics, passespasses, random_state42, alphaauto, etaauto ) return lda_model, dictionary, corpus逻辑说明Dictionary把每个词映射成整数IDfilter_extremes是必做的一步——no_below2把出现少于2次的词删掉干掉拼写错误和生僻词no_above0.7把出现在超过70%文档里的词删掉比如微博转发这类全语料通用词doc2bow把分词列表转成词袋向量。参数说明num_topics是LDA里唯一需要人工定的核心参数表示想从语料里挖出几个主题。微博短文本场景、三千到五千条语料建议从6到10起步不要一次设到30——主题数太多时每个主题只剩两三个词可读完全失去业务价值。passes是模型迭代次数10是经验起点数值越大主题越稳定但耗时线性增长。random_state42固定随机种子保证每次运行结果一致调参阶段不固定种子的话同一组参数两次跑出的主题顺序是乱的。alpha和eta设为auto让模型自动学习先验分布对中小语料比固定值更稳。3.3 困惑度与主题一致性两个指标怎么配合LDA调参最怕玄学。主题数设多少才算好单纯靠人眼读主题列表判断同一组结果两个人能得出不同结论。可靠的做法是用两个量化指标配合判断。困惑度衡量模型对语料的拟合程度数值越低模型对语料的解释力越强。但困惑度对主题数有单调下降趋势主题设越多困惑度越低拟合越好却不一定越可解释因此需要主题一致性兜底。主题一致性基于词共现计算值越高代表主题内部的关键词在语料里越常一起出现语义越紧凑。from gensim.models import CoherenceModel def evaluate_lda(lda_model, corpus, dictionary, tokenized_docs, topn10): 计算困惑度和主题一致性用于对比不同主题数的效果 perplexity lda_model.log_perplexity(corpus) coherence_model CoherenceModel( modellda_model, textstokenized_docs, dictionarydictionary, coherencec_v, topntopn ) coherence coherence_model.get_coherence() return perplexity, coherence逻辑说明log_perplexity返回对数困惑度同一语料上不同主题数的模型之间可以横向对比CoherenceModel的c_v指标计算主题关键词在原始文本里的共现强度值在0到1之间。指标作用经验阈值局限困惑度衡量模型拟合度越低越好对主题数单调下降不能单独用c_v一致性衡量主题语义清晰度大于0.4可用计算较慢适合中小语料我实际调参的做法把num_topics从4跑到20每隔2取一个值每个值算一次困惑度和一致性画一条折线。选择标准是一致性曲线右端趋于平缓且困惑度上升可接受的拐点位置而不是一致性最高的点——一致性最高往往对应极少的主题数把大量话题压在一起反而失去分析价值。时间有限时直接固定num_topics6或8先看一眼主题词有没有重样再决定调大还是调小。4. 树图可视化把LDA主题分布画成三层结构4.1 为什么主题结果适合用树图而不是词云LDA建模输出的是一堆主题每个主题带一串关键词和权重。直接展示的方式有很多词云、条形图、气泡图。词云只能展示全局高频词看不出主题边界条形图一次只能展示一个主题主题量多了要翻很多页。树图的优势是天然支持层级——根节点是这个话题第二层是LDA发现的各个主题第三层是每个主题的Top关键词及权重。用户一眼就能看出话题下面有几股主要声音、每股声音的代表词是什么。更重要的一点是树图在可解释性上比其他图表更适合非技术读者。舆情分析报告里读者可能完全不懂LDA但他能看懂这个话题分成五类第一类的关键词是这些。树图把模型输出翻译成了业务语言。项目里用的pyecharts Tree组件是echarts的封装渲染成独立HTML文件不用起服务双击就能在浏览器打开适合作为报告附件直接交付。树图和词云并不冲突常见的报告排版是左边放树图展示主题结构右边放词云展示全局高频词。词云承担氛围感读者一眼看到最大字号的关键词就知道这个月在聊什么树图承担结构感读者能看清话题分成几类每类的关键词集合是什么。两个图配合使用比单图信息量大很多这是做舆情报告时的常见布局。4.2 构造树图数据从LDA输出到树形JSONLDA模型直接输出的是(词, 权重)元组列表。树图需要的是{name: ..., children: [...]}的嵌套结构。转换逻辑分三步根节点命名主题层取show_topics结果每个主题下挂关键词节点。def build_tree_data(lda_model, num_topics: int, topn: int 8) - dict: 把LDA主题-关键词-权重转成pyecharts树图需要的嵌套结构 topics lda_model.show_topics( num_topicsnum_topics, num_wordstopn, formattedFalse ) root { name: 微博话题, children: [] } for topic_id, words in topics: topic_node { name: f主题{topic_id 1}, children: [] } for word, weight in words: topic_node[children].append({ name: f{word} ({weight:.2f}), value: round(float(weight), 3) }) root[children].append(topic_node) return root代码逻辑说明show_topics里的formattedFalse让模型返回原始的(词, 权重)元组而不是带词*权重格式的格式化字符串便于程序读取每个主题节点命名主题1、主题2关键词节点把权重写进名称里这样树图不用额外布局就能直接看到权重数值value字段保留数值供后续按权重排序或点击事件使用。参数说明topn8表示每个主题展示8个关键词这个数值不要贪大——树图上节点越多层级越深超过12个关键词后子节点会拥挤到标签互相遮挡。需要按主题整体热度排序的话可以在树图数据里的主题节点上补一个value字段但要注意这里value在pyecharts里会影响节点大小如果只想排序不想改节点大小建议在名称里加序号而不是靠value。这里有个容易被忽略的细节主题名称直接叫主题1、主题2非技术读者看的时候要来回对照关键词才能记住哪个是哪个。建议在树图数据生成后人工读一遍每个主题的关键词给它起一个业务名比如主题1改为价格讨论、主题2改为供应链缺货。改造方式很简单建一个映射字典替换topic_node的name字段就行成本很低但报告可读性提升很大。4.3 pyecharts树图渲染与页面参数调整pyecharts渲染树图有两条路render()生成HTML文件或者直接输出到Jupyter Notebook。这里用render()生成独立HTML方便在项目里作为静态报告交付。from pyecharts import options as opts from pyecharts.charts import Tree def render_tree(tree_data: dict, output_path: str weibo_lda_tree.html): 用pyecharts把树图数据渲染成交互式HTML tree ( Tree(init_optsopts.InitOpts(width1200px, height800px)) .add( series_name微博LDA主题树图, data[tree_data], layoutorthogonal, orientLR, initial_tree_depth2, label_optsopts.LabelOpts( positionleft, font_size12 ), line_style_optsopts.LineStyleOpts( color#666, width2, type_curve ) ) .set_global_opts( title_optsopts.TitleOpts(title微博文本LDA主题树图) ) ) tree.render(output_path)渲染逻辑说明Tree组件接受data列表里面放一个根节点字典layoutorthogonal是正交布局配合orientLR让父节点在左、子节点向右展开中文标签在LR方向从左往右读最自然initial_tree_depth2让页面加载时默认展开到第二层关键词层留给用户点击展开避免首次加载节点过多页面卡顿。参数说明width和height控制画布尺寸1200x800适合大多数显示器直接打开line_style_opts里type_curve让连线变曲线美观但性能略低节点超过两百个时建议改回折线label_opts的positionleft对LR方向的树是必要的否则标签会叠在节点上方。如果树图里的关键词名称过长把font_size调小到10或者把名称里的权重和词拆到两行展示。另外pyecharts的Tree组件还有个常见坑data列表里如果只有一个根节点部分版本会渲染异常需要在根节点外面再套一层数据节点。做法是data[{name: 数据, children: [tree_data]}]这样等于是四级树根节点变成顶层的数据不影响展示逻辑但能绕开渲染bug。如果要把树图嵌进更大的报告页面常见做法是pyecharts的Page组件把多个图纵向排列或者把生成的HTML用iframe嵌到Flask项目里。对这个源码项目来说独立HTML文件已经是可交付的产物放在报告目录里随文档一起发即可。5. 微博文本分析与LDA落地避坑五个高频翻车点5.1 采集静默失败Cookie失效后接口返回ok0现象脚本跑了两天都正常第三天拉取数据时items一直返回空列表但程序不报错、不退出日志里只有第3页抓到0条。原因微博Cookie有有效期失效后接口不再返回正常数据而是返回ok0的错误响应。代码里只对card_type9做过滤错误响应里没有任何卡片自然得到空列表。这个坑最难查的地方在于它长得像没有数据不看接口原始返回根本发现不了。解决在fetch_weibo函数里增加对data.get(ok)的显式检查ok不等于1直接抛异常而不是返回空列表把接口错误和确实没有数据区分开if data.get(ok) ! 1: raise RuntimeError(f接口返回错误: {data.get(msg)}请检查Cookie是否过期)改成抛异常后采集脚本会在Cookie失效的第一时间报错退出而不是默默写完一批空数据。经验是采集任务挂定时器之前先手动跑一次确认Cookie有效长期任务里建议把最后一次成功采集的时间写到日志里超过30分钟没有增量就触发检查。5.2 分词结果全是单字网络新词被切碎现象分词结果里绝绝子变成绝/绝/子破防变成破/防LDA主题里全是单字词主题语义完全读不出来。原因jieba默认词典基于新闻语料训练网络新词覆盖严重不足。微博是网络新词的高发区新词被默认词典按单字切分的概率极高。解决建一个微博专属自定义词典把语料里的高频新词手动收集进去。不用一次收全跑完第一批分词后把结果里高频的单字碎片拿出来人工确认确认是词的加进weibo_dict.txt# weibo_dict.txt 示例内容格式词 词频 词性词频可省略 绝绝子 10 破防 8 yyds 10 纯爱战神 5词典文件每行一个词jieba.load_userdict加载后再次分词的切分结果就会改变。注意网络新词更新快这个词典要随语料持续维护跑一次新关键词补一次词。5.3 LDA主题被转发、视频、链接塞满停用词没做干净现象num_topics设为8打印主题发现其中三个主题几乎一样核心词都是转发、微博、视频、链接、网页。原因采集的微博正文里自带转发微博视频加载中网页链接这类平台占位文本清洗环节没处理干净。这些词在语料里出现频率极高LDA会把它们当成主题的核心词。解决在通用停用词表基础上追加微博平台噪声词至少包括转发、微博、视频、链接、网页、客户端、话题、分享、图片、长图、收起、展开。注意追加要用扩展集合的方式不要用通用停用词表直接覆盖——通用表里没有转发这个词覆盖后等于没加。清洗函数里对转发微博这个固定组合做整体删除单独删转发会把转发量这类有效信息也删掉。怎么判断停用词没做干净打印LDA主题词之后如果同一个词出现在两个以上主题且排前三优先怀疑它是噪声词如果主题之间区分度很低、词重叠严重先用停用词过滤再重新训练十次里有八次是噪声词在捣乱。5.4 树图生成HTML但页面空白pyecharts版本与资源加载现象render_tree跑完生成了HTML打开浏览器是空白或者控制台报JavascriptException: javascript error。原因pyecharts在1.x之后API变化较大Tree组件的data参数在老版本里要求传字典新版本要求list包字典传错直接白屏。另一个高频原因是用render()生成的HTML引用了远程JS资源目标机器没有外网或CDN被墙时页面无法加载echarts库。解决第一步固定版本pyecharts用1.9.1或2.x系列不要用0.5.x老版本老版本的Tree接口和数据格式完全不兼容。排查版本最快的方式是在命令行跑pip show pyecharts确认版本号再看导入的Tree组件参数是否与版本对应网上搜到的旧教程参数名可能是tree.add(dataxxx)这种写法在新版本里已经改掉直接照抄容易翻车。第二步检查生成的HTML文件头尾确认script标签里src的路径。内网部署时把echarts.min.js下载到项目本地并设置from pyecharts.globals import CurrentConfig CurrentConfig.ONLINE_HOST ./js/把本地js目录放到和HTML同级位置就行。不是内网环境的话用官方默认CDN最省事。5.5 存储文件里全是\uXXXX转义中文读取乱码现象采集的JSON文件用记事本打开全是\u6587\u672c或者程序读的时候中文乱码。原因json.dump时没传ensure_asciiFalsePython默认会把非ASCII字符转成\u转义序列落盘另一个原因是读取JSON文件时没指定encodingutf-8在Windows默认GBK环境下读UTF-8文件直接乱码。解决写入和读取都固定参数。写入用json.dump(all_data, f, ensure_asciiFalse, indent2)读取用open(weibo_raw.json, r, encodingutf-8)。项目里所有文件读写统一指定UTF-8不要依赖系统默认编码。另外清洗文本时把emoji过滤掉否则emoji会被jieba当成词元参与建模LDA主题里出现一堆表情符号占位符import re def remove_emoji(text: str) - str: 过滤emoji和特殊符号避免进入LDA词表 emoji_pattern re.compile( r[\U0001F300-\U0001FAFF] r|[\U00002600-\U000027BF] r|[\U0001F900-\U0001F9FF] ) return emoji_pattern.sub(, text)编码问题不解决后续所有环节都会跟着乱提前在采集落盘时把UTF-8这件事固定下来是整个项目里性价比最高的一个动作。6. 让主题结果产生业务价值时间切片对比与报告页生成LDA模型的输出不能只停在画出树图这一步。实际做舆情分析时最有价值的是时间维度上的主题变化——同一个关键词上周和这周的讨论主题可能完全不同。做法是把语料按周或月切片每个时间段单独训练LDA再对比主题分布。切片逻辑在采集阶段就要做好每条微博存了绝对时间字符串处理时用前7位做月份分组。from collections import defaultdict def train_by_month(raw_items: list, month_key: str 2025-03) - dict: 按月份切分训练LDA返回该月份的树图数据 month_docs defaultdict(list) for item in raw_items: m item[created_at][:7] # 按yyyy-mm截取月份 if m month_key: month_docs[m].append(tokenize(item[text], stopwords)) month_model, _, _ build_lda(list(month_docs.values()), num_topics6) return build_tree_data(month_model, num_topics6)这样每个月生成一张树图文件名按时间命名报告页里并排展示主题迁移一眼就能看出来——比如上个月主题还是价格、发布这个月变成缺货、延期业务侧就能及时跟进。我做这类项目有个习惯先把单次采集的完整链路跑通再封装重复性操作最后才考虑做成Flask服务或可视化大屏。先拿小语料验证模型是否可解释再扩数据量。那些炫酷的大屏背后主题建模不扎实的话全是装饰。这个方向值不值得投入如果只是交作业跑通链路即可如果要服务真实业务建议把更多时间花在停用词维护和主题调参上这两件事决定LDA的输出能不能被业务读懂树图只是把结果呈现出来。源码项目里如果带了文档说明最该写清楚的就是环境版本和Cookie获取步骤这是别人复现时卡壳最多的地方。希望这篇笔记能帮你在从能跑走向能用的路上少走弯路——那些失效Cookie和\uXXXX转义坑过我太多次了提前绕开它们项目推进会顺很多。本文还有配套的精品资源点击获取
返回列表