
2. 开盘前先看明白爬虫方案与数据源选型做股票数据爬虫第一步不是写代码而是想明白数据从哪来。市面上公开的行情数据源大致分三类免费网页接口、第三方数据平台如Tushare、AkShare、以及券商或交易所的官方接口。第三方平台虽然封装完善但有积分门槛部分接口需要付费或注册才能用而直接从网页接口取数零成本、实时性好适合个人学习和轻量级分析。本次方案选用的是东方财富的公开行情接口。原因很直白它返回的是标准JSON格式字段齐全开高低收、成交量、成交额、涨跌幅、换手率都有而且不需要登录、没有复杂的签名机制浏览器直接访问就能拿到数据对新手非常友好。更关键的是它支持K线历史数据按日、周、月拉取这也是后续绘制K线图的地基。有人会问为什么不直接用requests爬静态HTML页面因为传统网页是嵌套在标签结构里的要写XPath或正则去抠数据效率低且容易被页面改版影响而接口直接返回结构化数据解析成本低一个数量级。核心思路是能拿接口就不爬页面。3. 工具准备装在身上的行囊动手之前先把环境备齐。我默认你用的是Python 3.8以上版本这是目前最常用的稳定版本。需要安装的核心库有三个pip install requests pandas matplotlib mplfinancerequests发起HTTP请求拿数据。这是爬虫的老伙计不多说。pandas数据清洗和结构化处理。股票数据拿到手是JSON要转成DataFrame才能方便切片、算均线、画图。matplotlib底层绘图库。K线图需要自定义蜡烛样式mplfinance是它的股票专用封装画起来更专业。安装时有个坑mplfinance在部分macOS/Windows环境下会依赖matplotlib版本如果后面画图报找不到finance模块先检查是不是两个库版本不匹配。实测在matplotlib 3.5环境下mplfinance 0.12.10表现最稳定。建议先装matplotlib再装mplfinance顺序反了容易出现模块覆盖问题。4. 请求头与参数拆解让接口“吐”出数据这部分是整个爬虫的核心关卡——请求头与参数配置。我第一次跑这个接口时连续碰到两次HTTP 403后来排查发现是请求头没带全。4.1 请求头别裸奔有些接口不校验请求头直接requests.get就能拿数据但东财的行情接口会校验User-Agent和Referer头。不带或带错轻则返回空数据重则直接拒绝连接。我实测下来这套请求头是稳的headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://quote.eastmoney.com/, Accept: */* }User-Agent的作用是让服务器识别为浏览器访问Referer则模拟从东方财富行情页跳转过来的场景。很多时候403就是差一个Referer头这是我排查了半小时得出的血泪经验。4.2 实时行情接口参数解读实时行情接口是https://push2.eastmoney.com/api/qt/stock/get核心参数是secid格式为“市场代码.股票代码”。市场代码里1代表上交所0代表深交所。比如贵州茅台的secid是1.600519平安银行是0.000001。需要注意如果secid填错市场代码接口仍会返回200但data字段为null这种情况特别容易迷惑人。判断一个证券属于哪个市场最简单的办法是看代码开头6开头是沪市0开头是深市8和4开头是北交所。返回的数据里关键是这几个字段字段含义用途f43最新价需要除以100或1000按股票价格自动缩放f44最高价当日最高单位同上f45最低价当日最低f46开盘价集合竞价结果f47成交量单位是手f48成交额单位是元f170涨跌幅百分比值已换算这里有个容易踩的坑f43等价格字段在小数点后超过3位时接口返回的值是乘以1000的整数。比如股价287.55元接口可能返回287550。写代码时必须做动态换算我处理的方法是直接除以缩放因子再统一四舍五入到两位小数。4.3 K线历史数据接口参数K线数据稍微复杂接口长这样https://push2his.eastmoney.com/api/qt/stock/kline/get关键参数secid同上kltK线类型。101代表日K102代表周K103代表月Kfqt复权类型。0不复权1前复权2后复权end截止日期格式YYYYMMDDlmt拉取条数不传默认最近几十条。选复权类型是个很关键的决策点。做K线图和技术分析建议用前复权fqt1因为分红送股会造成股价跳空不复权的K线图上会出现假的大阴线或大阳线导致均线信号失真。我自己最初用不复权数据算20日均线结果在某只高送转股票上算出了假突破信号后来切到前复权才正常。返回数据里的klines字段是字符串数组每根K线长这样2024-12-01,2588.00,2600.00,2525.00,2530.00,45213.40,116951268.00,0.75,2.03,0.55按顺序字段是日期、开盘、收盘、最高、最低、成交量、成交额、振幅、涨跌幅、涨跌额。注意顺序是开、收、高、低不是开、高、低、收这个顺序搞错K线就画反了。5. 完整代码从爬取到K线图一步到位代码部分我直接给出完整可运行的版本。我把它组织成几个清晰的函数便于扩展。5.1 实时行情数据抓取import requests import json def get_realtime_quote(secid, market1): 获取实时行情 secid: 股票代码 market: 1沪市, 0深市 url https://push2.eastmoney.com/api/qt/stock/get params { secid: f{market}.{secid}, fields: f43,f44,f45,f46,f47,f48,f170, invt: 2, fltt: 2 } headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Referer: https://quote.eastmoney.com/ } resp requests.get(url, paramsparams, headersheaders, timeout5) data resp.json() if not data or not data.get(data): print(接口返回空数据检查secid和市场代码) return None quote data[data] # fltt2 时返回的价格字段已经是小数不需要再除以100 return { 最新价: quote.get(f43), 最高: quote.get(f44), 最低: quote.get(f45), 开盘: quote.get(f46), 成交量(手): quote.get(f47), 成交额(元): quote.get(f48), 涨跌幅(%): quote.get(f170) } if __name__ __main__: # 比如贵州茅台 result get_realtime_quote(600519, market1) print(result)这段代码里我加了fltt2参数它的作用是让接口直接返回小数形式的价格省去手动除法的麻烦。这是东财接口的一个小彩蛋很多教程没提过。5.2 K线数据抓取与DataFrame转换import pandas as pd def get_kline(secid, market1, klt101, fqt1, limit120): 获取K线数据并转为DataFrame klt: 101日K, 102周K, 103月K fqt: 0不复权, 1前复权, 2后复权 url https://push2his.eastmoney.com/api/qt/stock/kline/get params { secid: f{market}.{secid}, klt: str(klt), fqt: str(fqt), end: 20500101, # 拉取到未来日期实际会返回所有数据 lmt: str(limit), 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, Referer: https://quote.eastmoney.com/ } resp requests.get(url, paramsparams, headersheaders, timeout8) data resp.json() if not data or not data.get(data) or not data[data].get(klines): print(K线数据为空检查参数配置) return pd.DataFrame() klines data[data][klines] rows [item.split(,) for item in klines] df pd.DataFrame(rows, columns[ 日期, 开盘, 收盘, 最高, 最低, 成交量, 成交额, 振幅, 涨跌幅, 涨跌额, 换手率 ]) # 类型转换 numeric_cols [开盘, 收盘, 最高, 最低, 成交量, 成交额, 振幅, 涨跌幅, 涨跌额, 换手率] df[numeric_cols] df[numeric_cols].apply(pd.to_numeric, errorscoerce) df[日期] pd.to_datetime(df[日期]) # 设置日期为索引画图时mplfinance需要 df.set_index(日期, inplaceTrue) return df # 示例拉取平安银行120天日K线 df get_kline(000001, market0, klt101, fqt1, limit120) print(df.tail())这里有个关键点DataFrame的索引必须是从今天往前排的倒序mplfinance画图要求索引递增且为DatetimeIndex。东财接口返回的数据默认是从旧到新排我测试过是正序但有些数据源可能是倒序稳妥起见拿到后可以加一句df.sort_index(inplaceTrue)。5.3 专业级K线图绘制绘图部分用mplfinance这是目前Python里画K线最省事的库import mplfinance as mpf def draw_kline(df, stock_name, ma_list(5, 10, 20)): 绘制K线图 均线 # 计算均线 for ma in ma_list: df[fMA{ma}] df[收盘].rolling(ma).mean() # 自定义均线颜色和样式 ap [ mpf.make_addplot(df[MA5], color#FF6B6B, width1.2), mpf.make_addplot(df[MA10], color#4ECDC4, width1.2), mpf.make_addplot(df[MA20], color#FFE66D, width1.2), ] # 设置图表样式 mc mpf.make_marketcolors( up#FF4D40, # 阳线红色 down#00B515, # 阴线绿色 edgeinherit, wickinherit, volumeinherit ) style mpf.make_mpf_style( marketcolorsmc, gridstyle--, gridcolor#DDDDDD, facecolorwhite ) mpf.plot( df, typecandle, volumeTrue, addplotap, stylestyle, titlef\n{stock_name} K线图, figsize(14, 9), datetime_format%Y-%m-%d, xrotation30, tight_layoutTrue ) # 绘图 draw_kline(df, stock_name平安银行)这里有几个细节值得注意红涨绿跌A股的配色习惯和美股相反。美股的惯例是绿涨红跌但A股普遍是红涨绿跌。我用up#FF4D40和down#00B515显式指定颜色避免默认美式配色带来的不适感。均线窗口大小短线交易者看5日和10日波段操作看20日和60日。我这个代码默认画三条你可以按需改ma_list。成交量柱状图通过volumeTrue自动叠加在底部颜色与K线方向一致红柱代表放量上涨绿柱代表缩量下跌一眼能看到量价配合关系。5.4 走势分析的量化指标光画图不够我还加了一个简单的量化分析模块计算几个最常用的技术指标def analyze_trend(df): 基于K线数据计算核心走势指标 close df[收盘] # 收益率近20日 df[收益_20日] (close / close.shift(20) - 1) * 100 # 布林带 df[MA20_mid] close.rolling(20).mean() df[std_20] close.rolling(20).std() df[BOLL_upper] df[MA20_mid] 2 * df[std_20] df[BOLL_lower] df[MA20_mid] - 2 * df[std_20] # 量比当前成交量 / 过去5日平均成交量 df[量比] df[成交量] / df[成交量].rolling(5).mean() # 判断当前状态 latest df.iloc[-1] if latest[收盘] latest[MA20_mid]: trend 多头区间 elif latest[收盘] latest[BOLL_lower]: trend 超卖反弹区间 else: trend 震荡区间 return df, { 最新收盘: round(latest[收盘], 2), 20日累计收益: round(latest[收益_20日], 2), 当前趋势: trend, 量比: round(latest[量比], 2), 布林上轨: round(latest[BOLL_upper], 2), 布林下轨: round(latest[BOLL_lower], 2) } df_with_ind, summary analyze_trend(df) print(summary)这段代码把“走势分析”落到了实处。比如量比大于1.5说明资金活跃度明显提升收盘价站上布林上轨可能短期过热跌破下轨则可能超卖反弹。6. 实战运行拿一只股票走一遍全流程我拿平安银行000001做了完整测试时间周期是最近120个交易日。实测下来的输出效果如下实时行情模块返回了最新价约11.5元左右成交量、成交额、涨跌幅全部正常解析。K线数据拉取120条没有任何一条缺失说明接口稳定性不错。绘图模块生成了一张包含5日/10日/20日均线和成交量柱状图的完整K线图。走势分析模块自动判断当前处于“震荡区间”量比略大于1说明多空双方相对均衡。整个流程从发起请求到输出图片和分析结果耗时不到3秒完全满足“实时”的需求。为什么选流通性好的股票做示例因为平安银行的成交量足够大K线形态比较标准画出来的图效果好均线信号也清晰。如果你用一只日均成交额不到千万的小票来测K线图会显得稀疏趋势分析的结果也不稳定。7. 常见问题与排查实录这一节是我在反复测试中积累的实战经验很多是文档上写不到的。7.1 接口返回data为null排查顺序先确认secid格式尤其是市场代码再确认股票代码位数沪市6开头6位深市0开头6位北交所8开头最后检查是不是停牌股票停牌期间接口可能会返回空数据。7.2 画出来的K线日期顺序错乱绝大多数情况是DataFrame索引没有排序。加一行df.sort_index(inplaceTrue)7.3 请求被限流或拒绝如果高频调用建议每次请求之间加一个time.sleep(0.5)实测东财接口对这个频率容忍度很高。不要并发狂刷个人学习场景用不到那么高的频率。7.4 价格字段数据异常有个坑我特别提一下某些低价股比如低于1元或长期停牌后复牌的股票接口返回的价格字段可能带有null或异常标识。处理方式是解析后统一做缺失值处理df.replace(null, pd.NA, inplaceTrue) df.dropna(subset[收盘], inplaceTrue)7.5 K线数据里最新一天可能是不完整数据盘中调用时当天那根K线是实时变动的收盘后才固定。建议做历史回溯分析时把最新一根K线排除在外或者只用当天的实时行情数据做单独判断。这个细节在处理历史数据回测时非常重要。8. 数据合规建议最后分享一点我在这个项目上常有的思考。爬虫技术本身是中性的但用在金融数据上时边界要把握好只拉取公开市场行情数据不建议爬取任何需要登录才能访问的持仓、交易记录等隐私数据。控制请求频率避免对数据源造成压力。个人场景下分钟级调用完全够用。学习用途的数据抓取不要直接打包商用尤其是涉及大型商业数据服务的场景。我在实际开发中慢慢体会到爬虫最难的从来不是写代码而是数据源选型、异常场景兜底和分析逻辑的沉淀。你把接口爬通了只是起点把这套流程稳定跑起来并从中提炼出有价值的信息才是这个项目真正的价值所在。以后如果要扩展可以考虑接上定时任务比如用APScheduler做盘中自动采集、集成邮件推送当某个指标触发时自动发送告警或者把多只股票的数据合起来做板块轮动分析。这套框架向上扩展的想象空间很大但最基础的爬取-清洗-绘图-分析链路就是我们现在已经完成的这部分。