基于Python的股吧评论情感分析与情绪时间序列可视化

📅 发布时间:2026/9/8 8:40:11
基于Python的股吧评论情感分析与情绪时间序列可视化
简介围绕“上证指数吧”评论数据这套资源提供完整的股票评论情感分析Python项目适合金融数据分析、自然语言处理入门者及量化投资爱好者用于从海量股吧评论中捕捉市场情绪并观察其随时间变化。项目既有网络爬虫脚本也有基于NLP的情感分类与时间序列分析流程能够帮助理解从数据采集、清洗标注到情感建模与趋势可视化的完整分析路径。压缩包为rar格式共6个文件以5个Python脚本为主体涵盖评论爬虫、数据标注、情感分类、趋势可视化等环节另含一个按时间整理的文本数据文件整体仅236KB结构紧凑。已有1835人学习下载适合快速上手体验。脚本覆盖数据清洗与标注、模型评估与优化等关键环节可帮助读者掌握金融舆情分析的基本范式也可在此基础上接入实时数据构建自己的市场情绪观察工具。 我一直觉得股市分析最缺的往往不是更复杂的模型而是对市场参与者情绪的第一手感知。做股票评论的情感分析尤其是像上证指数吧这种人气极高的股吧本质上就是在给市场的“心跳”做记录。这个项目把两个方向结合到了一起一是对海量、非结构化的中文股票评论做情感打分二是把分数按时间轴排列找出情绪和指数走势之间的微妙关系。简单说就是用爬虫抓取股吧帖子或评论跑一个情感分析模型然后按日粒度计算情绪均值、情绪分化度等指标最终得到一条能跟K线对比的情绪曲线。这篇文章我会把从数据获取、清洗、情感打分到时间序列可视化的完整过程都拆开讲适合想入门金融文本挖掘、或者正在找练手项目的Python学习者参考。1. 项目到底想解决什么问题从“乱糟糟的评论”里捞出“市场温度”1.1 为什么选上证指数吧而不是个股吧上证指数吧在东方财富社区里属于流量最大的几个吧之一日发帖量、回复量都是海量级别。选择它作为研究对象有几个很现实的好处一是样本量充足能保证按天聚合之后情感曲线不会因为样本太少而剧烈跳动二是它讨论的对象是大盘指数不是某一只个股受单一公司消息的干扰小情绪更能反映整体市场的状态三是指数本身有公开的行情数据方便后期做情绪和走势的对照分析这是很多个股吧不具备的便利条件。这种“公开数据 公开行情”的组合非常适合用来做情感分析和时间序列相关性的实验。它不需要你有Wind账号也不需要付费API一条爬虫加一个开源情感模型就能启动研究。1.2 情感分析和时间序列在这个项目里的关系很多初学者会把情感分析理解为“给一句话打一个正负面分数”这没错但在金融评论场景里单条评论的分数几乎是没意义的。一条评论被点踩、被淹没在评论区底部和一条被几百人回复的热帖影响权重天差地别。所以项目的核心思路不是“判断单条评论好坏”而是把大量评论在时间维度上做聚合形成情绪指数。情绪指数类似一个“市场体温计”把某一天的评论分数做平均得到日情绪均值把所有分数的标准差保留得到情绪分化度再用指数走势做对照看情绪拐点是否领先或滞后于指数拐点。这就是情感分析和时间序列结合的意义所在。2. 整体方案设计数据、模型、时间轴三件套2.1 技术选型为什么是Python 东方财富接口 SnowNLP我选用的技术栈非常朴素没有上大模型因为做时间序列聚合这种任务重点不在单句精度而在效率和稳定性。模块工具选择理由数据采集requests 东方财富股吧帖子接口该接口无需登录分页参数简单返回JSON格式解析成本低文本清洗re jieba过滤HTML标签、表情、用户、重复字符为后续打分降噪情感打分SnowNLP 或 金融领域微调模型SnowNLP开箱即用适合快速验证微调模型效果更好但需要标注语料时间序列处理pandas numpy按日resample、移动平均平滑、滚动波动率计算pandas天生擅长这种工作可视化matplotlib mplfinance情绪曲线和K线的叠加展示直观观察领先滞后关系SnowNLP默认模型是在电商评论上训练的说实话直接拿到股吧场景会有一定的偏置比如“涨停”在它眼里可能不是正面词“暴跌”在它眼里可能也不是特别负面。后面实操部分会专门讲怎么处理这个偏置问题常用的方式有两个一是准备一些股吧语料做模型微调二是在打分之后对极端值做缩尾处理不让模型偏差影响整体趋势判断。2.2 数据采集思路不是爬全文而是爬趋势有的朋友一上来就想把所有评论内容全抓下来我建议分阶段来。第一阶段只需要抓“标题 发布时间 热度指标阅读量/评论量”因为情感分析对内容长度不敏感但热度对情绪权重影响很大。热帖的标题往往就代表了一种强烈的市场观点比如“今天这根大阳线说明什么”“完了要破位了”这类标题本身已经足够表达情绪。股吧的帖子接口有一个显著特点它是按最后回复时间而非发帖时间排序的。这对做时间序列的人来说是个坑如果直接按列表页顺序抓抓下来的数据是“被顶起来的旧帖”不是当天的真实发帖分布。解决办法是抓取时带上发帖时间筛选参数或者抓下来后再按发帖时间过滤只保留目标时间窗口内的数据。3. 核心代码与实现细节3.1 第一步写一个“温和”的爬虫别把自己搞进黑名单这里说的温和指两个方面请求频率要控制别每秒几十次请求去打人家服务器数据量要克制不需要百万级样本每天均匀采集一部分就能稳定估算情绪趋势。下面是我常用的抓取函数核心是利用东方财富的帖子列表接口分页读取帖子标题和发帖时间。import requests import pandas as pd import time def get_guba_posts(page_no1, page_size50): url https://gbapi.eastmoney.com/api/Article/GetArticleList params { code: 000001, # 上证指数吧的代码 type: 1, # 1表示全部帖子 pageSize: page_size, pageIndex: page_no, sortType: 1, # 按发帖时间排序 callback: } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://guba.eastmoney.com/ } resp requests.get(url, paramsparams, headersheaders, timeout10) if resp.status_code ! 200: return pd.DataFrame() data resp.json().get(result, {}) posts data.get(list, []) df pd.DataFrame(posts) if not df.empty: df df[[post_title, post_publish_time, post_click_count, post_reply_count]] df.rename(columns{ post_title: title, post_publish_time: publish_time, post_click_count: click_count, post_reply_count: reply_count }, inplaceTrue) return df注意这里有两个关键细节sortType1表示按发帖时间排序这个参数一定要显式传否则默认按最后回复时间排会拿到大量旧帖。code000001是上证指数吧的代码这是东方财富内部的板块编码跟行情软件里的000001不是一回事直接替换成个股代码是拿不到对应吧数据的。采集端还需要做随机延时处理我一般用time.sleep(random.uniform(1, 3))既不给服务器造成压力也能降低被风控的概率。实测下来用这个频率跑一天能采集几万条帖子数据完全够做情绪分析了。3.2 第二步清洗文本把噪声挡在情感模型之前股吧文本的噪声类型和普通社交平台不太一样主要问题包括大量“顶”“支持”“666”等无意义短词股票代码和数字夹杂在中文里“今天000001真是坑”股民自创的谐音梗“韭菜”“站岗”“接盘侠”重复的标点符号和表情符号“”清洗策略不需要特别复杂的模型正则表达式就能解决大部分问题import re def clean_text(text): if not isinstance(text, str): return # 去掉URL text re.sub(rhttp\S, , text) # 去掉用户 text re.sub(r\w, , text) # 去掉HTML标签 text re.sub(r.*?, , text) # 去掉表情符号和特殊符号 text re.sub(r[^\u4e00-\u9fa5a-zA-Z0-9。、], , text) # 去掉重复标点 text re.sub(r([。、])\1, r\1, text) # 去除纯数字或过短内容 if len(text) 2: return return text.strip()清洗这一步的价值在于情感模型的输入越干净模型语义空间的判断越稳定。像“”这种噪声如果不清理SnowNLP会给整句分数产生难以预期的扰动日维度聚合时虽然会被平均掉一部分但碰到极端样本少的日期一条脏数据就能让当天得分偏离0.1以上。3.3 第三步情感打分以及怎么处理模型偏置清洗完文本之后就是情感打分。我之前试过几种方案包括直接使用SnowNLP、基于金融语料微调的BERT模型以及基于规则的情感词典。简单对比一下方案精度速度适用场景SnowNLP原版中等快快速验证、小规模数据SnowNLP 股吧语料微调较高快本项目推荐BERT金融微调模型高慢对精度要求较高的研究场景基于词典的规则方法低极快大规模数据的粗筛SnowNLP有一个特别方便的特性支持train()方法自定义训练喂给它两个语料文件一个是正面评论一个是负面评论就能微调模型权重。我攒了大概一万条人工标注的股吧评论训练效果在个股吧场景下有明显提升典型例子是“利好出尽”这种话原版模型判断为正面微调后能识别为负面因为股吧语境下这句话表达的往往是失望情绪。from snownlp import SnowNLP def sentiment_score(text): clean clean_text(text) if not clean: return None s SnowNLP(clean) return s.sentiments # 返回0~1之间的情感分越接近1越正面 df[sentiment] df[title].apply(sentiment_score) df df.dropna(subset[sentiment])这里有个值得注意的点sentiments输出是0到1之间的置信度不是严格意义上的概率更不是“看多概率”。0.6和0.4之间的差距在单条评论上没有解释意义但在几百条评论取平均以后0.01的差异也可能对应着情绪面的微妙转变。所以后续分析一定要基于聚合数据不要拿单条分数去做判断。3.4 第四步按时间维度聚合生成情绪指数情感打完之后真正的“随时间变化”分析才开始。这一步的核心操作就是pandas的resample聚合把每一秒都在产生的评论映射到“天”这个时间维度上。df[publish_time] pd.to_datetime(df[publish_time]) df.set_index(publish_time, inplaceTrue) # 按日聚合情绪指标 daily_sentiment df[sentiment].resample(D).agg([mean, std, count]) daily_sentiment.columns [sentiment_mean, sentiment_std, comment_count] # 做7日移动平均平滑消除周末和节假日带来的样本量波动 daily_sentiment[sentiment_ma7] daily_sentiment[sentiment_mean].rolling(7).mean() # 情绪分化度标准差越大说明市场分歧越大 daily_sentiment[deviation_ma7] daily_sentiment[sentiment_std].rolling(7).mean()我一般同时保留三个指标日平均情感分、情感标准差和评论总数。平均分描述市场情绪的中心位置标准差描述分歧程度评论总数描述关注热度。这三个维度合成起来看比单独看均值信息量要大得多。比如某一天平均分是0.45看起来偏负面但如果标准差只有0.1说明大家一致看空如果标准差是0.3说明多空分歧严重平均分代表的只是中间位置不能简单翻译成“市场情绪悲观”。这种多指标联动的分析才是时间序列情感分析真正有意思的地方。4. 实操过程中的踩坑记录与排查技巧4.1 高频问题速查表我在做这个项目的过程中踩过不少坑有些是数据层面的有些是模型层面的整理了一个速查表发给大家参考。问题表现原因与解决方案抓到的帖子都是旧帖当天数据量远小于预期排序参数是最后回复时间必须显式设置按发帖时间排序情感分数全部集中在0.4~0.6模型输出看不出区分度SnowNLP对短文本有天然的中性偏置需要微调模型或者在聚合时做极值缩放某一天样本量极少日情感均值突变剧烈节日、周末、非交易日样本量天然少需要用7日移动平均或者过滤低样本日期文本被截断部分帖子标题明显不完整接口本身有长度限制分析时不要过度依赖长文本短标题情绪表达往往更直接爬虫被封IP请求返回403请求频率太高增加随机延迟切换IP代理或降低数据采集量4.2 一个容易忽略的细节非交易日数据要过滤股吧评论虽然全年都在产生但交易日的评论样本量和非交易日完全不在一个量级。周六周日大家聊的往往是“下周怎么走”“有什么消息”情绪和盘面脱钩周一的情绪里则包含了周末两天的积累。这种结构性差异会直接影响时间序列的平稳性。我建议在聚合之前把非交易日的样本直接过滤掉或者做交易日重采样。最简单的办法是引入交易日历只保留交易日数据再画情绪曲线。否则周末的“假冷清”和周一开盘后的“假爆发”会在曲线上形成明显的锯齿干扰对真实趋势的观察。如果不是专门研究节假日效应直接过滤掉非交易日是最省心且最合理的选择。4.3 情感曲线和指数K线的对照怎么画才有信息量情感曲线单独看价值有限必须和指数走势叠加才能看出“谁领先谁滞后”。我习惯用mplfinance做蜡烛图把情感均值作为第二子图用fill_between填充0.5基准线上下的区域这样一眼就能看出情绪的翻转节奏。import matplotlib.pyplot as plt import mplfinance as mpf # daily_sentiment和index_data都按交易日对齐 plot_df pd.concat([index_data, daily_sentiment], axis1).dropna() # 先画K线再在下方子图画情绪均值 fig, axes plt.subplots( 2, 1, figsize(14, 10), sharexTrue, gridspec_kw{height_ratios: [3, 1]} ) mpf.plot( plot_df, axaxes[0], typecandle, volumeFalse, styleyahoo ) axes[1].plot(plot_df.index, plot_df[sentiment_ma7], colororange) axes[1].axhline(0.5, colorgray, linestyle--, linewidth1) axes[1].fill_between( plot_df.index, 0.5, plot_df[sentiment_ma7], whereplot_df[sentiment_ma7] 0.5, colorred, alpha0.3 ) axes[1].fill_between( plot_df.index, 0.5, plot_df[sentiment_ma7], whereplot_df[sentiment_ma7] 0.5, colorgreen, alpha0.3 ) plt.show()我在实际对照中还发现一个现象短线情绪均值往往和指数走势同步性很高但情绪标准差指标通常有提前见顶或见底的特征。也就是说市场还在涨但评论里的分歧度已经在悄悄放大这往往是行情进入分歧期的早期信号。这种领先关系不一定稳定但作为观察维度非常值得跟踪。5. 进阶思路情绪指数还能往哪个方向走到这里基础版的情感时间序列分析已经跑通了。如果你想让这个项目更有深度我建议往两个方向延伸。第一是构建更复杂的情绪状态指标不是简单看均值而是把均值、标准差、评论量组合成一个“情绪温度计”。比如用均值判断多空、用标准差判断分歧、用评论量判断关注度三个维度组合产生八种状态例如“高关注 强分歧 偏多”就是一种典型的顶部区域特征“低关注 低分歧 偏空”则可能对应缩量阴跌的低迷期。把这些状态编码后做转移概率矩阵可以研究情绪状态之间的切换规律。第二是把情绪序列和指数收益率做滞后相关分析看情绪是不是具备预测能力。这一步用pandas的shift函数就能实现核心是计算不同滞后天数下的相关系数或互信息。需要注意处理数据的平稳性一般对日收益率序列可以直接用但如果看价格序列要先做差分。我试过把情绪均值滞后3到5天后和指数日收益率做相关在某些时间段能到0.2以上的相关性已经远超随机水平了说明市场情绪确实包含了一部分未来价格的信息。刚开始做这个项目的人最容易掉进的误区是追求模型复杂度和单句准确率但实际上做时间序列情感分析数据质量、时间口径的一致性和特征构造的合理性远比模型本身的AUC重要得多。先跑通一个稳定、可重复的情感曲线生成流程后续再慢慢加花样这条路走起来会顺畅很多。回到实操层面这个项目最大的门槛其实不在算法在数据。今天把代码逻辑和踩坑细节都写清楚了你完全可以照着这套流程自己拉一份数据跑一遍把情绪曲线画出来之后再回来对照K线看那种“原来情绪提前发出了信号”的感觉会给你继续做下去的信心。本文还有配套的精品资源点击获取