尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

Python抓取东方财富A股K线数据:无需Token的接口直连方案

发布时间:2026/9/29 19:00:17

资讯中心
01
ARTICLE

Python抓取东方财富A股K线数据:无需Token的接口直连方案

Python抓取东方财富A股K线数据:无需Token的接口直连方案
先聊点实在的。做A股数据抓取很多人一上来就想到去装tushare、baostock这类现成库但它们要么需要注册token要么对历史数据条数有限制要么接口字段不够全。我在实际做量化回测和个人盯盘工具的时候越来越倾向直接对接数据源本身。东方财富作为国内活跃度极高的财经平台它的行情接口设计得很规整返回的是标准JSON不需要登录、不需要签名、不需要Token而且字段覆盖了开高低收、成交量、成交额、换手率等常用指标。这篇文章就带你把这条路径跑通目标是——在5分钟内用Python稳定抓到东方财富的A股K线数据拿到可以直接入库或用于分析的干净DataFrame。适合谁来参考如果你正在做股票数据可视化、写量化策略回测脚本或者想给自己的分析工具补上实时行情源这篇都很对口。如果你是完全零基础也不用慌代码可以直接跑通我会把每一步的原理和坑点都拆开讲清楚。1. 方案选型为什么直接抓东方财富接口而不是用tushare1.1 现成库的痛点很多人第一反应是用tushare不就行了吗确实tushare做了很好的数据封装但它有几个在实际使用中绕不开的麻烦。首先是权限问题tushare很多高频或全量接口需要积分积分要通过贡献或付费获取对个人开发者来说门槛不低。其次是稳定性我有段时间用tushare跑定时任务偶尔会遇到服务端限流或返回超时追查起来比较被动。当然tushare的数据质量是好的这里只是说在“单纯想要最新行情数据”这个场景下它未必是最轻量的选择。再看baostock它胜在免费且无需注册但数据更新频率偏慢分钟级数据支持也不够灵活。如果只是做日线级别的选股扫描勉强能用一旦涉及到盘中实时行情或者需要自定义复权方式它就比较吃力了。所以我干脆研究了一下东方财富网页端的请求链路发现它有一套非常成熟的行情接口直接通过HTTP请求就能拿数。1.2 东方财富接口的核心优势那东方财富这套接口到底好在哪我总结了几个关键点公开可用普通用户在网页端看行情时浏览器发起的就是这些请求没有任何加密签名。数据全面日线、周线、月线、分钟线都能给字段包含前收盘、今开盘、最高、最低、成交量、成交额、振幅、换手率等已经覆盖绝大部分分析需求。响应速度快请求一个股票的全部日线数据通常300毫秒左右就能返回比渲染完整网页再解析要快一个数量级。不需要维护登录状态Cookie、Session都不用管requests直接打过去就是200。这套方案的本质是直接绕过网页渲染层去拿数据接口返回的JSON。这种思路在爬虫里叫“接口直连”比解析HTML或者用playwright模拟浏览器都要稳定因为JSON结构是固定的只要接口不升版本代码就能一直跑。1.3 什么时候才需要playwright这类渲染工具想必你也在热搜词里看到了playwright。这里顺便说清楚适用场景。playwright这类浏览器自动化工具适合对付那种数据是通过JavaScript动态渲染、并且接口有加密参数的网站。比如有些站点请求接口时要带动态tokentoken是前端JS算出来的你直接requests请求拿不到数据那就必须用playwright或pyppeteer去构造一个真实浏览器环境。但在东方财富这个场景里不需要走这条路。即便页面上的K线图是JS画的它的数据源头还是那个JSON接口URL可以直接拼出来。永远记得一个原则优先找接口找不到接口再考虑渲染。用playwright爬静态信息比较笨重而且消耗资源。2. 核心细节解析看懂接口URL的每个参数2.1 接口原型拆解我们实际要用的接口长这样https://push2his.eastmoney.com/api/qt/stock/kline/get?secid1.600519fields1f1,f2,f3,f4,f5,f6fields2f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61klt101fqt1beg0end20500101看着长其实规律很强。我做接口分析的习惯是先把一个完整URL丢到浏览器里看返回的JSON长什么样再回头反推每个参数的作用。这个接口的关键参数我整理成了下表参数含义示例值补充说明secid证券唯一标识1.600519前缀1代表沪市0代表深市后面是股票代码kltK线周期101101日线102周线103月线60分钟线30分钟线等fqt复权方式10不复权1前复权2后复权beg开始日期00表示从最早数据开始end结束日期20500101用一个超大日期表示取到最新fields1基础字段组f1-f6固定写法即可fields2K线数据字段组f51-f61每一列对应的具体指标fields2这组参数决定了数据返回什么我逐个拆一下f51是日期f52是开盘价f53是收盘价f54是最高价f55是最低价f56是成交量f57是成交额f58是振幅f59是涨跌幅f60是涨跌额f61是换手率。这个顺序是固定的后面解析的时候直接对应列名即可。2.2 secid编码规则与常见坑点secid是这套接口里最容易出错的参数。规则是市场前缀加股票代码1开头是上海证券交易所0开头是深圳证券交易所。但这里有个细节特别坑北交所的股票前缀是0还是1我实测过北交所用的是0。所以判断规则不能简单理解为“6开头就一定是1”比如科创板股票688开头它依然在上海交易所前缀就是1。深市的000、002、300前缀都是0。实际操作中最稳的方法是用接口去自动匹配而不是人肉判断。东方财富有一个搜索接口输入股票名称或代码返回里带secidhttps://searchapi.eastmoney.com/api/suggest/get?input贵州茅台type14tokenD43BF722C8E33BDC906FB84D85E326E8我平时会先跑这个搜索接口拿secid再交给行情接口去拉数据。这样写代码就会更健壮不怕用户输入的是“贵州茅台”还是“600519”统一转换成标准secid。2.3 复权因子的选择逻辑复权这里值得多说几句。fqt参数有三个值0是不复权1是前复权2是后复权。做技术分析的朋友最好统一用前复权数据因为前复权会以当前价格为基准对历史价格做调整这样均线、MACD这些指标的计算才不会因为除权除息出现假信号。但如果你要做的是计算真实收益率或回测资金曲线前复权的历史价格是失真的——因为它是站在今天往回看的视角。后复权则相反它保持历史价格是真实价格调整的是当前价格。我做回测时习惯同时拉一份不复权和一份前复权不复权用于计算真实涨跌幅前复权用于画技术指标图各有分工。3. 完整代码实现从零搭建一个可复用的爬虫脚本3.1 基础版核心请求逻辑先把最核心的部分写出来。这一步的目标就是——用最小代码量把数据完整拿下来。import requests import pandas as pd def get_kline(secid: str, klt: int 101, fqt: int 1) - pd.DataFrame: url https://push2his.eastmoney.com/api/qt/stock/kline/get params { secid: secid, klt: klt, fqt: fqt, beg: 0, end: 20500101, fields1: f1,f2,f3,f4,f5,f6, fields2: f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61, } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 } resp requests.get(url, paramsparams, headersheaders, timeout10) data resp.json() if data.get(data) is None or data[data].get(klines) is None: raise ValueError(f接口返回异常: {data}) rows [line.split(,) for line in data[data][klines]] df pd.DataFrame(rows, columns[ date, open, close, high, low, volume, amount, amplitude, pct_chg, change, turnover ]) for col in [open, close, high, low, volume, amount, amplitude, pct_chg, change, turnover]: df[col] pd.to_numeric(df[col], errorscoerce) return df if __name__ __main__: df get_kline(1.600519, klt101, fqt1) print(df.tail(10))这段代码跑起来是什么效果你会看到最近10个交易日的日K线数据日期、开高低收、成交量、涨跌幅都在里面。从发起请求到打印结果没有多余的噪音整个流程能控制在半秒以内。3.2 为什么用params而不是直接拼URL注意我上面用的是requests的params参数而不是把完整的URL字符串直接拼出来。这个习惯很重要。当你的URL参数比较多的时候如果手动拼字符串很容易漏掉某个参数或者因为某个值里带了特殊字符导致URL解析失败。requests的params会自动帮你处理URL编码同时还能保持代码更易读。还有一点requests默认会带上Accept-Encoding服务器返回的数据通常是gzip压缩过的。requests会自动解压这一层我们不需要手动处理。headers里加一个常见的User-Agent就行主要是为了模拟浏览器环境有些反爬策略会拦截默认的python-requests标识。3.3 进阶版自动识别secid与批量拉取基础版跑通之后我建议直接把它升级成带自动识别secid的版本。这一版在实际工程里更好用因为你不需要记住前缀规则直接丢代码或者名字给它就行。import requests import pandas as pd from typing import Union SEARCH_URL https://searchapi.eastmoney.com/api/suggest/get def get_secid(keyword: str) - str: params { input: keyword, type: 14, token: D43BF722C8E33BDC906FB84D85E326E8, } resp requests.get(SEARCH_URL, paramsparams, timeout10) data resp.json() items data.get(QuotationCodeTable, {}).get(Data, []) if not items: raise ValueError(f未找到标的: {keyword}) item items[0] market_type str(item.get(MarketType, )) code item.get(Code, ) secid f{market_type}.{code} return secid def get_kline_by_keyword(keyword: str, klt: int 101, fqt: int 1) - pd.DataFrame: secid get_secid(keyword) return get_kline(secid, klt, fqt) if __name__ __main__: df get_kline_by_keyword(贵州茅台, klt101, fqt1) print(df.tail(5))搜索接口返回的数据里有一个MarketType字段这个字段的值就是secid前缀。我实测过贵州茅台返回1平安银行返回0跟规则完全吻合。不过要注意的是搜索接口可能会返回多个匹配项比如你搜“茅台”可能同时返回贵州茅台和茅台相关基金。稳妥的做法是取第一条或者根据用户输入是6位数字还是中文字符做不同处理。如果输入的是纯数字代码直接判断首位6开头用1前缀0、2、3开头用0前缀这也是一种可靠的兜底方案。3.4 分钟级数据的玩法除了日线分钟级数据其实是很多做短线的朋友关心的高频数据。klt参数这里需要重点说明我整理出一份常用对照表klt值周期适用场景11分钟线日内T0分析55分钟线短线择时1515分钟线波段判断3030分钟线日内趋势6060分钟线日内趋势101日线标准技术分析102周线中期趋势103月线长期趋势分钟级数据有一点要注意beg和end参数对分钟线的数据条数有实际影响。东方财富的分钟线接口单次最多返回大约57000条数据对于1分钟线来说大约能覆盖近3个月。如果需求是拉取一年以上的分钟数据建议按时间切片分段请求最后再拼接。def get_kline_period(secid: str, klt: int, start: str, end: str, fqt: int 1) - pd.DataFrame: url https://push2his.eastmoney.com/api/qt/stock/kline/get params { secid: secid, klt: klt, fqt: fqt, beg: start, end: end, fields1: f1,f2,f3,f4,f5,f6, fields2: f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61, } headers {User-Agent: Mozilla/5.0} resp requests.get(url, paramsparams, headersheaders, timeout10) data resp.json() rows [line.split(,) for line in data[data][klines]] df pd.DataFrame(rows, columns[ date, open, close, high, low, volume, amount, amplitude, pct_chg, change, turnover ]) return df调用方式也很简单get_kline_period(1.600519, 1, 2024-01-01, 2024-03-01)一次性把2024年头两个月的1分钟线全拉下来。如果你要跑连续几个月的分钟数据就在外面套一个日期列表循环每次请求之间sleep 0.5秒避免给服务器造成压力。4. 实操中遇到的典型问题与排查技巧4.1 返回格式不对或字段缺失这个是最常见的问题。核心原因通常是fields2参数写错了或者接口更新了返回结构。遇到这种情况我的排查思路很固定先直接在浏览器里打开接口URL看原始JSON结构。如果浏览器能正常显示说明接口本身没问题那就是Python请求的参数出了问题。拿浏览器里的JSON和代码返回的JSON逐字段对比很容易定位到差异。另外注意接口返回的data字段为空时大概率是secid不对。比如把深市股票配成了1.000001接口不会报错但返回的data是null。这种“静默失败”特别容易让人怀疑是网络问题其实是市场前缀搞错了。4.2 请求频率限制与封禁风险东方财富的接口对普通的低频率请求比较宽松但你若用for循环一次性拉几千只股票每只间隔不到100毫秒很容易触发服务端的限流表现是请求返回超时或者直接拒绝连接。我实测下来其实不需要去试探它的极限在哪里更合理的做法是把批量任务控制在每秒1到2次请求并对每只股票做一次指数退避重试。import time import random def fetch_with_retry(secid: str, retries: int 3) - pd.DataFrame: for i in range(retries): try: return get_kline(secid) except Exception as e: wait (i 1) * 1 random.random() print(f第{i1}次请求失败{wait:.1f}秒后重试: {e}) time.sleep(wait) raise RuntimeError(f重试{retries}次仍然失败: {secid})这种重试机制能大幅度提升批量任务的成功率。重试间隔用随机值而不是固定值是为了避免多个线程同时重试时再次撞在一起形成“惊群效应”这是并发编程里的一个教训。4.3 pandas列类型数值化从接口拿到的原始数据所有字段都是字符串。如果你直接用df[close]去做计算会得到一堆字符串拼接的结果比如“1699”“1”得到“16991”。这就尴尬了。解决方式是在DataFrame创建后立即做类型转换。我在上面的代码里用pd.to_numeric把所有价格和成交量字段一次性转成数值。这一步是标准操作不要偷懒。坐标轴画图、计算收益率、做因子分析全部依赖于数值列。4.4 时区与日期边界问题东方财富返回的日期字符串是东八区时间不携带时区信息。如果你在本地跑脚本且本地时区不是东八区直接用date列去做时间聚合时可能会出现偏移。建议在拿到数据后统一清洗一次df[date] pd.to_datetime(df[date]) df df.set_index(date).tz_localize(Asia/Shanghai)对于做A股日线分析把索引统一成带时区的东八区时间后续跟其他数据源做对齐时能少踩很多坑。另外要注意交易日历问题A股有节假日休市数据中的日期就是实际的交易日不需要自己额外过滤周末。5. 扩展场景从K线数据到完整的量化分析流程5.1 计算技术指标数据抓回来之后必然要做的一件事是算指标。很多人会引入TA-Lib但TA-Lib在Windows上安装比较折腾。我建议日常用pandas直接算比如20日均线、MACD、RSI这些都不复杂。df[ma20] df[close].rolling(window20).mean() df[ma60] df[close].rolling(window60).mean() ema12 df[close].ewm(span12, adjustFalse).mean() ema26 df[close].ewm(span26, adjustFalse).mean() df[dif] ema12 - ema26 df[dea] df[dif].ewm(span9, adjustFalse).mean() df[macd] (df[dif] - df[dea]) * 2这些列加进去之后你就可以直接画K线叠加均线和MACD副图。整个分析链路从数据获取到可视化不需要依赖任何第三方数据库一个脚本全搞定。5.2 批量抓取全市场股票列表除了单只股票K线有时需要全市场股票列表。东方财富同样提供了接口一秒就能拉回所有A股代码https://push2.eastmoney.com/api/qt/clist/get?pn1pz5000po1np1fltt2invt2fidf3fsm:0t:6,m:0t:80,m:1t:2,m:1t:23fieldsf2,f3,f4,f5,f6,f7,f8,f9,f10,f12,f14fs参数的含义我解释一下m:0代表深市m:1代表沪市t:6是深市主板t:80是创业板t:2是沪市主板t:23是科创板。这样组合起来正好覆盖所有A股标的。返回的f12是股票代码f14是股票名称f2是最新价。拿到这个列表后再配合前面的get_kline就能实现全市场数据扫描。5.3 数据落库与增量更新等到数据量大了建议从DataFrame落盘到SQLite或CSV。我自己喜欢用SQLite因为轻量、不需要单独起服务一个文件搞定。增量更新时先查一下库里已有的最大日期再从这个日期开始拉取新数据直接append效率高也不会重复存储。import sqlite3 def save_to_sqlite(df: pd.DataFrame, db_path: str, table_name: str) - None: conn sqlite3.connect(db_path) df.to_sql(table_name, conn, if_existsappend, indexFalse) conn.close()配合一个定时任务工具每交易日收盘后自动拉一次数据入库就形成了一套完整的个人行情数据库。6. 合规与长期使用的几点建议无论是抓数据做个人分析还是用于学习研究都要注意几个边界问题。首先东方财富这些接口本身是网页端公开使用的数据通道个人以合理频率取数用于学习和研究风险和压力相对较小。但千万不要拿去做成对外提供数据服务的商业产品这会直接触及数据版权问题。其次是代码的维护成本。接口字段和URL有变动可能建议代码里把URL和字段列表集中定义为常量不要散落在各个函数里。这样接口一旦调整只需要改一处。最后关于“因爬虫入狱”这类热搜词的提醒爬虫本身不违法但需要关注抓取对象的协议、频率和个人隐私信息。做A股行情数据抓取只取公开行情不碰用户信息不绕过权限控制频次保持在合理区间就不会触及红线。用技术做正经事这条路才能走得长。根据我个人的经验东方财富这套接口是个人开发者获取A股行情的一个非常顺手的源头比起注册各种数据平台帐号要省心得多。用requests直连拿JSON代码写起来干净跑起来稳定扩展空间也很大。建议你照着上面的代码先跑通一只股票再逐步扩展成自己的全市场行情工具箱。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

◈

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

◐

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

▲

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。