简介:围绕“股票评论情绪与股价涨跌关联”这一主题,此资源完整给出了Python爬虫采集评论、调用百度AI接口做语义倾向分析、再用MATLAB做统计处理与可视化的项目文件。面向金融舆情分析、量化数据挖掘,以及希望将爬虫、NLP和数值分析串成完整链路的Python/MATLAB学习者。压缩包共10个文件、1.71MB,包含2个Python脚本(评论抓取、情感分析)、1个MATLAB脚本(时序比较)、1个CSV评论样本、2个Excel分析结果及4张走势和合并图表,文件分工清晰,基本覆盖从数据采集到结论呈现的各个环节。目前已有6461人学习下载,适合对照练习与二次开发。通过这份资源,可以快速复现“评论采集—百度AI情感打分—MATLAB关联分析—图表展示”全流程,理解评论情感分布与股价涨跌之间的潜在关系;脚本中的接口调用方式、数据清洗思路和对比方法,也可迁移到其他股票或市场情绪分析场景。
1. 股票评论与涨跌:一个用 Python + AI + MATLAB 搭起来的情感分析实验
先说结论:股票评论区里的情绪,和第二天涨跌确实存在相关性,但不是“骂得狠就跌、夸得猛就涨”这么简单。想验证这一点,常规的手工翻评论根本做不到——一天几千条帖子,语气、反讽、口头禅混在一起,人眼看完已经晕了,更别提量化成数字。
这个项目做的事情很直接:用 Python 爬虫把某股票社区的评论抓下来,调用云厂商开放的 NLP 情感分析接口做语义打分,再把评分和行情数据一起丢给 MATLAB 做统计分析,最后输出一张“情绪得分 vs 涨跌幅”的关系图。整个流程从数据采集、文本分析到统计建模闭环跑通,适合手里有 Python 和 MATLAB 基础、想拿真实数据练手的人——不只是看看热词,而是能把评论区情绪变成可计算的指标。
2. 系统架构与数据获取:从网页评论到结构化 CSV
2.1 整体环节设计
这个项目不能算一个大型系统,更准确地说是一条数据处理流水线。四个环节各司其职:爬虫负责把网页上的零散评论变成结构化文本;接口调用负责把文本换成量化情绪;MATLAB 负责把情绪和行情关联起来做分析;最后的可视化把结果呈现出来。
我搭这套流程时的选型逻辑很简单:Python 适合写爬虫和调用接口,生态齐全;MATLAB 的统计工具箱和绘图能力比 Python 的 matplotlib 更适合做相关性分析和回归实验;云厂商的 NLP 接口省去了自己训练情感模型的重活——实际用的就是百度 AI 开放平台的文本情感分析接口,输入一段文本,返回情感极性(正向/负向/中性)和置信度。
import requests import json def fetch_comments(stock_code, page_count=10): comments = [] for page in range(1, page_count + 1): url = f"https://example-stock-forum.com/stock/{stock_code}/comment?page={page}" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", "Referer": f"https://example-stock-forum.com/stock/{stock_code}" } resp = requests.get(url, headers=headers, timeout=10) # 假设接口返回 JSON 数组,每条含 author, content, post_time page_data = resp.json() for item in page_data: comments.append({ "content": item["content"], "time": item["post_time"], "stock": stock_code }) time.sleep(1.5) # 避免请求过快触发反爬 return comments这段代码把“抓评论”这个动作封装成两个可变参数:stock_code决定抓哪只股票,page_count决定抓多少页。headers里的 User-Agent 和 Referer 是模拟浏览器的关键,很多股票社区对空 UA 的请求直接返回 403。time.sleep(1.5)是血泪经验——不睡够时间,抓两页就被封 IP。
抓下来的数据第一时间落盘存成 JSON,不能直接进分析环节,因为接口调用和统计分析都可能中断重来,有原始文件就永远有后悔药:
with open("raw_comments.json", "w", encoding="utf-8") as f: json.dump(comments, f, ensure_ascii=False, indent=2)这一行虽然简单,但ensure_ascii=False必须写——不写的话中文会被转成\u转义序列,MATLAB 读进去全变成乱码。存成带缩进的 JSON 也方便中途打开检查字段是否完整。
2.2 数据清洗:过滤广告、噪声与重复内容
股票评论区比一般社区脏得多:有人刷屏发广告,有人复制粘贴段子,还有机器人凌晨三点连续发帖。这些数据直接送进情感分析接口,不单浪费调用额度,还会污染统计结果。清洗策略按规则走,不搞机器学习——规则简单、可控、可解释。
def clean_comment(raw_text): text = raw_text.strip() # 去掉纯数字、纯链接、无意义短文本 if len(text) < 5: return None if text.startswith("http") or "推荐" in text and len(text) < 20: return None # 连续重复字符压缩,比如 "哈哈哈哈哈哈哈" -> "哈哈" while "哈哈" in text and text.count("哈") > 4: text = text.replace("哈哈哈哈", "哈哈") return text清洗规则从简单到严格分了四层:长度过滤把一句话都算不上的噪声去掉;URL 过滤排除引流帖;“推荐”关键词配合短文本长度过滤掉投资群广告;重复字符合并则是把情绪表达压缩成有效长度,避免影响情感接口的判定。
这一步的取舍值得说明——清洗不能太狠,比如“这支票主力太狠了”这种带情绪的吐槽,关键词“太狠了”不属于广告词,不能过滤;但“加微信领牛股”这种广告就必须干掉。规则匹配靠的是维护一个黑名单关键词列表,不用模型。
清洗完的数据视数量决定是否做去重。如果同一个用户在同一分钟内重复发同样的内容,保留第一条就够了,Python 里用 dict 按用户 ID 去重非常方便:
seen = {} unique_comments = [] for c in cleaned_list: dedup_key = c["user"] + "_" + c["content"][:30] if dedup_key not in seen: seen[dedup_key] = True unique_comments.append(c)按“用户 + 内容前 30 字”组合做 key,能在不引入额外依赖的情况下干掉大部分复制粘贴的刷屏内容。这一步做完,数据量通常会减少 10%-20%,分析质量提升非常明显。
2.3 行情数据补齐:评论必须和收盘价对齐
光有评论情感分数,没有行情数据,MATLAB 那边没法做相关性分析。行情数据两个来源二选一:第三方数据接口拉历史日线,或者自己从财经网站爬。接口稳定就优先接口,接口挂了才爬网页。
import pandas as pd def fetch_kline(stock_code, start_date, end_date): url = "https://example-quote-api.com/kline" params = { "code": stock_code, "start": start_date, "end": end_date, "adjust": "qfq" # 前复权,避免除权跳空误导统计 } resp = requests.get(url, params=params, timeout=10) data = resp.json()["data"] df = pd.DataFrame(data, columns=["date", "open", "close", "high", "low", "volume"]) df["date"] = pd.to_datetime(df["date"]) df["pct_change"] = df["close"].pct_change() * 100 return df.dropna()行情数据里必须算一列pct_change——百分比涨跌幅,而不是收盘价绝对值。原因很好理解:不同价位的股票,10 块钱跌 1 块和 100 块钱跌 1 块不是同一个概念,只有百分比才能跨股票横向比较。选qfq前复权是为了把历史上除权导致的假跳空修正掉,不然统计时会出现一根大阴线但股民其实没亏钱的错位。
2.4 中间数据落盘:给 MATLAB 留好接口
Python 这边的最终产物是两份 CSV:一份是清洗后的评论数据,一份是对齐好日期的行情数据。MATLAB 不读 JSON——readtable支持 CSV 最稳定,编码也最好控制。我用pandas把两份数据导出成 UTF-8 编码的 CSV 文件,index=False去掉索引列,避免 MATLAB 读进来多一列无名变量。
comments_df.to_csv("sentiment_scores.csv", index=False, encoding="utf-8-sig") kline_df.to_csv("daily_kline.csv", index=False, encoding="utf-8-sig")utf-8-sig是给 Excel 和 MATLAB 用的编码,带 BOM 头,Windows 平台上不会出现中文乱码。这个细节第一次跑项目的人基本都会踩——默认的utf-8在 Windows 的 MATLAB 里读中文 CSV,表头全变乱码,白费半小时。
3. 情感分析接入:把评论变成可计算的数值分数
3.1 云接口调用的完整流程
百度 AI 开放平台的情感分析接口,标准做法是先用 API Key 换 Access Token,再拿着 Token 请求情感分析服务。Token 有效期一般 30 天,不用每次调用都重新换,缓存到本地文件能省掉大部分网络请求。
def get_access_token(api_key, secret_key): url = "https://example-ai-cloud.com/oauth/2.0/token" params = { "grant_type": "client_credentials", "client_id": api_key, "client_secret": secret_key } resp = requests.post(url, params=params) return resp.json()["access_token"]换 Token 是标准 OAuth 流程,grant_type固定为client_credentials,client_id和client_secret在云厂商控制台里创建应用后能拿到。Token 要保存到本地文件,我一般把它写进一个token.json,下次启动先检查文件是否存在且未过期,省一次 HTTP 往返。
拿到 Token 后才开始真正的语义分析调用。每条评论发一个请求,接口返回情感极性、情感倾向置信度和正向/负向的概率:
def analyze_sentiment(text, token): url = "https://example-ai-cloud.com/nlp/v1/sentiment_classify" headers = {"Content-Type": "application/json"} body = { "text": text[:200], # 接口限制单条文本长度,超长截断 "token": token } resp = requests.post(url, headers=headers, json=body) result = resp.json() if result.get("error_code"): return None items = result["items"][0] return { "sentiment": items["sentiment"], # 0负向 1中性 2正向 "confidence": items["confidence"], # 置信度 0-1 "positive_prob": items["positive_prob"], "negative_prob": items["negative_prob"] }接口参数有三个关键点。text截断到 200 字符是因为接口对单条文本有长度上限,超长会被拒绝;sentiment返回的是 0/1/2 三分类标签,不是连续分数;confidence表示模型对本次判断的信心程度,如果低于 0.6,这条数据的可靠性就要打问号。我在实际调用时会把confidence低于阈值的结果单独存一个文件,后续分析时当作低质量样本剔除或降权。
3.2 批量处理与错误重试
真实评论量是几千甚至上万条,循环一条条调用接口速度能接受,但稳定性不行——网络抖动、接口限流、Token 过期,任何一个问题都会让循环中断。批量处理的框架必须支持断点续跑和错误重试。
def batch_analyze(comments, token, start_idx=0): results = [] for i in range(start_idx, len(comments)): text = comments[i]["content"] try: score = analyze_sentiment(text, token) if score: results.append({**comments[i], **score}) except requests.exceptions.RequestException: # 遇到网络错误,睡 3 秒后重试一次 time.sleep(3) score = analyze_sentiment(text, token) if score: results.append({**comments[i], **score}) if (i + 1) % 50 == 0: # 每处理 50 条存一次进度 pd.DataFrame(results).to_csv("progress.csv", index=False) print(f"已完成 {i + 1} / {len(comments)}") time.sleep(0.5) # 控制 QPS,避免触发限流 return results进度上报是这套方案里的关键设计。每 50 条写一次 CSV,程序中途挂了,已处理的结果不会丢,改start_idx就能从断点继续。sleep 0.5 秒把 QPS 控制在 2 以内,既不会触发云厂商的并发限制,也不会让 IP 被社区的反爬盯上。
3.3 情感得分的聚合策略
单条评论的情感标签只是三分类,直接拿 0/1/2 和涨跌幅做相关分析太粗糙。需要把一天内的所有评论聚合成一个“当日情绪指标”。最常用的方法是情绪比率加权:
def aggregate_daily_sentiment(sentiment_df): daily = sentiment_df.groupby("date").apply( lambda x: (x["sentiment"] == 2).sum() / max(len(x), 1) ).reset_index(name="positive_ratio") daily["negative_ratio"] = sentiment_df.groupby("date").apply( lambda x: (x["sentiment"] == 0).sum() / max(len(x), 1) ).values daily["sentiment_score"] = daily["positive_ratio"] - daily["negative_ratio"] return daily这个聚合逻辑选的是“正向占比减负向占比”作为情绪得分,取值范围在 -1 到 1 之间,负值代表当天评论情绪整体偏空,正值代表偏多。为什么不直接用平均值?因为三分类数据取平均值会把“中性”当作“不够积极”,信息量损失大;而正负占比的差值天然具有方向性,更容易和涨跌幅建立线性关系。
3.4 特征覆盖率的衡量
评论数量和涨跌幅的关系里有一个被忽视的变量——讨论热度。一只股票一整天只有 3 条评论,就算全是正向,情绪指标的可信度也远低于一天 300 条评论的样本。我在聚合结果里加了一列comment_count,然后在 MATLAB 分析时先把样本量低于 20 条的日子过滤掉,这个阈值是自己尝试出来的,设太低会引入噪声,设太高会把活跃度一般的股票直接删没了。
4. 避坑指南:这四步最容易翻车
4.1 评论抓回来全被反爬拦截
现象:爬虫代码完全照常运行,但返回的评论列表是空的,或者只有一条“访问过于频繁”的提示文本。
原因:股票社区的服务器对有明确爬虫特征的请求做了拦截。判断依据很简单——你自己浏览器能打开页面,但 requests 抓回来的是空壳 HTML。服务端检测到了缺 Cookie、高频访问、非常规请求头。
解决:先在自己的浏览器里登录社区,打开 DevTools 把完整的 Cookie 字符串复制出来,放进 headers。再设置访问间隔在 1-2 秒之间波动,不要固定同一个值——固定频率本身也是爬虫特征。如果这样还被封,就降低采集时段,只抓晚间到次日凌晨的低峰期评论。
4.2 中文 CSV 被 MATLAB 读成乱码
现象:MATLAB 执行readtable('sentiment_scores.csv')以后,日期列正常,但评论内容列和表头全是“锟斤拷”。
原因:文件编码不匹配。Python 默认写入的 UTF-8 不带 BOM,MATLAB 在 Windows 中文环境默认按 GBK 解码,编码错位导致中文乱码。
解决:导出 CSV 时用encoding="utf-8-sig"代替"utf-8",带 BOM 的文件 MATLAB 会正确识别。另外用readtable时可以显式指定参数:readtable('sentiment_scores.csv', 'Encoding', 'UTF-8'),双重保险。
4.3 情感接口返回大量 error_code
现象:批量分析跑到一半,突然连续报错,错误码提示token invalid或QPS limit exceeded。
原因:Token 过期是常见情况,尤其是调试阶段隔几天才跑一次;QPS 超限则是因为 sleep 间隔设置太短,或者多线程并发没有控制总量。
解决:在batch_analyze的循环里加 Token 有效性检查,捕获到鉴权失败错误时重新调用get_access_token。QPS 超限就把time.sleep(0.5)改成time.sleep(1),宁可慢一点,不要白跑一遍。更稳的做法是给请求加指数退避重试,第一次失败等 1 秒重试,第二次等 3 秒,第三次等 10 秒。
4.4 情绪得分和涨跌幅算出来相关系数接近零
现象:辛辛苦苦跑完整个流程,MATLAB 里corrcoef出来的相关系数只有 0.03,看起来情绪和涨跌毫无关系。
原因:数据对齐错了或者聚合粒度不对。最常见的是评论日期和行情日期没有做交易日对齐——周六周日的评论被算进了下周一的情绪,而周五的收盘价波动其实是周一才会反应到盘面上。
解决:拉行情数据时只保留交易日 K 线,然后把评论按日期映射到最近的交易日。更进阶的做法是给情绪指标做滞后一期对齐:用第 T 日的情绪得分预测 T+1 日的涨跌幅,这一步是整套分析的精髓——当天评论影响的是次日开盘后的走势,而不是当天已经收盘的走势。
5. MATLAB 数据处理与关系建模:情绪和涨跌到底怎么关联
5.1 数据读入与预处理
MATLAB 端的分析承接 Python 输出的两份 CSV。第一步是数据读入、列名检查、缺失值处理。这一步不难但必须做——数据不干净,后面跑出来的任何统计指标都是自欺欺人。
% 读取情感聚合结果和行情数据 sentiment = readtable('sentiment_scores.csv', 'Encoding', 'UTF-8'); kline = readtable('daily_kline.csv', 'Encoding', 'UTF-8'); % 统一日期格式,便于后续 join sentiment.Date = datetime(sentiment.Date, 'InputFormat', 'yyyy-MM-dd'); kline.Date = datetime(kline.Date, 'InputFormat', 'yyyy-MM-dd'); % 过滤评论样本量过少的日期(少于20条评论不参与统计) valid_idx = sentiment.comment_count >= 20; sentiment = sentiment(valid_idx, :); % 按日期对表 join,inner join 保证两边都有数据 data = innerjoin(sentiment, kline, 'Keys', 'Date');innerjoin在这里比outerjoin安全——如果某一天有行情但没评论,或者有评论但没行情,这一行数据就不应该混进分析集合里。comment_count >= 20的阈值是之前在 Python 端定的,MATLAB 里再过滤一次,确保两个环境里用同一套标准。
5.2 相关性分析与滞后效应检验
拿到了对齐后的数据表,接下来是核心分析:情绪得分和涨跌幅之间是否存在线性相关。
% 情绪得分与当日涨跌幅的相关性 [r_daily, p_daily] = corrcoef(data.sentiment_score, data.pct_change); % 情绪得分与次日涨跌幅的相关性(滞后一期) pct_next = [data.pct_change(2:end); NaN]; % 次日涨跌幅,最后一行补 NaN [r_lag1, p_lag1] = corrcoef(data.sentiment_score, pct_next); fprintf('当日相关系数: %.3f, p值: %.3f\n', r_daily(1,2), p_daily(1,2)); fprintf('滞后一天相关系数: %.3f, p值: %.3f\n', r_lag1(1,2), p_lag1(1,2));corrcoef返回的是一个 2x2 矩阵,对角线上是自相关,非对角线是目标相关系数。p值是显著性检验的结果——p 小于 0.05 才算统计上有意义的关联,不然纯粹是噪声。滞后一期的做法是把 T 日的情绪得分对应到 T+1 日的涨跌幅,这反映的是一个自然逻辑:股民看完评论和收盘数据后,做出买卖决策通常发生在下一个交易日。
这个项目的关键发现通常出现在滞后一期的相关系数上——当日情绪和当日涨跌幅很可能相关性不显著,因为价格变动先于情绪发酵;而前一天的极端情绪往往能预测次日开盘的方向。这正是把 AI 语义分析和 MATLAB 统计建模串起来以后才看得到的洞察。
5.3 回归建模:加入更多解释变量
皮尔逊相关只是第一步,要解释涨跌幅的多因素影响,得用多元线性回归。在情绪得分之外,把成交量、当日振幅、前一日涨跌幅都放进模型。
% 构造回归矩阵:情绪得分 + 成交量变化率 + 当日振幅 X = [data.sentiment_score, data.volume, data.high - data.low]; y = data.pct_change; % 多元线性回归 mdl = fitlm(X, y, 'VarNames', {'SentimentScore', 'Volume', 'Amplitude', 'PctChange'}); disp(mdl);fitlm输出的表格里每一行代表一个特征的回归系数、标准误、t 统计量和 p 值。重点看两件事:SentimentScore的系数是否显著不为零(p < 0.05);系数的正负方向是否符合直觉。如果情绪得分的系数为负且显著,说明评论区情绪越亢奋,次日越可能下跌——这其实是一个在市场里反复被验证的“反向指标”现象。
5.4 可视化:把结论画出来
模型跑完,最终交付物是一张散点图加拟合线。MATLAB 的scatter配合lsline是最快的呈现方式,但我会手动画拟合线,方便控制样式和标注。
figure; scatter(data.sentiment_score, pct_next, 20, 'filled', 'MarkerFaceAlpha', 0.6); hold on; % 一阶线性拟合 p = polyfit(data.sentiment_score, pct_next, 1); x_fit = linspace(min(data.sentiment_score), max(data.sentiment_score), 100); y_fit = polyval(p, x_fit); plot(x_fit, y_fit, 'r-', 'LineWidth', 2); xlabel('情感得分(正负情绪占比差)'); ylabel('次日涨跌幅(%)'); title('股票评论情感得分与次日涨跌幅关系'); grid on;polyfit和polyval负责拟合一阶直线,MarkerFaceAlpha控制散点透明度——样本量大时重叠严重,调低透明度能看出密度分布。这张图如果呈现明显的正斜率,说明偏多情绪能带动短线行情;如果斜率是负的,说明评论热烈的时候反而是离场信号。具体结果取决于你抓的是哪只股票、哪个时间段,但这套流程本身是可以反复替换数据验证的。
6. 二次开发指南:把流水线改造成可扩展的分析框架
6.1 模块解耦:让每一步都能独立替换
这套流水线从一开始就按“单输入单输出”的模块化方式搭建,Python 端负责数据和接口调用,MATLAB 端负责统计和绘图,两边只靠 CSV 文件通信。这个设计让替换任何一环都变得容易——想换数据源、想换语义分析接口、想换统计方法,都只改一个模块,不动整条链路。
最常见的改造方向是换情感分析引擎。云厂商的 NLP 接口按调用次数计费,量大以后成本不可忽略。替代方案有本地部署的开源模型,或者在评论数量不大(一两百条)时用基于词典的规则方案——维护一个正面词表和负面词表,直接算文本里两类词的命中频次。词典方案精度不如深度学习接口,但零成本、速度快、可解释性强。我的建议是保留接口调用作为基准答案,用词典方案做消融实验,对比两者的分析结果是否符合直觉。
6.2 增加股票池:从单支票到横向对比
当前代码里stock_code是单值参数,扩展到多股票需要做一次循环包装。建立一个股票池列表,逐只股票跑爬虫、调用接口、导出 CSV,最后在 MATLAB 里按股票代码分组做对比分析。横向对比能回答更有趣的问题——不同市值、不同行业的股票,评论情绪与涨跌的相关性方向是否一致。
stock_pool = ["600000", "000001", "300750"] # 示例股票池 for code in stock_pool: comments = fetch_comments(code, page_count=10) # 清洗、情感分析、导出 CSV export_sentiment_csv(comments, code)注意股票池不宜过大——每只股票 10 页评论按 300 条算,三只股票就是近千次接口调用,QPS 控制在 2 以内至少需要跑 10 分钟。我先跑一只股票验证全流程,再逐步扩池。
6.3 时间窗口调整:盘中情绪与尾盘涨跌的关系
当前的聚合单位是“自然日”,实际上一天之内的情绪变化也有分析价值。把评论时间切分成上午盘、下午盘、收盘后三个窗口,分别聚合情绪得分,再用 MATLAB 分析不同窗口的情绪对次日开盘的影响。改动点集中在时间戳处理和分组聚合逻辑上:
def time_window(hour): if hour < 11 or (hour >= 13 and hour < 15): return "trading" elif hour >= 15: return "after_close" else: return "lunch"把这条规则加进入库前的处理函数,sentiment_scores.csv就多出一列time_window,MATLAB 端按窗口分组做回归就行。一个小的改动就能看出盘中和盘后情绪对股价的驱动力是否一致——多数情况下盘后情绪更能反映散户对次日走势的真实预期。
6.4 验证方法:回测和样本外测试
任何改造完成后,都要回答一个问题:改完的效果是真实提升还是过拟合?我的习惯是用时间序列切分做验证——取前 70% 的数据建模,后 30% 的数据验证。如果模型在前段拟合得好、后段预测也跟得上,说明关系稳定;如果只在某一段历史数据上显著,换个时间段就失效,那基本是伪相关。这个习惯在验证情绪指标与涨跌关系时尤其重要,评论区情绪受人气和舆情事件影响极大,不同市场阶段的相关性方向可能完全不同。
从那以后,我每次跑完相关分析都会强制做一遍样本外验证,哪怕只是简单的前后段对比。这个项目本身是入门级的,但把它当成一个可反复实验的框架来用,能挖的东西比一次性跑通多得多——换股票、换时间窗、换语义引擎,每次改动都是一次新的实验。希望帮到你。
import re import json import time import requests
定义一个函数用于获取股票评论数据
def get_stock_comments(stock_code, page): url = f"https://example-stock-api.com/{stock_code}/comments" params = { "page": page, "per_page": 100 } headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } try: response = requests.get(url, params=params, headers=headers, timeout=10) response.raise_for_status() # 检查请求是否成功 return response.json() except requests.exceptions.RequestException as e: print(f"请求失败: {e}, 股票代码: {stock_code}, 页码: {page}") return []
清洗评论数据:去除空字符串、特殊字符等无效内容
def clean_text(text): # 去除特殊字符和非中文字符外的内容 cleaned = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', text) cleaned = re.sub(r'\s+', ' ', cleaned).strip() return cleaned if len(cleaned) > 10 else None
调用百度AI情感分析API
def sentiment_analysis(comment_text, access_token): url = "https://aip.baidubce.com/rpc/2.0/nlp/v1/sentiment_classify" headers = { "Content-Type": "application/json" } data = { "text": comment_text, "access_token": access_token } try: response = requests.post(url, headers=headers, json=data, timeout=10) response.raise_for_status() result = response.json() if "items" in result and len(result["items"]) > 0: item = result["items"][0] return { "sentiment": item.get("sentiment", 2), "confidence": item.get("confidence", 0), "positive_prob": item.get("positive_prob", 0), "negative_prob": item.get("negative_prob", 0) } else: return None except Exception as e: print(f"情感分析失败: {e}") return None
主处理流程
def main(): stock_code = "600000" total_pages = 5 comments = []
for page in range(1, total_pages + 1): page_data = get_stock_comments(stock_code, page) if page_data and "data" in page_data: for item in page_data["data"]: content = item.get("content", "") cleaned = clean_text(content) if cleaned: comments.append({ "stock_code": stock_code, "time": item.get("time", ""), "content": cleaned, "page": page }) time.sleep(1) # 避免请求过快 print(f"共抓取到 {len(comments)} 条有效评论") # 保存原始数据 - 用于后续处理 with open(f"comments_{stock_code}.json", "w", encoding="utf-8") as f: json.dump(comments, f, ensure_ascii=False, indent=2) print(f"评论数据已保存至 comments_{stock_code}.json")ifname== "main": main()
保存数据至CSV,便于Matlab读取
import pandas as pd
def save_to_csv(comments, filename): df = pd.DataFrame(comments) df.to_csv(filename, index=False, encoding="utf-8-sig") print(f"数据已保存至 {filename}")
生成交易日历(示例:仅计划生成日期)
def generate_trading_dates(start_date, end_date): # 排除周末的简单示例;实际可以网上拉取交易日历 import datetime dates = [] current = datetime.date.fromisoformat(start_date) end = datetime.date.fromisoformat(end_date) while current <= end: if current.weekday() < 5: # 周一到周五 dates.append(current.isoformat()) current += datetime.timedelta(days=1) return dates
获取历史K线数据
def fetch_kline_data(stock_code, start_date, end_date): url = f"https://example-stock-api.com/{stock_code}/kline" params = { "start": start_date, "end": end_date, "adjust": "qfq" } response = requests.get(url, params=params, timeout=10) if response.status_code == 200: data = response.json() if "data" in data: df = pd.DataFrame(data["data"]) df["date"] = pd.to_datetime(df["date"]) df["pct_change"] = df["close"].pct_change() * 100 return df return pd.DataFrame()
保存情感分析结果
def analyze_and_save(comments, access_token): results = [] for comment in comments: sentiment = sentiment_analysis(comment["content"], access_token) if sentiment: results.append({**comment, **sentiment}) time.sleep(0.5) # 控制请求频率
df = pd.DataFrame(results) df.to_csv("sentiment_scores.csv", index=False, encoding="utf-8-sig") print(f"情感分析完成,结果已保存至 sentiment_scores.csv,共 {len(results)} 条") return dfMATLAB数据分析示例
这是从Python视角对MATLAB侧的处理说明
''' 在MATLAB中,对情感分数和市场表现做相关性分析,读取CSV后进行数据对齐,计算加权情绪得分,并探索当日或滞后一日的涨跌关系。 主要步骤:
- readtable('sentiment_scores.csv')
- innerjoin 与 kline 数据对齐
- 计算情绪得分 = positive占比 - negative占比
- corrcoef 检验相关性
- fitlm 建立回归模型 '''
权重调整:考虑评论数量的影响
def weighted_sentiment(daily_scores): daily_scores["weighted_score"] = ( daily_scores["sentiment_score"] * (daily_scores["comment_count"] / daily_scores["comment_count"].mean()) ) return daily_scores
7. 延伸思路:情绪分析的更多可能性
情感分析除了判断正负向情绪外,还可以挖掘评论中提到的具体话题。比如股民经常讨论“主力吸筹”、“利好兑现”、“业绩预增”等关键词,提取这些关键词的频次并和涨跌关联,能发现比单纯正负情绪更有意思的规律——短期的市场噪音和长期的业绩预期,在评论中会有完全不同的表达方式。把“文本中的事件”和“文本中的情绪”拆成两维,分析精度会上一个台阶。
这个项目本身是一个全套可复现的实验,从爬虫到语义分析到统计建模,每一条链路都值得二次打磨。
本文还有配套的精品资源,点击获取