尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
用Python分析上市公司舆情与股票相关性实战指南
简介一套面向金融数据分析学习者的 Python 项目源码围绕上市公司网络舆情与股票价格的相关性分析展开。项目以新闻文本采集为起点完成舆情评分、股票行情数据处理、相关性计算与结果可视化覆盖数据获取、清洗、建模到出图的完整流程尤其适合具备 Python 基础并想接触舆情分析或量化关联研究的读者。压缩包内共10个文件包含5个Python脚本对应数据采集、舆情评分、关联分析与可视化等模块、2个Excel数据表股票及舆情得分数据、2个文本说明建表语句与爬取注意事项、1个使用文档整体大小仅136KB结构精简、便于快速查阅。源码附有详细注解并配有运行截图可辅助理解爬虫解析、数据存储和相关性计算等关键环节适合作为课程设计、毕业设计或入门实践的参考模板。目前已有6300人学习下载可借此熟悉舆情评分与股票联动分析的实现思路。1. 上市公司网络舆情与股票相关性分析这个Python项目到底在做什么很多人以为“网络舆情和股价相关”只是一句股评真正用Python做过上市公司舆情与股票相关性分析的人知道这是一条完整的数据流水线采集新闻和股吧评论、用情感模型打分、按交易日把分数和收益率对齐、再算相关系数。任何一个环节偷懒算出来的相关性都可能是假信号。这个方向的项目源码通常由舆情采集、情感计算、行情拉取、相关性统计、可视化五块组成配上文档、注解和运行截图正好让想复现的人对每一步输出做校验。适合两类人一是刚入门量化分析的Python使用者需要一条可改可跑的数据通路二是做事件驱动研究的金融从业者想快速验证“消息面”对个股收益的影响。2. 架构与数据准备舆情爬虫和股票行情先跑通再谈相关性2.1 项目目录与模块拆解先看清五个文件各自负责什么这类项目最常见的目录结构是这样的sentiment_stock_analysis/ ├── config.py # 股票代码、日期范围、抓取页数等参数 ├── data_fetcher.py # 舆情文本抓取 股票日线拉取 ├── sentiment.py # 情感打分与舆情指标聚合 ├── correlation.py # 时间对齐、滞后计算、相关系数 ├── main.py # 把上面的模块串成一条主流程 ├── requirements.txt # 依赖清单 └── docs/ ├── 设计说明.md └── 运行截图/ # 每一阶段的输出图我一般会建议先读config.py和main.py运行截图是对照物舆情抓取阶段对应“新闻条数统计图”情感分析阶段对应“情感分数分布直方图”最后相关性阶段对应“舆情曲线与股价曲线双轴图”。每张截图里的数字就是你在对应函数里打印出来的中间结果排查时能快速定位到底哪一步和预期不一致。依赖环境上Python 3.9以上版本即可核心包是requests、pandas、beautifulsoup4、snownlp、akshare、matplotlib。按Python安装教程准备好解释器后在项目目录执行pip install -r requirements.txt。这里有个细节akshare对pandas版本有较高要求如果本机pandas过旧安装akshare时会自动升级可能连带影响其他脚本兼容性后面避坑章节细说。2.2 舆情数据采集东方财富资讯接口的抓取与清洗常见的做法是抓东方财富的公开搜索接口按股票名称或代码去查相关新闻标题返回JSON格式比解析HTML页面稳定得多。它的返回是jsonp包裹需要先剥离回调函数再转JSON。import requests import re import json import pandas as pd def fetch_news(stock_name: str, pages: int 3) - pd.DataFrame: 按股票名称/代码抓取东方财富资讯标题 stock_name: 如 招商银行 或 600036 pages: 抓取页数每页约50条3页约150条 base_url https://search-api-web.eastmoney.com/search/jsonp headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)} rows [] for page in range(1, pages 1): param json.dumps({ uid: , keyword: stock_name, type: [cmsArticleWebOld], client: web, clientType: web, clientVersion: curr, param: { pageSize: 50, pageIndex: page, preTag: em, postTag: /em, }, }, ensure_asciiFalse) params {param: param, callback: cb} resp requests.get(base_url, paramsparams, headersheaders, timeout10) # jsonp 返回形如 cb({...})剥掉外层才是标准 JSON raw resp.text json_match re.search(rcb\((.*)\)$, raw, re.S) if not json_match: continue data json.loads(json_match.group(1)) items data.get(result, {}).get(cmsArticleWebOld, []) for item in items: rows.append({ title: item.get(title, ), publish_time: item.get(date, ), }) if not items: break # 没有更多内容时提前结束 df pd.DataFrame(rows) df.drop_duplicates(subsettitle, inplaceTrue) df[publish_time] pd.to_datetime(df[publish_time]) return df代码逻辑不复杂每次请求50条资讯循环翻页把标题和发布时间收集成DataFrame。最容易被新手卡住的是jsonp解析——直接json.loads(resp.text)一定会报错因为外面包了一层函数调用必须先用正则剥离。去重按标题做drop_duplicates防止同一篇稿件被多个栏目重复收录。提示不要把jsonp返回直接交给json.loads这是复现时最容易报错的一行。参数说明里最值得调的是pages。150条资讯对做日级别相关性够了想做盘中级别要另外找分钟级舆情源。接口的keyword传股票代码往往比传名称更准因为“招商”“银行”这类词容易被泛匹配成其他公司新闻。2.3 股票行情对齐用akshare拉取前复权日线股票日线数据我用akshare免费、无需注册token对A股支持很全。import akshare as ak def fetch_daily(stock_code: str, start_date: str, end_date: str) - pd.DataFrame: 拉取个股前复权日线 stock_code: 6位代码如 600036 start_date/end_date: YYYYMMDD 格式 df ak.stock_zh_a_hist( symbolstock_code, perioddaily, start_datestart_date, end_dateend_date, adjustqfq, # 前复权 ) df.rename( columns{日期: date, 收盘: close, 涨跌幅: pct_chg}, inplaceTrue, ) df[date] pd.to_datetime(df[date]) return df[[date, close, pct_chg]]adjust参数建议固定为qfq。如果不复权分红送股当天价格会人为向下跳空收益率变成负的而舆情并没有变差相关性就被污染了。pct_chg是接口算好的涨跌幅后面算收益率优先用这一列而不是用close自己pct_change因为接口的涨跌幅考虑了停牌和复权边界。3. 情感打分与舆情指标把新闻标题变成能进模型的分数3.1 SnowNLP中文舆情打分的最小可用模型舆情文本要变成数值常见选择是SnowNLP的sentiments属性。它基于贝叶斯分类训练输出0到1之间的情感概率越接近1越正面越接近0越负面。优点是安装方便、单条文本打分毫秒级缺点也明显训练语料偏向电商评论对金融术语判断经常别扭。比如“股价放量下跌”容易被“下跌”二字主导而对“业绩预增”反而可能因“预增”判正面整体属于“够用但不够准”的基线模型。from snownlp import SnowNLP def sentiment_score(text: str) - float: 对单条新闻标题做情感打分 返回 0~1 的浮点数 if not text or len(text.strip()) 0: return 0.5 # 空文本按中性处理 return SnowNLP(text).sentiments打分完成后把它应用在新闻DataFrame上def add_sentiment_score(df: pd.DataFrame) - pd.DataFrame: 给新闻数据加一列情感分数 df[score] df[title].apply(sentiment_score) return df这里有一个容易忽略的细节空文本或纯数字标题SnowNLP会返回0.0而不是中性的0.5。如果不做过滤那些“6”“12”“复盘”之类的异常标题会把当天情感均值拉低。我在生产脚本里保留一层过滤只保留标题长度大于4、且包含至少一个汉字的记录再参与打分。3.2 舆情指标构造热度、均值、波动三项计算单条新闻的分数没有统计意义要按交易日聚合。常用的舆情指标有三个当日新闻条数热度、当日情感均值情绪方向、当日情感标准差分歧度。def build_sentiment_features(df: pd.DataFrame) - pd.DataFrame: 按发布日期聚合舆情指标 返回date, news_count, sentiment_mean, sentiment_std df[date] df[publish_time].dt.date grouped df.groupby(date).agg( news_count(score, count), sentiment_mean(score, mean), sentiment_std(score, std), ).reset_index() grouped[date] pd.to_datetime(grouped[date]) return grouped三个参数的坑要分别说。news_count用的是count它会跳过NaN如果打分函数返回Nonecount会变小而不报错所以打分函数要么返回float要么返回0.5不要返回None。sentiment_std在当天只有一条新闻时会变成NaN会让后续相关系数计算自动剔除该行想保留它用fillna(0)填成0表示当天没有分歧。这四个字段就是后面和股价做相关性的“舆情侧输入”。3.3 主流程串联main.py里各函数的调用顺序有了抓取、打分、聚合这几个函数主流程就很短了import config def main(stock_code: str, stock_name: str, start_date: str, end_date: str): # 第一步抓舆情 news_df fetch_news(stock_name, pagesconfig.NEWS_PAGES) news_df add_sentiment_score(news_df) sent_df build_sentiment_features(news_df) print(舆情数据范围, sent_df[date].min(), →, sent_df[date].max()) print(sent_df.tail(10)) # 第二步拉行情 stock_df fetch_daily(stock_code, start_date, end_date) print(行情数据范围, stock_df[date].min(), →, stock_df[date].max()) # 第三步相关性 result correlation_analysis(stock_df, sent_df, max_lag5) for lag, corr in result.items(): print(f舆情提前 {lag} 天相关系数 {corr:.4f}) if __name__ __main__: main(stock_code600036, stock_name招商银行, start_date20240101, end_date20241231)为什么舆情用“招商银行”而行情用“600036”两个不同键资讯搜索接口按名称召回效果更好行情接口只认6位代码两者用不同的键去查数据。这正是复现者常踩的坑拿代码去搜新闻返回的是讨论区帖子拿名称去拉行情又会被akshare拒绝。config.py里把两个参数分开配置就是为了避免混用。4. 相关性分析算相关系数前先解决时间和滞后问题4.1 时间对齐merge on date 才是正确的姿势舆情聚合结果是自然日行情是交易日。直接用concat或append会得到一堆空值正确做法是按date做inner merge。def align_data(stock_df: pd.DataFrame, sent_df: pd.DataFrame) - pd.DataFrame: 将舆情日频指标与行情按交易日对齐 以行情交易日做主表inner 会自然过滤非交易日 stock_df[date] pd.to_datetime(stock_df[date]) sent_df[date] pd.to_datetime(sent_df[date]) merged pd.merge(stock_df, sent_df, ondate, howinner) merged.sort_values(date, inplaceTrue) return mergedinner join在这里是刻意的。它把没有行情的日期周末、节假日、停牌日自动排除避免舆情数据占位而股价缺失导致相关系数被NaN干扰。建议在merge前打印两边的日期范围如果舆情范围远小于行情范围说明抓取页数太少如果舆情范围里有大量周末日期属于正常现象merge后这些日期会消失。4.2 滞后计算舆情领先股价几天才有信号多数新闻在收盘后或盘中发布对当天股价的影响是“部分已反映”所以舆情信号对收益率的领先性比同步性更值得检验。def correlation_analysis(stock_df, sent_df, max_lag: int 5) - dict: 计算舆情得分序列与个股日收益率的相关系数 max_lag: 测试舆情领先股价多少天 shift(-lag) 把舆情序列整体向前移 lag 天 merged align_data(stock_df, sent_df) merged[ret] merged[pct_chg] / 100.0 results {} for lag in range(0, max_lag 1): col_name fsentiment_lag_{lag} # shift(-lag) 表示把第 t 天的舆情拉到第 t-lag 行 merged[col_name] merged[sentiment_mean].shift(-lag) corr merged[[col_name, ret]].corr(methodpearson).iloc[0, 1] results[lag] corr return results这里的shift方向容易搞反。shift(-lag)的含义是把当天的舆情分数与lag天前的收益率配对。lag1时就是用昨天舆情预测今天收益。如果你发现lag0相关性最高说明舆情和涨幅同一天市场已经消化了消息如果lag2更明显说明舆情领先两个交易日存在可利用的信息滞后窗口。4.3 相关系数选型Pearson和Spearman怎么取舍Pearson假设线性关系且受极端值影响大。股价收益率不是正态分布常见厚尾某天突发跌停造成的大幅负收益可能一票把Pearson拉高。这种情况用Spearman更稳它算的是秩相关只关心排序关系。corr merged[[ret, col_name]].corr(methodspearman).iloc[0, 1]实际项目里我会把两种都算出来对比。如果Pearson显著但Spearman不显著说明相关性靠少数极端值撑着不可靠两者都显著才认为舆情与收益的关系是稳定的。相关系数的显著性可以用scipy.stats.spearmanr直接输出p值样本量少于30时即使相关系数到0.3也达不到显著水平别急着下结论。4.4 可视化验证双轴图怎么画舆情与股价的关系相关系数是数字但要向同事说明白还得画图。舆情分数0到1股价几十到几百必须用双轴。import matplotlib.pyplot as plt def plot_sentiment_vs_stock(merged: pd.DataFrame): 双轴图左轴股价右轴舆情得分 fig, ax1 plt.subplots(figsize(14, 5)) ax1.plot(merged[date], merged[close], colorblack, linewidth1.2, labelclose) ax1.set_ylabel(close price) ax2 ax1.twinx() ax2.plot(merged[date], merged[sentiment_mean], colorsteelblue, linewidth1.0, labelsentiment) ax2.set_ylabel(sentiment score) ax2.set_ylim(0, 1) fig.autofmt_xdate() fig.legend(locupper left) fig.tight_layout() plt.show()fig.autofmt_xdate()必须加否则日期标签会全部叠在一起。set_ylim(0, 1)把舆情分数限制在真实取值范围不然部分分数贴边时图形会显得太平。还有一个细节如果区间内发生除权除息close曲线会跳变要确认给akshare传的是qfq否则图上会有一段悬崖那不是舆情造成的。5. 复现这个项目最常踩的5个坑现象、原因、解决5.1 资讯接口突然返回空列表现象前一周还能抓到新闻某天开始fetch_news返回空DataFrame程序不报错。原因东方财富的搜索接口偶尔调整参数结构最常见的是param里的type字段改枚举值或者callback名字变化旧参数组合直接查不到数据。解决先在浏览器里手工复制一处搜索URL对照新参数改配置同时给fetch_news加一个fallback逻辑当items为空时自动改用备用接口。不要试图把解析逻辑写死在一个版本上金融数据网站的改动频率比想象中高。5.2 SnowNLP对金融文本的情感判断总“反着来”现象“公司发布减持公告”被打了0.8分正向“股价创十年新高”反而0.3分负向。原因SnowNLP训练语料以购物评论为主“减持”里含“减”字容易被联想成“减少”这种中性偏负的词而“新高”这种无语气词汇它识别不了情绪极性。解决不要用单一模型扛金融场景。初级方案是维护一份金融否定/敏感词表命中后手动修正分数例如得分大于0.7但标题含“减持、处罚、立案、亏损”时改为0.3进阶方案是用雪球评论语料微调一个二分类模型但工程量大适合后续迭代不建议第一次就上。5.3 停牌和节假日导致时间轴错位相关性出现假信号现象算出来的相关系数高达0.8但画图后发现舆情日期和股价日期对不上差了好几天。原因舆情按自然日聚合行情按交易日直接按行号对齐而不是按日期对齐停牌日、节假日会把后续数据整体错位。解决严格按date字段做inner merge不要用reset_index后的行位置对。每次merge后打印长度校验合并后的行数约等于行情交易日数减去舆情缺失的天数。如果行数等于舆情天数而大于行情天数一定是merge对象用反了。5.4 样本量太小相关系数虚高现象只抓了20天新闻Pearson算出0.75换一段日期再算变成-0.2。原因20个样本里只要有一两天极端舆情相关系数就会被带偏置信区间极大。解决数据量下限建议60个交易日最好能覆盖一年。对少于30天的结果不下结论做滚动窗口验证看相关系数是否稳定。另外把首尾各1%的极端收益率做winsorize截断可以避免单日事件主导整条曲线。5.5 抓取频率过高触发反爬限制现象连续跑了几百页后requests返回内容变成一段安全验证提示代码不报错但解析结果全是空的。原因同一IP短时间请求次数过多被风控临时限制。解决在每页请求之间加time.sleep(1)到2秒的随机延时pages控制在10页以内。批量分析多只股票时不要用线程池并发抓取先把每只股票的数据落成CSV缓存到本地第二次运行直接读缓存。这里没有银弹频率和采集量始终是矛盾的。6. 进阶把舆情因子接进量化流程用滚动窗口验证稳定性6.1 滚动窗口检验相关系数的稳定性固定整段数据算一个相关系数没有任何决策价值。我一般把数据切成两段前70%做样本内相关性检验后30%做样本外验证。更细的做法是滚动窗口窗口40个交易日、步长5天把每个窗口的Spearman相关系数画成曲线。def rolling_corr(merged: pd.DataFrame, window: int 40, step: int 5): 滚动窗口计算舆情与收益的相关性 merged[ret] merged[pct_chg] / 100.0 merged[sentiment_mean] merged[sentiment_mean].shift(1) # 用昨日舆情 results [] for start in range(0, len(merged) - window 1, step): seg merged.iloc[start:start window] corr seg[ret].corr(seg[sentiment_mean], methodspearman) results.append({end_date: seg[date].iloc[-1], corr: corr}) return pd.DataFrame(results)这里多了一步shift(1)模拟“今天收盘后才知道舆情明天开盘才能交易”的真实约束。如果直接用当日舆情和当日收益算相关性会有严重的未来函数问题。滚动窗口的相关系数符号稳定且绝对值超过0.2才值得继续往下做。6.2 把舆情分数构造成三档信号舆情与股价的关系通常不是线性的更常见的是极端情绪才有效。可以先把sentiment_mean映射成三档信号大于0.65为正面小于0.35为负面中间为中性。def sentiment_signal(score: float) - int: 舆情情绪映射为信号1正面-1负面0中性 if score 0.65: return 1 elif score 0.35: return -1 return 0然后统计信号第二天收益的均值merged[signal] merged[sentiment_mean].map(sentiment_signal) merged[ret_next] merged[pct_chg].shift(-1) summary merged.groupby(signal)[ret_next].agg([mean, count])三档收益均值做对比比单个相关系数更能说明舆情有没有交易价值。真实项目里我会把news_count也纳入过滤只有新闻条数超过中位数的日子才产生信号因为冷门股一天两条新闻打出的高分没有统计意义。6.3 一个实用技巧先缓存舆情数据再做参数实验做了几个类似项目后有个体会舆情采集和情感打分是最慢的一环akshare和资讯接口的响应速度都不稳定反复跑main.py会把大量时间浪费在等待上。我的习惯是第一次运行时把舆情聚合结果落盘sent_df.to_csv(fcache/sentiment_{stock_code}_{start_date}.csv, indexFalse)后续调整相关性参数、测试不同滞后天数时直接加载缓存文件不再碰网络接口。源码复现最耗时的往往不是代码逻辑而是接口等待和反爬限流。这个缓存习惯救了我很多次希望帮到你。本文还有配套的精品资源点击获取
RELATED

相关推荐

RoIPooling与RoIAlign深度解析:从量化误差到双线性插值的精度演进

RoIPooling与RoIAlign深度解析:从量化误差到双线性插值的精度演进

1. 从一次检测框偏移说起:为什么RoIPooling和RoIAlign值得单独拎出来讲如果你做过目标检测,大概率遇到过这种场景:模型训练时分类损失和回归损失都降得挺漂亮,mAP看着也还行,但一到实例分割或者关键点检测任务上&#…

📅 2026/10/12 22:36:23
轻量CNN手势识别系统:纯Python端到端实时实现

轻量CNN手势识别系统:纯Python端到端实时实现

简介:这是一套基于Python的实时手势识别系统开源实现,面向计算机视觉初学者与深度学习实践者,解决手势数据采集、模型训练与实时预测的一站式需求。资源包共4991个文件,主体为4984张手势样本JPG图像,辅以4个核心Python…

📅 2026/10/12 22:36:23
LibTorch C++工程化落地:OpenCV图像预处理与GPU推理避坑指南

LibTorch C++工程化落地:OpenCV图像预处理与GPU推理避坑指南

1. 项目概述:为什么是“Libtorch初试教程《二》”而不是“入门指南”“Libtorch初试教程《二》”这个标题本身就很说明问题——它不是从零开始的“Hello World”,也不是泛泛而谈的“安装跑通示例”。它明确指向一个已经跨过第一道门槛的人:你…

📅 2026/10/12 22:36:23
MORE NEWS

更多资讯

📰

Selenium Web自动化踩坑实录:从环境配置到元素定位的实战解决方案

做 Web 自动化用 Selenium,前前后后我也写了三四年了,从 Python 版到 VBA 里的 SeleniumBasic,从有头到无头,从简单采集数据到页面自动化巡检,踩过的坑整理下来真能写个小本子。这篇就是我的踩坑实录:环境装…

📰

tesseract-ocr安装与中文语言包配置:从装好到跑通中文识别

简介:这是 Tesseract OCR 的安装与中文识别资源包。Tesseract OCR 是谷歌维护的开源光学字符识别引擎,能将图像中的文字转换为可编辑文本,广泛应用于文档扫描、图片文字提取等领域。本包内含 Windows 平台所需的安装程序以及简体中文&#xf…

📰

开源代码审查工具实战:内网部署、规则引擎与人工复核方案

做了这么多年开发,我越来越觉得代码审查是所有研发流程里最容易被做成“形式主义”的一个环节。你说它不重要吧,很多线上事故明明可以在提交阶段被拦下来;你说它重要吧,真正愿意逐行看别人代码的团队真没几个。我见过太多评审流于…

📰

开放代码评审实践指南:从入口设计到结论沉淀的工程方法

1. 从“代码评审”到“开放评审”:一个被低估的工程实践第一次听到“open-code-review”这个词,很多人会下意识地把它理解成“开源项目的代码评审”。这个理解不算错,但太窄了。我在几个不同规模的研发团队里都推行过代码评审机制&#xff0c…

📰

RabbitMQ fanout exchange 核心原理与生产避坑指南

1. 为什么“扇形交换机”不是“广播”,而是消息路由的底层开关在刚接触 RabbitMQ 的时候,我见过太多人把 fanout exchange 直接等同于“消息广播”。某次在某公司做内部技术分享,一位后端同事脱口而出:“fanout 就是发一次&#x…

📰

Claude Code 安装与使用指南:把 settings 改到 TaoToken 的完整配置流程

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬