Python爬虫实战:从东方财富网抓取股票K线数据并分析

发布时间:2026/7/30 4:45:53
Python爬虫实战:从东方财富网抓取股票K线数据并分析 1. 项目概述与核心价值最近在复盘自己过去一年的投资策略时发现一个挺普遍的问题很多分析都停留在“感觉”层面缺乏足够的数据支撑。比如想看看某个行业龙头股在特定政策发布前后的股价和成交量变化或者想批量分析一批自选股的市盈率、市净率等基础指标手头却没有现成的、结构化的数据。去各大金融数据终端买服务对于个人投资者或者小团队来说成本又太高。这时候自己动手写个爬虫从像东方财富网这样的公开数据源抓取信息就成了一个非常实际且高效的解决方案。这个“简单爬虫东方财富网股票数据爬取”项目核心目标就是解决这个痛点。它不是什么复杂的量化交易系统而是一个聚焦于实用性和可复现性的工具。通过Python脚本我们可以自动化地从东方财富网获取指定股票的日K线数据、财务摘要等公开信息并保存到本地Excel文件中为后续的数据分析、可视化或策略回测提供原材料。整个过程不涉及任何付费API完全基于公开网页对于学习Python爬虫、数据分析入门或是需要定期获取股票数据的朋友来说都是一个绝佳的练手项目。为什么选择东方财富网首先它的数据覆盖面广A股、港股、美股、基金、期货等都有且更新及时。其次其网页结构相对规整数据通常以JSON格式嵌入在页面中对于爬虫来说比较“友好”不需要处理过于复杂的动态渲染。最后作为个人学习研究用途从这类公开网站获取数据是常见的做法但务必牢记合规与善意爬取的原则控制请求频率避免对目标服务器造成压力。2. 环境准备与核心工具选型工欲善其事必先利其器。在开始写代码之前我们需要搭建好开发环境并选择趁手的工具库。这个项目不依赖复杂的框架几个经典的Python库就足够了。2.1 Python环境与必备库安装首先确保你的电脑上安装了Python建议3.7及以上版本。接下来通过pip安装我们需要的库。打开你的命令行终端CMD、PowerShell或Terminal逐行执行以下命令pip install requests pip install pandas pip install lxmlrequests: 这是Python中用于发送HTTP请求的明星库简单易用。我们将用它来向东方财富网的服务器发送请求获取网页的HTML源码。相比于Python自带的urllibrequests的API更加人性化。pandas: 数据分析领域的“瑞士军刀”。我们爬取到的原始数据往往是列表或字典形式pandas的DataFrame结构可以极其方便地对这些数据进行清洗、处理和转换。最关键的是它提供了to_excel方法能一键将数据保存为Excel文件省去大量手动操作的麻烦。lxml: 一个高性能的HTML/XML解析库。虽然requests能拿到网页代码但那是一串长长的、混杂着标签和文本的字符串。我们需要用lxml或者另一个库BeautifulSoup它底层也常用lxml作解析器来解析这串代码从中精准地提取出我们需要的股票数据比如开盘价、收盘价。注意网络环境可能导致pip安装缓慢或失败。你可以使用国内镜像源来加速例如在命令后加上-i https://pypi.tuna.tsinghua.edu.cn/simple。2.2 开发工具与辅助技能除了Python库一个好的代码编辑器能提升不少效率比如VS Code或PyCharm。此外掌握一点前端基础知识会大有裨益主要是学会使用浏览器的“开发者工具”按F12键打开。为什么必须会用开发者工具因为爬虫的本质是模拟浏览器访问然后从返回的HTML中“挖”出数据。开发者工具中的“元素”Elements选项卡可以让你看到网页的完整DOM结构“网络”Network选项卡则能记录页面加载过程中所有的网络请求。我们的目标就是找到那个真正包含股票数据的请求。很多时候网页上看到的数据并非直接写在初始HTML里而是通过后续的Ajax请求动态加载的这些请求和返回的数据通常是JSON格式都在“网络”选项卡里一览无余。找到它就能省去解析复杂HTML的功夫直接获取结构化的数据。3. 目标网站分析与数据接口定位这是爬虫项目中最关键、也最需要耐心的一步。我们不能盲目地开始写代码必须先搞清楚数据在哪、以什么形式存在。3.1 东方财富网股票详情页结构我们以沪深300的龙头股之一贵州茅台股票代码600519为例。在浏览器中打开东方财富网关于贵州茅台的页面网址类似于https://quote.eastmoney.com/concept/sh600519.html。打开浏览器的开发者工具F12切换到“网络”Network选项卡然后刷新页面。你会看到一大堆请求有加载HTML文档的有加载CSS、JS文件的还有加载图片和字体资源的。我们的目标是找到携带核心K线数据的请求。通常这类数据是通过XHRAjax请求获取的。在“网络”选项卡中筛选XHR或Fetch请求然后仔细观察每个请求的“名称”Name和“响应”Response内容。经过一番查找你可能会发现一个名字包含“kline”或类似字样的请求。点击它查看其“响应”内容如果里面是整齐的JSON数据包含了data、klines等字段以及类似[2023-12-29, 1680.01, 1699.00, 1670.00, 1695.00, 2735502]这样的数组那么恭喜你找到了核心接口。这个数组通常对应着 [日期开盘价最高价最低价收盘价成交量]。然而东方财富网的数据接口可能会更新或调整。一个更稳定、更常用的接口是其用于绘制K线图的数据接口。我们可以尝试访问一个格式固定的URL来获取数据。例如对于日K线数据一个常见的接口模式是https://push2his.eastmoney.com/api/qt/stock/kline/get?secid[市场代码].[股票代码]klt101fqt1这里secid参数指定股票klt101通常表示日K线fqt1表示前复权。你需要根据股票所在市场替换secid比如沪市是1.600519深市是0.000858。实操心得直接分析网页找接口有时像侦探破案。一个技巧是在K线图附近右键“检查”看看有没有相关的canvas或div元素其数据属性可能指向某个API。另一个更高效的方法是直接在网上搜索“东方财富 股票数据 API”等关键词常能找到热心的技术爱好者总结的接口列表。但请注意这些非官方接口随时可能失效我们的代码需要有一定的容错能力。3.2 接口参数详解与构造假设我们确定了上述接口可用。让我们详细拆解一下这个请求的URL理解每个参数的意义这对于爬取其他周期的数据或处理不同股票至关重要。以获取贵州茅台SH600519的日K线为例构造的URL可能如下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,f61klt101fqt1beg0end20500101lmt500secid1.600519: 股票唯一标识。1代表上海证券交易所600519是股票代码。如果是深圳证券交易所的股票如平安银行000001则是secid0.000001。fields1fields2: 这两个参数定义了返回数据中包含哪些字段。不同的f代码代表不同的数据维度比如开盘、收盘、成交量、成交额等。上述示例是一组较全的字段组合。klt101: K线周期。101日K102周K103月K55分钟K线1515分钟K线等。fqt1: 复权类型。0不复权1前复权2后复权。做长期趋势分析通常使用前复权数据以保证价格曲线的连续性。beg0end20500101: 数据开始和结束日期。0通常表示最早可用数据20500101是一个未来的日期表示取到最新数据。lmt500: 限制返回的数据条数。这里设定为500条对于日K线大约是两年多的数据。通过灵活组合这些参数我们几乎可以获取任意股票、任意周期、任意时间段的K线数据。这就是分析接口的价值所在。4. 爬虫代码实现与核心逻辑解析环境准备好了接口也找到了现在让我们开始编写爬虫的核心代码。我会将整个过程分解为几个函数并详细解释每一行代码的意图。4.1 构建请求头与获取数据首先我们需要定义一个函数来发送HTTP请求并获取数据。直接使用requests.get()虽然简单但网站可能会屏蔽没有携带浏览器标识的请求。因此我们需要模拟一个真实的浏览器请求头。import requests import pandas as pd import json from datetime import datetime def fetch_stock_kline(secid, klt101, fqt1, lmt500): 获取股票K线数据 :param secid: 股票标识如 1.600519 (沪市) 或 0.000001 (深市) :param klt: K线类型101日K 102周K 103月K :param fqt: 复权类型0不复权1前复权2后复权 :param lmt: 数据条数限制 :return: 包含K线数据的列表失败则返回None # 构造请求URL url fhttps://push2his.eastmoney.com/api/qt/stock/kline/get params { secid: secid, fields1: f1,f2,f3,f4,f5,f6, fields2: f51,f52,f53,f54,f55,f56,f57,f58,f59,f60,f61, klt: klt, fqt: fqt, beg: 0, end: 20500101, lmt: lmt } # 模拟浏览器请求头这是绕过简单反爬的关键一步 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Referer: https://quote.eastmoney.com/ } try: response requests.get(url, paramsparams, headersheaders, timeout10) response.raise_for_status() # 如果状态码不是200抛出HTTPError异常 data_json response.json() # 将响应内容解析为JSON return data_json except requests.exceptions.RequestException as e: print(f网络请求失败: {e}) return None except json.JSONDecodeError as e: print(fJSON解析失败: {e}) return None代码解析与注意事项请求头HeadersUser-Agent是告诉服务器我们是什么类型的客户端这里是Chrome浏览器。Referer表示请求的来源页面有些网站会校验这个字段。添加这些信息能使我们的请求看起来更像普通用户浏览降低被屏蔽的风险。异常处理网络请求充满不确定性必须用try...except包裹。requests.get()可能会因为超时、连接错误等抛出异常。response.raise_for_status()会在HTTP状态码为4xx或5xx时抛出异常让我们能及时知道请求出了问题如404页面不存在403被禁止访问。超时设置timeout10表示如果服务器10秒内没有响应就放弃这次请求防止程序长时间卡住。4.2 解析JSON数据并转换为DataFrame获取到的JSON数据结构通常比较深我们需要从中提取出核心的K线列表并将其转换为pandas的DataFrame方便后续处理。def parse_kline_data(json_data): 解析从接口获取的JSON数据转换为DataFrame :param json_data: fetch_stock_kline函数返回的JSON数据 :return: 包含K线数据的DataFrame解析失败返回空DataFrame if json_data is None or json_data[data] is None: print(未获取到有效数据或数据为空。) return pd.DataFrame() try: # 根据实际接口返回结构调整这里是一个常见路径 klines json_data[data][klines] # klines 是一个列表每个元素是一个用逗号分隔的字符串如 2023-12-29,1680.01,1699.00,... data_list [] for item in klines: # 分割字符串转换为列表 values item.split(,) # 确保有足够的数据列这里假设至少有6列时间开盘收盘最高最低成交量 if len(values) 6: data_list.append(values[:6]) # 只取前6列可根据fields2调整 # 定义列名 columns [date, open, close, high, low, volume] df pd.DataFrame(data_list, columnscolumns) # 转换数据类型 df[date] pd.to_datetime(df[date]) for col in [open, close, high, low]: df[col] pd.to_numeric(df[col], errorscoerce) df[volume] pd.to_numeric(df[volume], errorscoerce) # 按日期排序 df.sort_values(date, inplaceTrue) df.reset_index(dropTrue, inplaceTrue) return df except KeyError as e: print(f解析JSON数据时键错误接口结构可能已变化: {e}) # 可以打印出json_data的部分内容来调试 # print(json.dumps(json_data, indent2, ensure_asciiFalse)[:1000]) return pd.DataFrame() except Exception as e: print(f解析数据时发生未知错误: {e}) return pd.DataFrame()关键点解析数据提取json_data[data][klines]这个路径是基于对接口响应结构的分析得出的。这是最容易出问题的地方如果东方财富网更新了接口这个路径可能会变。因此KeyError异常处理非常重要一旦捕获就应该打印出部分原始JSON来重新分析结构。数据清洗原始数据中的价格和成交量都是字符串格式必须转换为数值型float或int才能进行计算。pd.to_numeric的errorscoerce参数会将无法转换的值设为NaNNot a Number避免程序崩溃。日期处理将date列转换为datetime类型是至关重要的一步这为后续按时间筛选、重采样如将日数据聚合为周数据等操作奠定了基础。4.3 保存数据到Excel文件将清洗好的DataFrame保存为Excel文件非常简单pandas已经为我们提供了完美的支持。def save_to_excel(df, stock_code, save_path./stock_data): 将DataFrame保存为Excel文件 :param df: 包含股票数据的DataFrame :param stock_code: 股票代码用于命名文件 :param save_path: 保存目录默认为当前目录下的stock_data文件夹 :return: 保存的文件完整路径 import os # 如果保存目录不存在则创建 if not os.path.exists(save_path): os.makedirs(save_path) # 生成文件名包含股票代码和当前日期 filename f{stock_code}_{datetime.now().strftime(%Y%m%d)}.xlsx filepath os.path.join(save_path, filename) try: # 使用pandas的ExcelWriter可以更灵活地设置sheet名等属性 with pd.ExcelWriter(filepath, engineopenpyxl) as writer: df.to_excel(writer, sheet_nameK线数据, indexFalse) print(f数据已成功保存至: {filepath}) return filepath except Exception as e: print(f保存Excel文件失败: {e}) return None注意事项文件命名文件名中包含了股票代码和爬取日期datetime.now().strftime(%Y%m%d)这样可以避免多次爬取时覆盖旧文件也便于管理历史数据。引擎选择engineopenpyxl指定了写入Excel文件的引擎。openpyxl是处理.xlsx格式文件的流行库。确保已通过pip install openpyxl安装它。路径处理使用os.path.join来拼接路径比直接用字符串加号更安全它能自动处理不同操作系统Windows/macOS/Linux的路径分隔符差异。4.4 主函数与流程串联最后我们编写一个主函数main()将上述所有步骤串联起来形成一个完整的、可执行的脚本。def main(): 主函数控制整个爬取流程 # 1. 定义想要爬取的股票列表 # 格式: {股票代码: 市场.代码} stocks_to_fetch { 600519: 1.600519, # 贵州茅台沪市 000858: 0.000858, # 五粮液深市 300750: 0.300750, # 宁德时代创业板 } all_data {} # 用于存储所有股票的数据 for stock_name, secid in stocks_to_fetch.items(): print(f正在爬取股票 {stock_name} 的数据...) # 2. 获取数据 json_data fetch_stock_kline(secid, lmt1000) # 这里尝试获取1000条日K线 if json_data is None: print(f - 股票 {stock_name} 数据获取失败跳过。) continue # 3. 解析数据 df parse_kline_data(json_data) if df.empty: print(f - 股票 {stock_name} 数据解析失败或为空跳过。) continue print(f - 成功获取 {stock_name} 共 {len(df)} 条数据时间范围: {df[date].min()} 至 {df[date].max()}) # 4. 保存到Excel (每只股票单独保存一个文件) save_to_excel(df, stock_name) # 5. 也可以选择存入字典后续统一处理 all_data[stock_name] df # 6. (可选) 将所有股票数据合并到一个Excel的不同Sheet中 if all_data: print(\n开始合并所有数据到单个文件...) combined_path ./stock_data/combined_stocks.xlsx with pd.ExcelWriter(combined_path, engineopenpyxl) as writer: for stock_name, df in all_data.items(): # 避免sheet名过长Excel限制31字符 sheet_name stock_name[:31] df.to_excel(writer, sheet_namesheet_name, indexFalse) print(f所有股票数据已合并保存至: {combined_path}) print(\n爬取任务完成) if __name__ __main__: main()这个主函数展示了完整的流程定义任务列表 - 循环爬取 - 解析保存 - 汇总合并。你可以轻松地修改stocks_to_fetch字典来添加或删除想要爬取的股票。5. 反爬策略应对与稳健性优化公开网站的爬虫从来都不是一劳永逸的。服务器管理员会采取各种措施反爬虫来阻止自动化脚本过度访问。我们的代码必须具备一定的“韧性”。5.1 常见反爬机制与应对请求头校验我们已经通过添加User-Agent和Referer来模拟浏览器这是最基本也是最有效的一步。有时还需要添加Accept、Accept-Language、Connection等字段让请求头看起来更完整。你可以从浏览器开发者工具的“网络”选项卡中复制任意一个成功请求的完整Headers来使用。IP访问频率限制这是最常遇到的限制。如果短时间内从一个IP地址发起大量请求服务器可能会暂时或永久封禁该IP。应对策略在请求间增加随机延时。使用time.sleep()函数。import time import random # 在循环发起请求的地方添加 delay random.uniform(1, 3) # 随机等待1到3秒 time.sleep(delay)重要原则将延时设置得人性化一些比如2-5秒。这既是对目标网站服务器的尊重也能让你的爬虫更稳定地长期运行。切勿使用毫秒级的无间隔请求。Cookie/Session验证有些页面需要登录或验证后才能访问数据。东方财富网的公开K线数据接口通常不需要但如果遇到需要的情况可以使用requests.Session()对象来保持会话并在首次请求后保存服务器返回的Cookie后续请求自动带上。动态参数与加密高级的反爬会要求请求携带一个随时间或内容变化的加密参数如token,sign。这需要逆向分析网站的JavaScript代码找出加密算法并用Python实现难度较大。对于东方财富网的核心K线接口目前大多还是明文参数。5.2 代码健壮性增强除了应对反爬我们的代码本身也要更加健壮。重试机制网络请求可能因临时波动失败。我们可以实现一个简单的重试装饰器。import functools def retry(times3, delay2): 重试装饰器 :param times: 重试次数 :param delay: 每次重试间隔秒 def decorator(func): functools.wraps(func) def wrapper(*args, **kwargs): for i in range(times): try: return func(*args, **kwargs) except Exception as e: if i times - 1: # 最后一次重试也失败 raise e print(f函数 {func.__name__} 第{i1}次尝试失败{delay}秒后重试... 错误: {e}) time.sleep(delay) return None return wrapper return decorator # 使用装饰器 retry(times3, delay2) def fetch_stock_kline_retry(secid): # ... 原有的fetch_stock_kline函数代码 ... pass数据完整性校验保存数据前检查DataFrame是否为空关键列如日期、收盘价是否有大量缺失值NaN。def validate_data(df): if df.empty: return False, 数据为空 # 检查关键列是否存在 required_cols [date, close] for col in required_cols: if col not in df.columns: return False, f缺失必要列: {col} # 检查收盘价是否有过多缺失 if df[close].isna().sum() len(df) * 0.1: # 缺失超过10% return False, 收盘价数据缺失严重 return True, 数据有效日志记录使用Python的logging模块替代print可以将运行信息、错误记录到文件方便后期排查问题。import logging logging.basicConfig( levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(stock_spider.log), logging.StreamHandler() # 同时在控制台输出 ] ) # 在代码中用 logging.info(...), logging.error(...) 替换 print6. 数据后续处理与应用场景拓展爬取数据只是第一步让数据产生价值才是目的。保存到Excel的数据可以很方便地用pandas进行进一步分析或者导入到其他工具中。6.1 使用Pandas进行快速分析假设我们已经将贵州茅台的数据爬取下来并保存为DataFramedf_maotai。# 1. 查看数据概览 print(df_maotai.info()) print(df_maotai.describe()) # 2. 计算简单技术指标 # 例如计算5日、20日简单移动平均线SMA df_maotai[SMA_5] df_maotai[close].rolling(window5).mean() df_maotai[SMA_20] df_maotai[close].rolling(window20).mean() # 3. 计算每日涨跌幅 df_maotai[pct_change] df_maotai[close].pct_change() * 100 # 4. 筛选特定时间段的数据 start_date 2023-01-01 end_date 2023-12-31 mask (df_maotai[date] start_date) (df_maotai[date] end_date) df_2023 df_maotai.loc[mask] # 5. 统计年度表现 annual_return (df_2023[close].iloc[-1] / df_2023[close].iloc[0] - 1) * 100 print(f2023年度收益率: {annual_return:.2f}%)6.2 数据可视化结合matplotlib或plotly库可以轻松地将数据图表化。import matplotlib.pyplot as plt import matplotlib.dates as mdates # 设置中文字体如果需要显示中文标签 plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 fig, (ax1, ax2) plt.subplots(2, 1, figsize(14, 10), sharexTrue) # 子图1: 价格与均线 ax1.plot(df_maotai[date], df_maotai[close], label收盘价, linewidth1) ax1.plot(df_maotai[date], df_maotai[SMA_5], label5日均线, linestyle--, alpha0.8) ax1.plot(df_maotai[date], df_maotai[SMA_20], label20日均线, linestyle--, alpha0.8) ax1.set_ylabel(价格 (元)) ax1.set_title(贵州茅台股价走势与均线) ax1.legend() ax1.grid(True, linestyle--, alpha0.5) # 子图2: 成交量 ax2.bar(df_maotai[date], df_maotai[volume], label成交量, colorgray, alpha0.6) ax2.set_xlabel(日期) ax2.set_ylabel(成交量) ax2.legend() ax2.grid(True, linestyle--, alpha0.5) # 格式化x轴日期 ax2.xaxis.set_major_formatter(mdates.DateFormatter(%Y-%m)) ax2.xaxis.set_major_locator(mdates.MonthLocator(interval3)) plt.xticks(rotation45) plt.tight_layout() plt.show()6.3 应用场景举例个人投资复盘定期爬取自选股列表的数据计算关键指标如市盈率、市净率分位数制作简单的仪表盘辅助投资决策。量化策略研究获取大量股票的历史数据用于回测简单的交易策略如均线金叉死叉、动量策略。市场情绪分析结合爬取的股票论坛、新闻数据需注意合规性分析市场情绪与股价波动的相关性。学术研究为金融、经济相关的学术论文提供实证分析的数据基础。7. 常见问题与排查技巧实录在实际操作中你几乎一定会遇到各种问题。下面是我在多次爬取过程中总结的一些典型问题及其解决方法。7.1 问题速查表问题现象可能原因排查步骤与解决方案返回None或空数据1. 网络连接问题。2. 接口URL或参数错误。3. 请求被服务器拒绝反爬。1. 检查网络用浏览器手动访问接口URL测试。2. 打印出构造的完整URL与浏览器开发者工具中捕获的请求URL仔细比对。3. 检查请求头是否完整添加Referer、Accept等字段。在请求间增加time.sleep。解析JSON时KeyError接口返回的数据结构发生变化。1. 在except块中打印json_data查看最新的数据结构。2. 使用json.dumps(data, indent2)美化打印找到klines或类似数据数组的新位置。数据列错乱或为NaN1.fields参数与返回数据不匹配。2. 字符串分割时索引错误。1. 核对fields1和fields2参数或尝试使用更简单的字段组合。2. 打印一条原始klines字符串数清逗号分隔的字段数调整values[:6]中的索引。爬取速度慢1. 单线程顺序请求。2. 延时设置过长。1. 对于大量股票考虑使用concurrent.futures模块实现线程池但务必谨慎控制并发数建议不超过3-5个线程且每个线程内仍有延时。2. 在遵守善意爬取的前提下适当缩短随机延时范围如random.uniform(0.5, 2)。保存Excel失败1. 目录没有写入权限。2. 文件被其他程序打开。3. 未安装openpyxl引擎。1. 检查save_path目录是否存在或尝试换一个绝对路径。2. 关闭可能打开该Excel文件的程序。3. 运行pip install openpyxl。7.2 独家避坑技巧接口保鲜将接口URL和关键参数如fields1,fields2作为配置项写在代码开头或单独的配置文件中。一旦接口变化只需修改一处而不是满代码找。数据缓存对于需要反复测试或分析的数据可以在首次成功爬取后将原始的JSON响应或处理后的DataFrame用pickle或json模块保存到本地文件。下次直接读取本地文件避免重复请求网络既快又不会给服务器增加负担。import pickle # 保存 with open(f{stock_code}_raw_data.pkl, wb) as f: pickle.dump(json_data, f) # 读取 with open(f{stock_code}_raw_data.pkl, rb) as f: json_data pickle.load(f)增量爬取如果你需要每天更新数据每次都全量爬取几百条数据是低效的。可以设计一个逻辑先读取本地已保存的最新数据文件获取最新的日期然后只请求该日期之后的数据再与旧数据合并。这需要对接口支持按日期范围查询东方财富网的beg和end参数可以用于此目的。尊重robots.txt虽然这个项目针对的是公开数据接口但养成好习惯很重要。在爬取任何网站前可以访问https://www.example.com/robots.txt查看该网站对爬虫的限制规则。尽管API接口通常不在robots.txt规范内但遵守其中的爬取延迟Crawl-delay建议是良好的网络公民行为。这个项目从环境搭建到代码实现再到反爬应对和数据分析覆盖了一个数据爬取任务的完整生命周期。最关键的是理解每一步背后的“为什么”而不仅仅是复制代码。金融市场的数据接口和服务条款可能发生变化今天有效的方法明天可能就需要调整。因此培养自己分析网络请求、解析数据结构、编写稳健代码的能力远比记住某个特定的URL要重要得多。当你掌握了这套方法你就不仅能够爬取股票数据还能将这套技能迁移到其他需要获取公开数据的领域。