☰
雪球行情接口实战:从协议拆解到Python量化监控脚本
2026/10/4 6:24:57 网站建设 项目流程

做投资研究的人,十有八九绕不开雪球。社区讨论是一层,真正让技术派上头的,是雪球 App 和网页版背后那套行情接口。我自己最开始也是从新浪、腾讯的免费股票数据接口入手,后面发现字段不够细、要拼的数据太多,就开始研究雪球的数据采集方案。这篇文章就记录我从拆包到落地脚本的完整过程,包括协议分析、代码实现、常见坑,以及使用边界。适合对接口有兴趣、想自己搭自选股监控或做个人量化分析的朋友参考。

1. 为什么我最后选了雪球,而不是继续用新浪和腾讯

1.1 免费接口里,雪球的字段确实更“懂”投资者

老牌的免费接口我也用了挺久。新浪的hq.sinajs.cn/list=sz000001一行文本能拿到开盘、最高、最低、收盘、成交量,简单直接;腾讯的qt.gtimg.cn/q=sh600519信息稍微多一点,但本质上还是以行情报价为核心的轻量数据。

问题在于:当我想看“PE、PB、市值、换手率、量比、52周最高最低”这些偏估值和情绪面的指标时,新浪和腾讯并不直接给全,需要自己从其他页面去拼。而雪球的quote.json返回体里,这些字段基本一次到位,对于做自选股筛选和个股体检来说非常方便。

1.2 三种接口的横向对比

对比项新浪腾讯雪球
symbol 风格sz000001sh600519SH600519
实时性还不错还不错接近行情商体验
字段丰富度基础报价基础报价估值、市值、盘口、52周区间等比较全
批量查询支持逗号拼接支持逗号拼接支持批量 quote 接口
历史K线有,但分散有,字段简单kline 接口直接返回结构化数据
官方文档无无无,需要抓包分析
风控敏感度中中高,频率控制要求更严格

这张表不是想说明雪球一定最好,而是说:如果你的需求只是偶尔看一眼价格,新浪腾讯更快更省事;如果你要做筛选、回测、字段加工,雪球的综合体验更好。

1.3 什么样的人适合研究雪球接口

我个人总结下来,适合研究这套接口的人主要有三类:

  • 自选股监控派:想把自选股的实时价、涨跌、异动推到本地或者做提醒。
  • 量化学习派:需要历史K线跑均线策略、写简单的回测框架。
  • 数据整理派:想把不同平台的数据统一到一张本地表里做分析。

反过来,如果你准备做高频交易或者大规模商业分发,那我劝你直接放弃这个思路,原因后面第 6 节详细说。

2. 雪球接口协议拆解:浏览器做了什么,脚本就做什么

2.1 打开开发者工具,看一眼 XHR

研究任何网页接口,第一步永远是 F12。

打开雪球网页版,随便搜一只股票,在 Network 面板里筛quote相关的 XHR 请求,能看到类似这样的地址:

  • 实时行情:https://stock.xueqiu.com/v5/stock/quote.json?symbol=SH600519&extend=detail
  • 批量行情:https://stock.xueqiu.com/v5/stock/batch/quote.json?symbol=SH600519,SZ000001&extend=detail
  • 历史K线:https://stock.xueqiu.com/v5/stock/chart/kline.json?symbol=SH600519&begin=1696982400000&period=day&type=before&count=-5&indicator=kline

注意这几个接口都挂在stock.xueqiu.com这个域名下面,相对比较稳定,但不要理所当然以为它永远是公开的。接口路径和参数随时可能因为网页改版而调整。

2.2 三个请求头决定访问成败

参数其实不算复杂,真正的门槛在请求头。我踩过最开始的几次 403,基本都是请求头没给全。关键就三个:

  1. User-Agent:最好伪装成正常浏览器的 UA,不要用默认的python-requests。
  2. Referer:雪球的服务端会校验来源,通常设置成https://xueqiu.com/就能过。
  3. Cookie:核心是xq_a_token、u、xq_r_token这类字段。第一次访问雪球首页时服务端会下发 Cookie,所以脚本里要先用 Session 访问一次首页,把 Cookie 保持在会话里,再请求接口。

不要自己去拼 Cookie,因为 token 时效性很强,拼出来的可能会很快失效。

2.3 返回 JSON 的核心结构

quote.json返回的 JSON 大致长这样,我用示例结构说明,数字不是实时值:

{ "error_code": 0, "data": { "quote": { "symbol": "SH600519", "code": "600519", "name": "贵州茅台", "current": 1686.0, "percent": 1.23, "change": 20.5, "high": 1690.0, "low": 1650.0, "volume": 2600000, "amount": 4387500000, "market_capital": 2117000000000, "pe_ttm": 30.5, "pb": 8.1, "turnover_rate": 0.21, "high52week": 1700.0, "low52week": 1200.0 } } }

error_code等于 0 表示请求成功。核心字段都在data.quote里,很多是数字类型,解析成本很低。

3. 从零到一:一个可运行的雪球行情脚本

3.1 环境准备

我用的是 Python 3.9 以上的版本,依赖只需要requests,如果你想顺手做数据处理,可以再加个pandas。

python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install requests pandas

3.2 初始化会话和请求头

先建立一个 Session 对象,把请求头设置成浏览器风格,并访问一次首页拿到 Cookie。

import time import json import requests UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" session = requests.Session() session.headers.update({ "User-Agent": UA, "Accept": "application/json, text/plain, */*", "Referer": "https://xueqiu.com/" }) # 先访问首页,让服务端下发匿名 Cookie session.get("https://xueqiu.com/", timeout=10)

这一步很多人会偷懒省略,结果后面每次请求都会报错。首次访问首页不是没用的,它本质是“登录态初始化”。

3.3 获取单只股票实时行情

拿到带 Cookie 的 Session 之后,实时行情其实就是一个 GET 请求的事。

def get_quote(symbol): url = "https://stock.xueqiu.com/v5/stock/quote.json" params = { "symbol": symbol, "extend": "detail" } resp = session.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json() if data.get("error_code") != 0: print("请求异常:", data) return None return data["data"]["quote"]

调用一下:

if __name__ == "__main__": q = get_quote("SH600519") if q: print(q)

这里有几个细节值得注意:

  • symbol的格式是大写市场前缀加股票代码,沪市是SH,深市是SZ,港股通常是HK,美股是US前缀。不要和腾讯、新浪的混合格式搞混。
  • extend=detail参数会返回更全的字段,建议带上。
  • 不要在一个循环里对几百只股票逐个调quote.json,后面会单独讲批量接口。

3.4 解析关键字段,输出可读结果

接口返回的是 JSON,直接 print 会特别乱。我习惯封装一个格式化函数,把关心的字段输出成一眼能看懂的内容。

def format_quote(q): lines = [ f"{q.get('name')}({q.get('symbol')})", f"现价:{q.get('current')} 涨跌幅:{q.get('percent')}%", f"今高:{q.get('high')} 今低:{q.get('low')}", f"成交量:{q.get('volume')} 成交额:{q.get('amount')}", f"市值:{q.get('market_capital')} PE(TTM):{q.get('pe_ttm')} PB:{q.get('pb')}", f"52周高:{q.get('high52week')} 52周低:{q.get('low52week')}" ] return "\n".join(lines)

有一点要提醒:不同品种的字段不是完全一致的。股票、基金、债券、指数返回的公共字段可能相同,但估值类字段不一定都有。所以格式化的时候尽量用.get(),不要直接q["pe_ttm"],否则遇到基金或指数可能 KeyError。

4. 进阶用法:批量行情、历史K线和日常筛选

4.1 批量行情,一次请求能省不少事

如果自选股有几十只,用单个quote.json逐个去查显然不现实。雪球提供了一个批量接口:batch/quote.json。

def get_batch_quotes(symbols): url = "https://stock.xueqiu.com/v5/stock/batch/quote.json" params = { "symbol": ",".join(symbols), "extend": "detail" } resp = session.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json() if data.get("error_code") != 0: return [] items = data["data"]["items"] return [item["quote"] for item in items if "quote" in item]

批量接口返回的结构和单票接口有点区别,最外层是data.items,每一个 item 里再套quote。写解析逻辑的时候要按这个层级走。

你可以一次传几十个 symbol,我实际用下来查询效率比循环快很多,而且对服务端的压力也小一些。

4.2 历史K线数据怎么拿

历史K线是回测和趋势分析的基础。雪球的 kline 接口一次能返回多根K线,参数稍微复杂一点:

  • symbol:股票标识,和前面一致。
  • period:K线周期,常见day、week、month,分钟级别也有,但我用得少。
  • count=-30:负数表示从begin时间点往前取 30 根。
  • type=before:表示只取 begin 之前的K线,避免取到未来数据。
  • indicator=kline:指定返回 OHLC 数据。

示例代码:

import time def get_kline(symbol, period="day", count=30): url = "https://stock.xueqiu.com/v5/stock/chart/kline.json" begin = int(time.time() * 1000) params = { "symbol": symbol, "begin": begin, "period": period, "type": "before", "count": -count, "indicator": "kline" } resp = session.get(url, params=params, timeout=10) resp.raise_for_status() data = resp.json() if data.get("error_code") != 0: return [] columns = data["data"]["column"] rows = data["data"]["item"] return [dict(zip(columns, row)) for row in rows]

返回结果里column是字段名列表,item是二维数组。我把它们拼成一堆 dict,用起来会顺手很多。字段一般包括timestamp、volume、open、high、low、close、chg、percent、turnoverrate等。

注意begin用的是毫秒时间戳,不是秒。别在这上面踩坑。

4.3 把接口组合起来,做一个小筛选器

当你能批量拿行情、又能拿K线之后,能做的事情就很多了。

举个我自己的例子:每天收盘后,我想把“PE 低于 20、当日涨幅超过 3%、最近 20 天没有大涨过”的股票筛出来放到备选池里。

quotes = get_batch_quotes(symbols) for q in quotes: pe = q.get("pe_ttm") percent = q.get("percent", 0) if pe is None: continue if percent > 3 and pe < 20: print(q["name"], q["current"], percent, pe)

再配合 kline 数据算一下 20 日涨幅,过滤条件可以写得更细。整个过程不复杂,但对数据处理能力是有要求的。

我不建议把这个工具直接当投资决策依据,它更适合做研究阶段的初筛。筛选条件、数据源、阈值设置都需要自己反复验证。

5. 高频请求与小白容易踩的坑

5.1 请求频率过快,直接被限制

这是我第一次大规模拉数据时遇到的最狠的教训。

当时我写了一个 for 循环,想拉全市场几千只股票的实时行情,结果拉到一半左右开始出现 403,再往后直接返回验证页。最后我不得不停了几分钟,重新拿 Cookie 才恢复。

雪球对接口频率的限制比其他免费数据源更严,尤其是针对数据中心类的接口。我的建议是:

  • 批量接口一次能传几十个 symbol,就不要循环请求单票接口。
  • 每次请求之间至少time.sleep(1),如果量很大,建议间隔 1 到 3 秒。
  • 不要开多线程去并发打接口,雪球对这种短时间高并发请求非常敏感。

5.2 Cookie 过期,返回一堆看不懂的错误码

匿名 Cookie 不是永久有效的。脚本跑一段时间后,可能突然发现所有请求都返回error_code非 0,或者直接报错。

遇到这种情况,最直接的恢复方式就是重新执行一次首页访问,刷新会话里的 Cookie。

def refresh_session(): session.get("https://xueqiu.com/", timeout=10)

我把这个逻辑封装成了一个refresh_session()函数,在程序启动和请求连续失败时都会调用。

5.3 盘前、盘后和停牌数据不要想当然

这个坑特别隐蔽。

在非交易时段请求接口时,current往往还是上一个交易日的收盘价,percent可能显示为 0,但这并不意味着这只股票今天没有波动。同样,遇到停牌股,成交量、成交额可能就是 0,字段值也会保持不变。

所以被监控工具拿到数据后,最好根据接口返回的时间戳和本地时间做一次判断,区分“实时盘口”和“收盘快照”。否则你的告警逻辑很容易在盘前盘后误报。

5.4 非官方接口的变更不可控

雪球网页版和 App 每次改版,接口都有可能调整。我遇到过参数从extend=detail改成别的、字段名从high52week变成别的、接口路径加版本号的变更。

应对办法就是给脚本加一层容错:

  • JSON 解析失败或者 Key 不存在时,不要直接 crash,先打日志。
  • 把接口返回的原始 JSON 存一份到本地,方便出问题时排查。
  • 定期跑一个最小请求,确认接口还是通的。

这套接口本质上不是“文档齐全的开放平台”,而是网页功能的一部分,随时可能变。所以做个人研究没问题,但别把它当生产基础设施。

6. 接口研究的合规边界与长期使用建议

6.1 自己能用的接口,不等于可以随便分发

这是做数据采集最容易忽略的问题。

从技术上说,只要浏览器能访问,脚本也能访问。但从使用边界上说,接口是雪球网页端产品的一部分,不是官方对外开放的数据服务。个人为了学习、研究,在自己本地低频调用,通常问题不大;但如果把它做成公共接口、付费工具、企业数据服务,就很容易踩到平台规则和合规风险。

我的原则是:

  • 只在本地跑,不上公网服务。
  • 不把原始数据打包对外分发。
  • 不绕过平台已有的登录、验证机制。
  • 控制频率,明显低于正常用户操作节奏。

6.2 真正做产品时,优先考虑有授权的数据源

如果你不是研究,而是想做一个长期可用的产品,比起研究雪球接口,我更建议优先考虑有明确授权的数据服务商,比如量化平台的数据模块、专业行情商、或者有正式数据授权的服务商。这类服务的优势在于数据合法、接口稳定、字段有文档,出了问题有人处理。

如果你只是自用,那雪球这套接口可以玩得很开心。自选股提醒、估值异动监控、K线回测辅助,都是非常合适的场景。把频率控制好、把数据校验做好,它完全可以成为个人投资研究工作台的一部分。

写到这里,顺便分享一个小习惯:我每次跑完数据都会把原始 JSON 留一份本地缓存,再往下做清洗和转换。这样即使接口某天改了返回结构,手里的历史数据也不会突然“断档”。这套方法不完美,但对个人研究者来说,可能是最实用的落地方式。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询