Yahoo Finance 是最广泛使用的公开市场数据来源之一。每个行情页面都显示着交易者全天关注的核心数字:某个股票代码的当前价格、相较于前一收盘价的绝对变动,以及变动百分比。对于任何维护自选股列表或记录每日收盘价的人来说,如果需要跟踪的代码超过两三个,手动复制这些数据很快就会让人疲惫不堪。
本文是更完整的抓取 Yahoo Finance教程的简短配套指南。在这里,你将构建一个小型、可直接运行的 Python 脚本,输入一个股票代码,返回其当前价格、涨跌额和涨跌幅,范围限定在任何人无需账号即可查看的公开市场数据。完整的多字段指南请阅读配套文章;本文专注于快速获取价格的脚本。
你将构建什么
一个单一的 Python 函数,调用时传入股票代码。它通过 Crawling API 获取渲染后的 Yahoo Finance 行情页面,解析三个字段,并以小型字典形式返回。运行示例使用苹果公司(AAPL):
- 价格(Price)该代码的当前报价。
- 涨跌额(Change)相较于前一收盘价的绝对价格变动。
- 涨跌幅(Change percent)该变动的百分比表示。
为何普通请求在 Yahoo Finance 上会失败
将一个裸 HTTP 客户端指向 Yahoo Finance 的行情 URL,通常会得到状态码 200,但页面中并不包含你想要的实时数字。有两个因素对你不利。首先,行情页面在浏览器中通过 JavaScript 渲染其价格模块,价格实时跳动,因此初始 HTML 只是一个框架,只有在页面脚本运行后才会填入数据。解析第一次响应时,价格字段会是空的。其次,金融网站会监控自动化流量:数据中心 IP 以及不像真实浏览器的请求模式,在到达渲染内容之前就会被限速或受到挑战。
因此,一个能用的价格获取器需要在一次请求中同时满足两点:一个真正渲染行情模块的浏览器,以及一个被网站识别为真实访客的 IP。你可以自己搭建无头浏览器加上轮换住宅代理,但维护这套系统才是主要工作量。Crawling API 将两者折叠进一次调用:发送带有 JavaScript token 的 URL,它在受信任的 IP 后面渲染页面,并返回供你解析的完整 HTML。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 会先在真实浏览器中渲染页面。Yahoo Finance 在客户端填充其价格模块,因此这里需要 JS token。普通 token 返回的结果与裸请求相同,实时数字缺失。
前提条件
在编写任何代码之前,需要准备好三件事。
基础 Python 知识。 你应该能够运行脚本并使用 pip 安装包。如果你对解析还不熟悉,BeautifulSoup 指南与本教程搭配很好。
Python 3.8 或更高版本。 用 python --version 确认你的版本。如果没有,从 python.org 安装,并确保 Python 在你的 PATH 中。
Crawlbase 账号和 JS token。 注册账号,打开你的仪表盘,从账号文档页面复制你的 JavaScript(JS)token。Crawlbase 免费提供 1,000 次请求作为起步,对于这个脚本绰绰有余。像对待密码一样保管 token,不要将其纳入版本控制。
设置项目
创建虚拟环境以隔离项目依赖,然后安装脚本需要的两个库。
python --version python -m venv yahoo_env source yahoo_env/bin/activate pip install crawlbase beautifulsoup4
在 Windows 上,用 yahoo_env\Scripts\activate 替换 source 那行来激活环境。两个依赖各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 负责解析返回的 HTML 以便按属性提取字段。json 模块是标准库自带的。
第一步:获取渲染后的行情页面
首先获取完整页面。导入 CrawlingAPI 类,用你的 JS token 初始化它,然后请求行情 URL。行情页面由股票代码构建,格式为 https://finance.yahoo.com/quote/<SYMBOL>。传入 ajax_wait 和 page_wait 以确保价格模块在页面被捕获前已加载完毕,并在解析前检查 Crawlbase 的 pc_status,让失败清晰可见。
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 4000, } def crawl(symbol): quote_url = f"https://finance.yahoo.com/quote/{symbol}" response = api.get(quote_url, OPTIONS) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_status']}") return None if __name__ == "__main__": html = crawl("AAPL") print(html[:500] if html else "No HTML returned")
等待选项对于客户端渲染的目标至关重要。ajax_wait 等待异步内容加载完成,page_wait 在加载后再等待固定毫秒数,以确保价格模块在捕获前已填充。四秒是一个合理的起点;如果价格仍然是空的,可以增大这个值。用 python yahoo_price.py 运行脚本,你应该能看到真实的 Yahoo Finance 标记,而不是裸请求返回的框架,这证明渲染可以正常工作,之后再写选择器也不迟。
行情页面需要在受信任 IP 后面渲染出价格模块,一次调用搞定,这正是上面 ajax_wait 和 page_wait 选项所配置的。Crawling API 接受 JS token,在真实浏览器中运行页面,服务端轮换住宅 IP,并将完整 HTML 交给你,省去了自己运营无头浏览器集群和代理池的麻烦。先在免费套餐上用公开行情页面测试。
第二步:解析价格、涨跌额和涨跌幅
Yahoo Finance 在价格模块上使用稳定的 data-field 属性标注其核心行情数字,这让它们成为可靠的定位目标:价格在 regularMarketPrice 上,绝对涨跌额在 regularMarketChange 上,涨跌幅在 regularMarketChangePercent 上。将渲染后的 HTML 加载到 BeautifulSoup 中逐一读取。每次查找都有保护措施,字段缺失时返回 None 而不是让脚本崩溃。
from bs4 import BeautifulSoup def field(soup, name): el = soup.select_one(f'[data-field="{name}"]') return el.get_text(strip=True) if el else None def parse_quote(html, symbol): soup = BeautifulSoup(html, "html.parser") return { "symbol": symbol, "price": field(soup, "regularMarketPrice"), "change": field(soup, "regularMarketChange"), "change_percent": field(soup, "regularMarketChangePercent"), }
field 辅助函数通过 data-field 属性查询单个元素并返回去除空白的文本,当元素不存在时返回 None,因此某个代码的页面如果缺少某个值,不会中断整次运行。parse_quote 将三个数字加上股票代码打包成字典。通过属性标签读取字段比依赖 Yahoo 生成的 CSS 类名更为可靠。
Yahoo Finance 的布局和类名会随时间变化,data-field 属性也可能移动。将这里的选择器视为起点模板,而非永久契约。如果某个字段返回 None,请在浏览器开发者工具中打开实时行情页面,找到包含该数字的元素,然后更新选择器。
第三步:组装完整脚本
现在将两部分组合成一个可运行的脚本。它获取渲染后的行情页面,解析三个字段,打印结果,并将记录写入 JSON 文件,方便下游传递。
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/122.0", "ajax_wait": "true", "page_wait": 4000, } def crawl(symbol): quote_url = f"https://finance.yahoo.com/quote/{symbol}" response = api.get(quote_url, OPTIONS) if response["headers"]["pc_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['pc_status']}") return None def field(soup, name): el = soup.select_one(f'[data-field="{name}"]') return el.get_text(strip=True) if el else None def parse_quote(html, symbol): soup = BeautifulSoup(html, "html.parser") return { "symbol": symbol, "price": field(soup, "regularMarketPrice"), "change": field(soup, "regularMarketChange"), "change_percent": field(soup, "regularMarketChangePercent"), } def get_stock_price(symbol): html = crawl(symbol) if not html: return None return parse_quote(html, symbol) def main(): quote = get_stock_price("AAPL") if not quote: print("Could not fetch quote") return print(json.dumps(quote, indent=2)) with open("quote.json", "w") as f: json.dump(quote, f, indent=2) if __name__ == "__main__": main()
get_stock_price 函数是你调用的入口:传入股票代码,返回字典;获取失败时返回 None。main 对 AAPL 运行该函数,打印结果并保存到 quote.json。
输出示例
运行脚本后,你将得到一条清晰的结构化记录,可用于自选股列表、笔记本或数据库。以下数字仅为示例;实际运行返回的是实时数据。
{ "symbol": "AAPL", "price": "212.44", "change": "+1.86", "change_percent": "(+0.88%)" }
接下来的步骤很小:遍历自选股列表、将记录写入 CSV,或将脚本定时运行以在每天收盘时记录每个代码的一行数据。完整的多字段教程(包括历史数据)请参见抓取 Yahoo Finance指南;对于其他 JavaScript 渲染密集型目标的渲染加可信 IP 方案,请参见用 Python 抓取 JavaScript 页面。
保持不被封锁
即使解决了渲染问题,金融网站仍会监控爬虫特征的流量。以下三个习惯能让较长的自选股运行保持健康:在代码之间添加短暂的休眠来控制请求频率,以免紧密循环导致被限速;依靠轮换,住宅 IP 池将请求分散到许多地址,让任何单一地址都不会触发速率限制(Crawling API 为你处理这些);并读取状态码,将开始返回非 200 的 pc_status 值视为需要退让的信号。更全面的策略请参见如何在不被封锁的情况下抓取网站。
抓取 Yahoo Finance 合法吗?
抓取 Yahoo Finance 是否被允许取决于 Yahoo 的服务条款、你的司法管辖区以及你对数据的用途。Yahoo 的条款限制自动访问和批量采集,因此无论你的工具多么谨慎,爬虫都可能与这些条款相悖。请阅读 Yahoo 服务条款和网站的 robots.txt,并将两者作为你采集范围的边界。坚持使用公开市场数据:报价、价格、当日涨跌额和涨跌幅都是任何人无需账号即可看到的事实数据,这也是本脚本的范围所在。不要抓取登录或付费内容背后的任何内容,不要采集个人账号数据,不要转载受版权保护的编辑内容。如果项目涉及个人数据,GDPR 和 CCPA 等隐私法规适用;像本文这样的简单价格获取不属于此类。
如需用于生产,请优先使用官方市场数据源而非抓取消费者网站。Yahoo Finance 显示的大部分数据是从拥有自己再分发条款的市场数据提供商和交易所处获得授权的,因此从页面上抓取一个数字并不赋予你再分发它的权利。如果你正在构建产品或需要大规模获取报价,请获取官方市场数据 API 授权:这是商业或批量使用的正确路径,并为你提供真正有权再分发的数据。
核心要点
- 行情页面是客户端渲染的。 普通请求返回的是缺少实时价格的框架,因此必须先渲染页面再解析。
-
你需要同时具备渲染能力和受信任的 IP。 带 JS token 的 Crawling API 一次调用两者兼顾;
ajax_wait和page_wait控制等待价格模块的时长。 -
定位稳定的属性。 通过
data-field属性读取regularMarketPrice、regularMarketChange和regularMarketChangePercent,而非 Yahoo 生成的类名。 -
保持精简且可循环。 一个
get_stock_price(symbol)函数返回字典;遍历自选股列表并控制请求频率即可跟踪多个代码。 - 坚持使用公开市场数据。 遵守 Yahoo 的服务条款和 robots.txt,避免登录和个人数据,如需生产或再分发用途请使用官方市场数据 API。
常见问题
为什么普通请求返回的页面没有实时价格?
因为 Yahoo Finance 在客户端通过 JavaScript 渲染其行情模块,价格实时跳动。初始 HTML 是一个框架,只有在页面脚本运行后才会填入数据,所以裸 HTTP 请求返回的状态码 200,但价格字段为空。要获取数字,必须先渲染页面,这正是 Crawling API 的 JS token 为你处理的。
抓取 Yahoo Finance 需要普通 token 还是 JS token?
需要 JS token。普通 token 获取静态 HTML,对于行情页面来说和裸请求返回的框架相同。JS token 在返回 HTML 之前先在真实浏览器中渲染页面,这样当 BeautifulSoup 解析时,价格、涨跌额和涨跌幅都已存在。
如何获取多个股票代码的价格?
将你的代码放入列表,对每个代码调用 get_stock_price,收集返回的字典。在请求之间添加短暂的 time.sleep 以控制运行节奏,并将每条记录追加到一个列表中,最后写入 JSON 或 CSV。
我的价格字段返回 None,发生了什么变化?
通常是以下两种情况之一。要么页面在被捕获时尚未渲染完成,此时可将 page_wait 增加一两秒;要么 Yahoo 更改了标记,data-field 属性不再匹配。在浏览器开发者工具中打开实时行情页面,找到包含价格的元素,更新选择器。定期维护选择器对任何爬虫来说都是正常工作。
这个脚本能获取历史价格吗?
本脚本的范围仅限于当前行情:价格、涨跌额和涨跌幅。历史数据位于 Yahoo Finance 的不同视图中,具有自己的布局,因此需要不同的选择器。更全面的抓取 Yahoo Finance指南涵盖了更多行情面板内容,是获取其他字段的正确起点。
我可以将抓取的 Yahoo Finance 数据用于商业用途吗?
这是一个法律问题,而非技术问题。Yahoo Finance 的大部分数据获得了市场数据提供商和交易所的授权,这些机构有自己的再分发条款,而 Yahoo 自身的服务条款也限制自动再利用,因此商业或大规模使用通常需要获得许可。如需构建产品或大规模使用,请获取官方市场数据 API 授权,并在基于这些数据构建之前寻求法律建议。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
