如果你写过股票相关的小程序、量化脚本或者行情看板,大概率你接触过这个地址:hq.sinajs.cn。这就是被无数教程反复提及的新浪财经股票数据接口。它免费、无需注册、一个 URL 就能拿到实时报价,是很多人第一次接触行情数据的起点。
但很多读者不清楚的是:新浪官方从来没有对外发布过这个API接口。
它其实和我们早前提到过的Yahoo Finance一样,都是依靠群众的力量,一步步被破解出来的。没有官方文档,没有官方客服,随时可能改格式、加限制甚至停更。用它做个人 demo 没问题,一旦要放到线上产品、交易终端或者交易所系统里,几乎必然踩坑。
作为 Infoway,我们为海内外多家交易所和量化团队提供行情数据,接到过大量从新浪接口迁过来的需求,于是就有了这篇文章。我们将详细讲解新浪财经 API ,如果你之前没有了解过它,这篇教程能帮助你快速上手,如果你正在寻找新浪财经API的替代品,我们也会给出如何平滑迁移到 Infoway的方案。
一、新浪财经 API 到底是什么
正如前文所述,新浪财经并没有像专业数据商那样发布一套 RESTful API,它真正被大家当接口用的,其实是新浪股票页面背后的数据源地址。当年很多爬虫、看板都是直接读这个地址,久而久之就成了事实上的新浪财经 API。
它主要包含三类数据:
| 数据类型 | 地址(域名) | 返回格式 |
|---|---|---|
| 实时行情 + 五档盘口 | hq.sinajs.cn | JS 变量赋值字符串 |
| 历史 K 线 | money.finance.sina.com.cn | JSON(伪 JSON) |
| 分时数据 | money.finance.sina.com.cn | JSON |
覆盖市场包括 A 股、港股、美股,其中 A 股的实时买卖五档是它最受欢迎的部分。下面逐个拆解。
1.1 实时行情接口
最核心的接口,用法是在 list= 后面拼股票代码:
http://hq.sinajs.cn/list=sh601006代码前缀规则:
- 上交所股票加
sh,如大秦铁路sh601006; - 深交所股票加
sz,如平安银行sz000001; - 加
s_前缀(如s_sh601006)返回简略数据,只有价格、涨跌、成交量几个字段;不加则返回详细数据,包含五档盘口。
批量查询直接用逗号拼接多个代码:
http://hq.sinajs.cn/list=sh601006,sz000001,sh600519接口返回一段以 var hq_str_股票代码= 开头的文本,例如:
var hq_str_sh601006="大秦铁路,27.55,27.25,26.91,27.55,26.20,26.91,26.92,...";1.2 返回字段下标全解析
这是新浪接口最劝退的地方😢,所有数据挤在一个逗号分隔的字符串里,没有任何字段名,全靠下标记忆。以详细数据为例,各下标含义如下(A 股):
| 下标 | 含义 | 下标 | 含义 |
|---|---|---|---|
| 0 | 股票名称 | 1 | 今日开盘价 |
| 2 | 昨日收盘价 | 3 | 当前价格 |
| 4 | 今日最高价 | 5 | 今日最低价 |
| 6 | 竞买价(买一) | 7 | 竞卖价(卖一) |
| 8 | 成交量(股,除以 100 得”手”) | 9 | 成交额(元,除以 10000 得”万元”) |
| 10 / 11 | 买一量 / 买一价 | 12 / 13 | 买二量 / 买二价 |
| …… | 买三~买五(14~19) | 20 / 21 | 卖一量 / 卖一价 |
| …… | 卖二~卖五(22~29) | 30 | 日期 |
| 31 | 时间 | 32 | 停牌标记等 |
有两个几乎人人踩过的换算坑:
- 第 8 项成交量是股,A 股以 100 股为一手,通常要除以 100
- 第 9 项成交额单位是元,为了直观一般除以 10000 换算成万元
1.3 历史 K 线接口
新浪的历史 K 线走的是另一个域名,返回相对规整的 JSON:
http://money.finance.sina.com.cn/quotes_service/api/json_v2.php/CN_MarketData.getKLineData?symbol=sh000001&scale=60&ma=5&datalen=1023参数含义:symbol 股票代码、scale 周期(分钟,如 5/15/30/60,日线用 240)、ma 均线周期、datalen 返回条数。
1.4 Referer 防盗链
这是新浪接口最大的一个版本断层。2022 年下半年起,新浪对 hq.sinajs.cn 增加了 Referer 校验:直接在浏览器里打开 http://hq.sinajs.cn/list=sh600519,会返回 Forbidden(403)。
解决办法是在请求头里带上 Referer:
import requests
headers = {
"Referer": "https://finance.sina.com.cn", # 关键:不带会返回 403
"User-Agent": "Mozilla/5.0",
}
url = "http://hq.sinajs.cn/list=sh601006,sz000001"
resp = requests.get(url, headers=headers)
# 关键第二坑:返回内容是 GB2312/GBK 编码,直接读会中文乱码
resp.encoding = "gbk"
print(resp.text)注意上面代码里的第二个坑:编码。新浪接口返回的是 GB2312/GBK 编码,如果不手动指定 encoding="gbk",股票名称会变成乱码。
1.5 解析示例
把上面的知识点串起来,一个能用的新浪实时行情解析脚本大致长这样:
import requests
def fetch_sina(codes):
"""codes 形如 ['sh601006', 'sz000001']"""
url = "http://hq.sinajs.cn/list=" + ",".join(codes)
headers = {"Referer": "https://finance.sina.com.cn", "User-Agent": "Mozilla/5.0"}
resp = requests.get(url, headers=headers)
resp.encoding = "gbk"
result = {}
for line in resp.text.strip().split(";"):
line = line.strip()
if not line or "=" not in line:
continue
var, payload = line.split("=", 1)
code = var.replace("var hq_str_", "")
fields = payload.strip('"').split(",")
if len(fields) < 32: # 简略数据或停牌,字段不全,直接跳过
continue
result[code] = {
"name": fields[0],
"open": float(fields[1]),
"prev_close": float(fields[2]),
"price": float(fields[3]),
"high": float(fields[4]),
"low": float(fields[5]),
"volume_hand": float(fields[8]) / 100, # 股 → 手
"amount_wan": float(fields[9]) / 10000, # 元 → 万元
"bid1_price": float(fields[11]),
"ask1_price": float(fields[21]),
"date": fields[30],
"time": fields[31],
}
return result
print(fetch_sina(["sh601006", "sz000001"]))到这里,你已经掌握了网上 90% 教程会讲的内容。但真正决定能不能上生产的,是接下来这部分。
二、新浪财经 API 到底靠不靠谱?
说实话,新浪接口用来学习、做个人 demo 完全够用。但当你要把它塞进一个需要长期稳定运行的系统时,很多你未曾设想过问题会一个接一个冒出来。这也是绝大多数教程避而不谈的部分。
2.1 稳定性
由于这个接口从来没有获得过官方的承认,新浪自然不会承诺它的稳定性。字段顺序、Referer 规则、编码方式全靠社区口口相传,2022 年那次防盗链改动就是典型,一夜之间大量脚本集体 403,没有任何公告,更没有提前通知,说变就变。你的系统建立在一个对方随时可以推翻的地基上。
2.2 只能轮询,没有实时推送
新浪接口是纯 HTTP 拉取,想要实时数据只能不停轮询。这带来两个后果:一是延迟不可控,你的刷新频率就是你的延迟下限;二是极易触发限流,高频轮询同一个 IP,很快就会被风控封禁一段时间。对于逐笔成交、盘口异动这类需要毫秒级响应的场景,轮询架构从原理上就做不到。
2.3 维护成本高
前面那张字段下标表已经说明了问题:数据没有字段名、类型全是字符串、还得手动做单位换算和编码转换。代码里到处是 fields[21] 这样的魔法下标,一旦新浪调整字段顺序,排查起来非常痛苦。
2.4 覆盖与深度有限
新浪在 A 股实时行情上表现不错,但历史数据深度、复权因子、基本面财报、以及港股美股的字段完整度都比较有限,多市场统一接入更是无从谈起。
三、新浪财经 API vs Infoway:一张表看懂差异
下面把两者放在同一维度对比。需要说明:我们无意贬低新浪前辈(Respect ❤️),时至今日,它在免费快速上手这件事上依然是最好的选择之一,但两者的定位本就不同,一个是页面数据源,一个是面向生产的专业行情 API。
| 对比维度 | 新浪财经 API | Infoway API |
|---|---|---|
| 定位 | 网页数据源(非正式 API) | 面向生产的专业行情 API |
| 官方文档 / SLA | 无文档、无 SLA | 完整文档,99.96% SLA |
| 数据格式 | 逗号分隔字符串,靠下标 | 标准化 JSON,带字段名 |
| 接入方式 | 仅 HTTP 轮询 | REST + WebSocket 推送 |
| 实时性 | 取决于轮询频率,易被限流 | 平均 60–150ms 低延迟推送 |
| 编码 | GB2312/GBK,需手动转码 | UTF-8,开箱即用 |
| 防盗链 / 反爬 | 需伪造 Referer,随时可能加码 | 标准 API Key 鉴权 |
| 盘口 | 五档 | 五档 / 十档 |
| 市场覆盖 | A 股 / 港股 / 美股(字段不一) | A股、港股、美股、日股、印度、韩股、加密货币、外汇、期货、大宗商品等 30,000+ 品种 |
| 历史 K 线 | 有,深度有限 | 1 分钟~年线,可翻页取历史 |
| 复权 / 基本面 / 财报 | 基本没有 | 复权因子、三大报表、估值指标 |
| 稳定性 | 随时变格式、无保障 | 面向交易所的生产级稳定性 |
如果你在做玩具,用新浪,如果你在做产品,用一套真正的 API。
四、如何从新浪财经接口迁移到 Infoway API
很多人担心换数据源要大改代码。其实迁移的核心只有两件事:换 symbol 格式和换字段读取方式。Infoway 的返回是带字段名的标准 JSON,读起来比数下标舒服得多。
4.1 symbol 格式对照
新浪用前缀区分市场,Infoway 用后缀,规则更统一:
| 市场 | 新浪格式 | Infoway 格式 |
|---|---|---|
| A 股·上交所 | sh600519 | 600519.SH |
| A 股·深交所 | sz000001 | 000001.SZ |
| 港股 | hk00700 | 00700.HK |
| 美股 | gb_aapl | AAPL.US |
4.2 实时成交明细(对应新浪的实时行情)
新浪那段字符串里的价格、成交量、成交额,在 Infoway 里对应 /stock/batch_trade 这个接口,一次最多查 100 个代码:
import requests
API_KEY = "YOUR_API_KEY" # 官网注册即送 7 天试用
BASE = "https://data.infoway.io"
def infoway_trade(codes):
url = f"{BASE}/stock/batch_trade/{','.join(codes)}"
resp = requests.get(url, headers={"apiKey": API_KEY})
return resp.json()["data"]
for item in infoway_trade(["600519.SH", "000001.SZ"]):
print(item["s"], "价格:", item["p"], "成交量:", item["v"], "成交额:", item["vw"])返回是干净的 JSON,字段一目了然:
{
"s": "600519.SH",
"t": 1750177346523,
"p": "1685.20",
"v": "300",
"vw": "505560.00",
"td": 1
}新浪字段下标 → Infoway 字段迁移对照表(把老代码搬过来时直接对照):
| 含义 | 新浪下标 | Infoway 字段 | 说明 |
|---|---|---|---|
| 标的代码 | 变量名解析 | s | 直接给出 |
| 最新价 | fields[3] | p | 无需换算 |
| 成交量 | fields[8](需 /100) | v | 单位已规整 |
| 成交额 | fields[9](需 /10000) | vw | 直接可用 |
| 成交时间 | fields[30]+[31] 拼接 | t | 毫秒时间戳,无需拼字符串 |
| 主动买卖方向 | 无 | td | 0 默认 / 1 买 / 2 卖,新浪没有 |
4.3 五档盘口(对应新浪的买卖五档)
新浪把买一到卖五塞在下标 10–29 里,Infoway 用 /stock/batch_depth 直接返回结构化的买卖盘数组:
def infoway_depth(codes):
url = f"{BASE}/stock/batch_depth/{','.join(codes)}"
resp = requests.get(url, headers={"apiKey": API_KEY})
return resp.json()["data"]
d = infoway_depth(["000001.SZ"])[0]
# a = 卖盘, b = 买盘;每个都是 [价格数组, 数量数组]
ask_prices, ask_vols = d["a"]
bid_prices, bid_vols = d["b"]
print("卖一:", ask_prices[0], ask_vols[0])
print("买一:", bid_prices[0], bid_vols[0])不用再记 fields[11] 是买一价、fields[21] 是卖一价,买盘 b、卖盘 a、价格和数量分开成数组,第 0 档就是买一/卖一。
4.4 历史 K 线(对应新浪的 getKLineData)
新浪的 K 线接口只能拉近端有限的数据,Infoway 的 /stock/v2/batch_kline 支持从 1 分钟到年线共 12 种周期,单个产品一次可取 500 根,还能按秒级时间戳向前翻页取历史:
def infoway_kline(codes, kline_type=8, num=100):
# kline_type: 1=1分 2=5分 3=15分 4=30分 5=1时 8=日 9=周 10=月
url = f"{BASE}/stock/v2/batch_kline"
body = {"klineType": kline_type, "klineNum": num, "codes": ",".join(codes)}
resp = requests.post(url, json=body, headers={"apiKey": API_KEY})
return resp.json()["data"]
for row in infoway_kline(["600519.SH"], kline_type=8, num=30)[0]["respList"]:
print(row["t"], "开", row["o"], "高", row["h"], "低", row["l"], "收", row["c"])小提示:多个产品一起查 K 线时,每个产品只返回最近 2 根;要取某只票的长历史,请单产品查询并配合
timestamp参数向前翻页。
五、WebSocket 实时推送
前面说过,新浪接口只能轮询,这是它和生产级 API 最本质的差距。Infoway 提供 WebSocket 长连接,服务端主动推送成交、盘口、K 线,延迟从轮询间隔,直接降到毫秒级。下面是一个可直接跑的精简 Python 客户端:
import asyncio, json, uuid, websockets
API_KEY = "YOUR_API_KEY"
WS_URL = f"wss://data.infoway.io/ws?business=stock&apikey={API_KEY}"
def trace():
return str(uuid.uuid4())
async def main():
async with websockets.connect(WS_URL) as ws:
# 订阅实时成交明细(协议号 10000)
await ws.send(json.dumps({
"code": 10000, "trace": trace(),
"data": {"codes": "600519.SH,000001.SZ"}
}))
# 订阅五档盘口(协议号 10003)
await ws.send(json.dumps({
"code": 10003, "trace": trace(),
"data": {"codes": "600519.SH"}
}))
async def heartbeat(): # 每 30 秒心跳,超时会被断开
while True:
await asyncio.sleep(30)
await ws.send(json.dumps({"code": 10010, "trace": trace()}))
asyncio.create_task(heartbeat())
async for msg in ws:
data = json.loads(msg)
if data.get("code") == 10002: # 成交推送
print("成交:", data["data"])
elif data.get("code") == 10005: # 盘口推送
print("盘口:", data["data"])
asyncio.run(main())几个生产环境要点:
订阅请求码 +2 就是对应的推送码(10000→10002 成交、10003→10005 盘口、10006→10008 K 线)
心跳必须保持(30 秒一次),否则会被判定超时断开
断线重连后必须重新发送订阅。股票、加密货币、外汇/期货分别是 business=stock/crypto/common 三个通道,同时订阅只算一个连接。
六、如何选择
结合我们服务大量客户的经验,给出比较中肯的建议:
- 个人学习 / 一次性脚本 / 课程作业:新浪财经 API 足够了,免费、零门槛,本文第一部分够你用。
- 量化研究 / 策略回测:如果只需要 A 股日线历史,新浪 + 一点清洗能凑合;但只要涉及分钟级、多市场、复权或财报数据,用 Infoway 会省掉大量爬虫维护成本。
- 实盘交易 / 高频策略:必须用 WebSocket 推送,轮询接口从原理上就不满足延迟要求,选 Infoway
- 交易所 / 对外产品:这个场景就很明显了,抓取新浪不是好不好用的问题,而是能不能用的问题,Infoway是更好的选择
我们提供 7 天免费试用(官网注册即得),试用期内可以查询全部市场数据,你完全可以先用上面的代码把新浪那部分逻辑平移过来,跑通了再决定。
常见问题(FAQ)
新浪财经 API 为什么突然返回 403 Forbidden?
2022 年下半年起新浪对 hq.sinajs.cn 增加了 Referer 校验,请求头里必须带上 Referer: https://finance.sina.com.cn,否则一律 403。直接在浏览器地址栏打开也会 403,这是正常现象。
新浪接口返回的中文是乱码怎么办?
接口返回的是 GB2312/GBK 编码。用 Python 时手动设置 resp.encoding = "gbk" 再读取;其他语言同理,按 GBK 解码即可。
从新浪迁移到 Infoway,代码改动大吗?
主要改两处:一是 symbol 格式(sh600519 → 600519.SH),二是把”按下标读字符串”改成”按字段名读 JSON”。本文第四章的迁移对照表可以直接照着改,通常半天就能完成平移。
Infoway 支持哪些新浪没有的能力?
最关键的是 WebSocket 实时推送(新浪只能轮询)、标准化 JSON、多市场统一接入(A股/港股/美股/日股/印股/韩股/加密/外汇/期货等)、复权因子与财报基本面数据。
Infoway 有免费额度可以先试吗?
有。官网注册即自动获得 7 天试用会员,试用期内可查询所有市场数据,REST 每分钟 60 次、WebSocket 可订阅 10 个产品,足够完成完整的迁移验证。