
1. 内容整体设计与思路拆解1.1 为什么选东方财富网不选别的数据源做股票数据抓取第一反应往往是新浪财经或者腾讯财经因为这两个老牌接口已经存在十几年了网上教程一抓一大把。但实际用下来你会发现新浪接口返回的数据结构相对简单字段少得可怜想要市盈率、市净率、换手率这些东西还得自己去算。腾讯接口的实时性不错但历史数据这块一直是短板拉长周期就捉襟见肘。东方财富网的接口我前后对比过好几轮决策点就一个它把行情、财务、资金流向、板块概念这些数据都单独拆成了规范化接口字段命名是统一的返回结构也是统一的。写一个通用解析函数就能同时处理沪深A股、港股、美股、基金、债券这个收益在后期维护成本上体现得特别明显。另外一个实际问题就是东方财富的接口对新手极其友好——不需要鉴权不需要申请token请求头也不用搞什么复杂的签名算法直接GET就能拿到标准JSON格式的数据而且频率控制在合理范围内基本上不会触发限制。这篇实战我用的就是东方财富接口目标是5分钟内跑通整条链路请求行情快照、解析关键字段、批量拉取K线、导出本地文件。1.2 一条完整爬虫的执行链路很多新手写爬虫拿过URL就requests.get拿到HTML就正则开捞结果就是数据字段乱七八糟一改页面结构就全线崩溃。我自己踩过这个坑之后总结出一条执行链路先确认数据来源是不是页面直出再去找背后真正的数据服务接口然后用接口返回的JSON直接解析最后才是本地存储和异常兜底。这条链路用大白话解释就是这样——你看到东方财富的网页上有一个股票列表那个表格是前端JavaScript动态渲染出来的你直接把HTML源码拉下来里面根本找不到股票数据找到的只是一堆JS脚本和空壳标签。真正的数据是浏览器在执行JS脚本之后通过异步请求从一个独立的接口拿回来的。所以整个项目的核心不是“爬”HTML而是模拟浏览器去调用那个数据接口然后把返回的JSON解析成Python字典最后按字段提取写入本地文件。从工程角度来说用接口比用页面解析有三个实实在在的好处第一数据结构标准化JSON天然就是为程序解析设计的不需要写一堆正则在HTML里抠数据第二字段完整页面展示哪些字段、接口就返回哪些字段而且往往比页面展示的更全包括页面没有呈现的明细数据第三稳定性好页面改版是家常便饭但底层数据接口很少动不动就改。所以选对路径整个开发成本能降一半以上。2. 核心细节解析与实操要点2.1 东方财富行情接口的完整拆解这个项目里最核心的接口是行情快照接口URL大致长这样http://push2.eastmoney.com/api/qt/clist/get这个接口没那么神秘本质上就是个带参数GET请求返回的是一段标准JSON。其中几个核心参数必须搞清楚是干什么的不然你都不知道自己在请求什么。pn页码从1开始。pz每页返回条数官方最大支持100实测调大到几百也会返回但属于非常规用法不建议这么干。po排序方向1表示降序。np固定写1。ut这个参数是访问令牌第一次看到会觉得是鉴权但其实是一个公开的前端token固定值写死就行。fltt数值精度策略2表示返回带小数的浮点数。invt数值完整性2表示完整返回。fid排序字段f3表示按涨跌幅排序。fs市场代码m:0t:6表示深市主板m:0t:80表示深市创业板m:1t:2表示沪市主板m:1t:23表示科创板多个市场用逗号拼接。fields要返回的字段列表这是整个接口的灵魂从股票代码、名称到最新价、涨跌幅、成交量、市盈率、市净率、总市值、流通市值都在这个参数里控制。举个例子我想抓沪深A股全部股票按涨跌幅降序排列请求参数大致是这样组织起来的。字段部分我用的是最常用的一套组合f12是股票代码f14是股票名称f2是最新价f3是涨跌幅f4是涨跌额f5是成交量f6是成交额f8是换手率f9是市盈率f20是总市值f21是流通市值f23是市净率f62是主力净流入f100是行业板块。用requests批量请求一次能拿回100条沪深A股五千多只翻页五六十次就全部搞定。2.2 为什么5分钟就能跑通全流程这个项目被称为“5分钟搞定”不是说从零开始学习只需要5分钟而是说你有了代码模板之后改一改参数就能立刻抓到自己想要的数据。关键在于代码结构做到了高度模块化——核心请求函数只做一件事就是向接口发请求并返回JSON解析函数只做第二件事就是把JSON里的data字段拆出来转成结构化表格存储函数只做第三件事就是把表格写入CSV或Excel文件。三个函数各干各的互不干扰。你把这个项目的参数从A股换成港股只需要改fs的取值想把排序从涨跌幅换成成交额只需要改fid想多抓几个字段只需要在fields后追加参数。整个改动过程不超过30秒这就是模板化编程的威力。我在实际操作里是这么安排时间的第一分钟看接口返回的数据结构第二分钟写请求函数第三分钟写解析和存储第四分钟全量跑一遍沪深A股第五分钟检查数据完整性、修bug刚好完成闭环。3. 实操过程与核心环节实现3.1 环境准备与依赖安装开始写代码之前先确认手头环境是否齐活儿。我用的是Python 3.9以上版本理论上3.6以上也行但类型标注的高级特性在旧版本上体验会打折扣。需要安装的第三方库就三个requests负责发HTTP请求pandas负责数据处理和存储可以直接用CSV文件读写替代但pandas处理字段类型更顺手。pip install requests pandas安装完验证一下环境是否正常打开Python交互环境依次执行import requests和import pandas不报错就说明环境没问题。如果你用的是Anaconda发行版pandas大概率已经装好了只需要补一个requests就够了。关于编码问题Windows环境下跑Python脚本经常被中文编码坑到我建议在脚本开头加上两行import sys import io sys.stdout io.TextIOWrapper(sys.stdout.buffer, encodingutf-8)这一招在处理中文股票名称的时候尤其好使不会动不动就抛编码异常。3.2 核心代码实现完整可复用的抓取脚本这是整篇博文的重头戏我把完整代码贴在这里同学可以直接复制运行。这个脚本我命名为eastmoney_stock_crawler.py放在任意目录下都能跑不需要额外配置。import requests import pandas as pd import time class EastMoneyCrawler: def __init__(self): self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: http://quote.eastmoney.com/, } self.base_url http://push2.eastmoney.com/api/qt/clist/get self.all_data [] def build_params(self, page, page_size100, marketm:0t:6,m:0t:80,m:1t:2,m:1t:23): params { pn: page, pz: page_size, po: 1, np: 1, ut: bd1d9ddb04089700cf9c27f6f7426281, fltt: 2, invt: 2, fid: f3, fs: market, fields: f12,f14,f2,f3,f4,f5,f6,f7,f8,f9,f20,f21,f23,f62,f100, } return params def fetch_page(self, page, page_size100): try: resp requests.get( self.base_url, paramsself.build_params(page, page_size), headersself.headers, timeout10, ) resp.raise_for_status() data resp.json() if data and data.get(data) and data[data].get(diff): return data[data][diff] return [] except Exception as e: print(f第{page}页请求失败{e}) return [] def parse_row(self, row): return { 股票代码: row.get(f12), 股票名称: row.get(f14), 最新价: row.get(f2), 涨跌幅: row.get(f3), 涨跌额: row.get(f4), 成交量: row.get(f5), 成交额: row.get(f6), 换手率: row.get(f8), 市盈率: row.get(f9), 总市值: row.get(f20), 流通市值: row.get(f21), 市净率: row.get(f23), 主力净流入: row.get(f62), 所属行业: row.get(f100), } def crawl_all(self, max_pages70, page_size100): for page in range(1, max_pages 1): rows self.fetch_page(page, page_size) if not rows: break for row in rows: self.all_data.append(self.parse_row(row)) print(f已抓取第{page}页累计{len(self.all_data)}条) time.sleep(0.5) return pd.DataFrame(self.all_data) if __name__ __main__: crawler EastMoneyCrawler() df crawler.crawl_all(max_pages70) df df.drop_duplicates(subset股票代码, keepfirst) df.to_csv(eastmoney_stock.csv, indexFalse, encodingutf-8-sig) print(f数据抓取完成共{len(df)}条记录已保存至eastmoney_stock.csv)这个脚本的用法很简单直接运行终端会逐页打印进度信息大约40秒到1分钟就能跑完全市场生成的文件用Excel直接打开不会乱码因为utf-8-sig编码就是专门兼容Excel的。脚本里的几个关键点我单独拎出来说一下。fidf3是核心排序参数控制着全市场的排序方式。fetch_page里的异常捕获不是摆设网络抖动、接口超时、返回空数据都是日常捕获异常比裸奔强一百倍。time.sleep(0.5)是必须的单位是秒你的代码跑得再快也要照顾一下服务器的压力这是爬虫工程师的基本素质。3.3 批量抓取K线数据的进阶脚本上面那个脚本拿到的是某一天的交易快照也就是当前所有股票的最新行情。但实战中更多需求是拉取某只股票一段时间内的历史K线做策略回测、趋势判断、均线计算都离不开历史数据。东方财富同样提供了K线接口我顺手也把这块代码给你补上。import requests import pandas as pd def fetch_kline(secid, begin20240101, end20250201, klt101, fqt1): secid: 市场代码.股票代码如 1.600519 表示沪市贵州茅台 klt: K线类型101日K102周K103月K fqt: 复权方式1前复权2后复权0不复权 params { fields1: f1,f2,f3,f4,f5,f6, fields2: f51,f52,f53,f54,f55,f56,f57,f58, ut: 7eea3edcaed734bea9cbfc24409ed989, klt: klt, fqt: fqt, secid: secid, beg: begin, end: end, } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64), Referer: https://quote.eastmoney.com/, } url https://push2his.eastmoney.com/api/qt/stock/kline/get resp requests.get(url, paramsparams, headersheaders, timeout10) resp.raise_for_status() data resp.json() if not data.get(data) or not data[data].get(klines): print(未获取到K线数据请检查secid和日期范围) return pd.DataFrame() klines data[data][klines] rows [] for item in klines: parts item.split(,) rows.append({ 日期: parts[0], 开盘价: parts[1], 收盘价: parts[2], 最高价: parts[3], 最低价: parts[4], 成交量: parts[5], 成交额: parts[6], 振幅: parts[7], }) return pd.DataFrame(rows) if __name__ __main__: df fetch_kline(1.600519, begin20240101, end20250201) if not df.empty: df.to_csv(kline_600519.csv, indexFalse, encodingutf-8-sig) print(fK线数据抓取完成共{len(df)}个交易日已保存至kline_600519.csv)secid这个参数是新手的重灾区。它的格式是“市场代码.股票代码”沪市的股票市场代码是1深市的股票市场代码是0。举个例子贵州茅台在上交所所以是1.600519宁德时代在深交所所以是0.300750。把市场代码写反了接口返回的data就是null你连报错信息都看不懂。这个坑我踩过不只一次现在写爬虫第一件事就是先查股票对应的市场代码。K线接口返回的是一串逗号分隔的字符串不是标准嵌套JSON所以解析时要先split再逐个处理。字段含义在上面代码里已经写清楚了最后把它转成DataFrame存CSV就可以了。4. 常见问题与排查技巧实录4.1 高频问题速查表这个表里的内容是我在实际使用过程中反复遇到的也是各个技术群里新人问得最多的问题。整理成表格方便你快速定位。问题现象可能原因解决方案返回数据为null或空列表secid市场代码写错检查格式沪市1开头深市0开头返回406错误缺少请求头或Referer被拦截补全User-Agent和Referer头中文股票名称乱码CSV存储编码不对使用encodingutf-8-sig保存提示SSL证书错误本地证书过期或代理干扰关闭代理或设置verifyFalse抓取几百条后断掉请求频率过高被临时限制增大time.sleep间隔到2秒以上返回的数据全是0fltt参数设置为0把fltt改成2某些字段缺失字段代码拼写错误核对fields参数中的f编号其中最坑的是SSL证书错误这在公司网络或者开了代理的环境下特别容易出现。遇到这个问题的朋友直接加一行verifyFalse同时用urllib3.disable_warnings()把警告消掉虽然正规爬虫不应该这么做但你是为了学习数据抓取怎么快怎么来生产环境再来处理证书问题。4.2 反爬策略与合规提醒东方财富这个接口本身反爬力度不大只要不是几十并发一百并发去狂轰滥炸正常速度抓取基本不会触发限制。但这里我想认真说一件事爬虫能力越大责任就越大绝对不能越界。实际操作中有三条红线你心里要有数。第一控制请求频率。我在代码里每次请求间隔0.5秒一天全量跑几十次问题不大但你要是用多线程开50个并发疯狂刷接口被封是小给目标服务器造成压力就不好了。爬虫的初衷是高效获取公开数据不是给服务器制造负担。第二尊重数据使用边界。抓下来的数据自己分析研究、做教学演示都没问题但大规模商用需要评估数据合规风险。股票行情数据也是版权资产数据商的授权费用动辄几十万起步你白嫖就低调点。第三不要把爬取的接口直接公开部署到公网服务上你没有必要也不可能通过这种方式盈利。个人学习和研究才是这个项目最合适的定位。4.3 代码复用与扩展方向脚本写完之后你最需要知道的是怎么把它改造成自己真正想要的样子。如果你只想抓某个板块的数据比如银行板块、白酒板块、新能源车板块不需要走全市场那么把fs参数改成对应的板块代码即可。东方财富的板块代码也是统一格式比如b:BK0428表示白酒板块你可以先在网页端打开板块页面F12打开开发者工具在Network面板里看请求参数搜fs字段就能找到当前板块的代码。如果你想抓港股把fs参数改成m:128t:3,m:128t:4,m:128t:1,m:128t:2即可港股的市场代码是m:128。再比如你想做定时监控写一个while循环每隔5分钟调用一次抓取脚本把结果追加到数据库里这就是一个简易版实时行情监控系统了。再往后可以接上可视化库比如pyecharts或者streamlit把抓下来的数据画成K线图、资金流向图、板块热力图整个项目就从爬虫实战升级成了数据分析展示全栈项目。我个人在实际操作中的体会是爬虫项目能不能落地百分之六十取决于接口选得好不好百分之三十取决于代码结构清晰不清晰剩下百分之十才是反爬防盗那些花活儿。东方财富这个选型配合今天这套模板代码足够覆盖90%的个人量化分析和数据展示需求。最后再分享一个小技巧抓完数据之后用pandas的df.info()和df.describe()先看一眼数据类型和分布很多脏数据问题在这个环节就能暴露出来别急着拿去算策略数据质量永远是第一位的。