Google Finance 是读取实时市场数据使用最广泛的平台之一,其行情页面包含驱动价格追踪、投资组合监控和研究所需的精确结构化数据:实时价格、当日涨跌金额和百分比、前收盘价,以及市值、市盈率、日内和年内交易区间等核心市场指标。对于监控一篮子股票的人来说,这些公开的市场数据就是原始素材,而手动跨数十只股票进行复制既费时又容易出错。
本指南将向你展示如何以可靠的方式使用 Python 抓取 Google Finance。你将构建一个小而可运行的爬虫,通过 Crawling API 获取已渲染的行情页面,用 BeautifulSoup 解析你需要的字段,循环抓取多只股票,并导出整洁的 JSON。整个演示始终聚焦于公开市场数据(属于事实性数据,而非个人信息),文末的合法性章节并非套话,在将本工具指向任何真实流量之前请务必阅读。
你将构建什么
一个 Python 脚本,接收一个 Google Finance 行情 URL 列表(每只股票一个,使用 Google 的 SYMBOL:EXCHANGE 格式),通过 Crawling API 获取每个已渲染页面,并提取每只股票的结构化记录。以下以 NASDAQ 上的 GOOGL、AAPL 和 MSFT 为运行示例,抓取这些字段:
- 标题 行情页顶部显示的公司名称。
- 价格 该股票的当前报价。
- 涨跌幅 本交易日的绝对涨跌金额及百分比。
- 前收盘价 上一交易日的收盘价。
- 市场数据 关键指标块:市值、市盈率以及日内和年内区间(如有)。
为什么普通请求在 Google Finance 上会失败
用普通 HTTP 客户端请求 Google Finance 行情 URL,你会得到状态 200 的响应,但响应体中只有一小部分数据。有两个因素对你不利。第一,Google Finance 通过 JavaScript 在浏览器中填充其行情页面,因此初始 HTML 是一个薄壳。最明显的证据是价格涨跌百分比:从第一次响应中提取它,你会得到 None,因为该值是在页面加载后通过客户端渲染的。第二,Google 能迅速识别自动化流量。数据中心 IP 和不像真实浏览器的请求模式,在到达已渲染内容之前就会被限速、封禁 IP 或发起挑战。
因此,一个可用的 Google Finance 爬虫需要在一次请求中同时满足两点:一个实际渲染页面的浏览器,以及一个平台视为真实访客的 IP。你可以自行用无头浏览器加轮换住宅代理来搭建,但维护这套系统是大部分的工作量所在。Crawling API 将两者融合进一次调用:发送带 JavaScript token 的 URL,它在可信 IP 后方渲染页面,返回已完成的 HTML 供解析。关于已渲染目标的更多背景,请参阅如何爬取 JavaScript 网站。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript (JS) token 先在真实浏览器中渲染页面。Google Finance 的价格、涨跌百分比和指标均在客户端填充,因此此处需要 JS token。普通 token 返回的是与普通请求相同的薄壳,涨跌百分比缺失,几乎没有可解析的有用内容。
前提条件
在编写任何代码之前,你需要准备好几样东西,都不会花太长时间。
基础 Python 知识。 你应当能够编写并运行 Python 脚本,以及使用 pip 安装包。如果你对解析部分不熟悉,BeautifulSoup 指南是本教程的好伴侣。
Python 3.8 或更高版本。 使用 python --version 确认你的版本。如果没有,请从 python.org 安装或通过 Anaconda 等发行版安装,并确保 Python 在你的 PATH 中。
Crawlbase 账户和 JS token。 注册账户,打开控制台,从账户文档页面复制你的 JavaScript (JS) token。Crawlbase 提供最多 20,000 次免费请求,足够完成本指南的学习,且仅对成功请求计费。请将 token 视为密码:它用于验证你的请求,因此不要将其提交到版本控制中。
设置项目
创建虚拟环境以隔离项目依赖,然后安装爬虫所需的库。
python --version python -m venv google_finance_env source google_finance_env/bin/activate pip install crawlbase beautifulsoup4
在 Windows 上,请用 google_finance_env\Scripts\activate 代替 source 那行来激活环境。两个依赖项各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML 以便按 CSS 选择器提取各字段。json 模块随标准库附带,导出步骤无需额外安装。
第一步:获取已渲染的 Google Finance 页面
首先获取一个完整页面。导入 CrawlingAPI 类,用你的 JS token 初始化它,然后请求一个行情 URL。Google Finance 是异步加载数据的,因此传入 ajax_wait 和 page_wait,让 API 在捕获页面前等待动态内容加载完毕。在解析前检查 Crawlbase 的 cb_status(legacy pc_status),可以让失败情况显而易见而非悄无声息。
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "ajax_wait": "true", "page_wait": 5000, } def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None if __name__ == "__main__": quote_url = "https://www.google.com/finance/quote/GOOGL:NASDAQ" html = crawl(quote_url) print(html[:500] if html else "No HTML returned")
两个等待选项对于 Google Finance 这样的客户端渲染目标至关重要。ajax_wait 告知 API 等待异步内容加载完毕,page_wait 在页面加载后固定等待指定毫秒数,以确保延迟渲染的数值在捕获前出现。五秒是一个合理的起点;如果涨跌百分比返回为空,可适当增大。用 python google_finance_scraper.py 运行脚本,你应当看到真实的行情页标记,而非普通请求返回的薄壳。这可以在编写任何选择器之前确认渲染是否正常工作。
Google Finance 需要在一次调用中同时完成页面渲染和可信 IP 访问,而这正是上面 ajax_wait 和 page_wait 选项所实现的。Crawling API 接收 JS token,在真实浏览器中运行页面(确保涨跌百分比实际加载),在服务端轮换住宅 IP,并将已完成的 HTML 交给你,省去了自行运营无头浏览器集群和代理池的麻烦。先用免费额度对公开行情页面进行测试。
第二步:解析价格、涨跌幅和标题
有了已完成的 HTML,将其加载到 BeautifulSoup 中并提取核心字段。Google Finance 将公司名称、当前价格和当日涨跌幅保存在页面的 main 区域,每个字段都有一个生成的 class。以下选择器直接来自实时行情布局:标题位于 zzDege,价格位于 AHmHk,涨跌百分比位于 JwB6zf。每次查找都有防护,确保缺失字段时返回 None 而不是让运行崩溃。
from bs4 import BeautifulSoup def get_price(soup): el = soup.find("main") if not el: return None price = el.find("div", "AHmHk") return price.get_text(strip=True) if price else None def get_change_percentage(soup): main = soup.find("main") if not main: return None change = main.find("div", "JwB6zf") return change.get_text(strip=True) if change else None def get_stock_title(soup): main = soup.find("main") if not main: return None title = main.find("div", "zzDege") return title.get_text(strip=True) if title else None
JwB6zf 元素是需要重点关注的。在非渲染的薄壳响应中它完全缺失,get_change_percentage 返回 None;而通过 JS token 渲染页面后,它携带当日的涨跌金额和百分比。这个单一字段是检验渲染是否正常工作的最简单证据。
Google 生成的 class 名称,如 AHmHk、JwB6zf 和 zzDege,会在没有通知的情况下发生变化。将此处的选择器视为起始模板,而非合同。当一个字段在明显已渲染的页面上返回 None 时,在浏览器开发者工具中重新检查实时行情并更新 class。定期维护选择器对于任何生产爬虫来说都是正常的,并不意味着出了什么问题。
第三步:解析市场数据块
在核心数据下方,Google Finance 以标签和数值行的形式列出关键指标:前收盘价、日内区间、年内区间、市值、市盈率等。每一行是一个包含 gyFHrc 的容器,其中标签位于 mfs7Fc,数值位于 P6K39c。将每一行转入字典,可以让解析器保持弹性:无论某只股票披露哪些指标,都会作为键值出现,你可以直接从该字典读取 Previous close、Market cap、P/E ratio、Day range 和 Year range。
def get_market_data(soup): rows = soup.find_all("div", {"class": "gyFHrc"}) data = {} for row in rows: label = row.find("div", {"class": "mfs7Fc"}) value = row.find("div", {"class": "P6K39c"}) if label and value: data[label.get_text(strip=True)] = value.get_text(strip=True) return data KEEP = ["Previous close", "Day range", "Year range", "Market cap", "P/E ratio"] def select_market_fields(market_data): return {key: market_data[key] for key in KEEP if key in market_data}
get_market_data 辅助函数捕获页面上的所有标签和数值对,无需为每个指标硬编码一个选择器。select_market_fields 将其缩小到本指南的范围:前收盘价、日内和年内区间、市值以及市盈率。由于过滤器检查 if key in market_data,缺少某个指标的股票会直接从记录中省略该键,而不会导致失败。如果后续还需要平均成交量或主要交易所信息,保留完整的 get_market_data 输出即可。
第四步:组装完整脚本
现在将各部分串接成一个可运行的脚本:获取每个行情页面,解析核心字段和市场数据块,并将记录导出为 JSON。extract_quote 函数将各字段辅助函数整合成一条记录,main 函数遍历 SYMBOL:EXCHANGE URL 列表,让你可以在一次运行中获取多只股票的数据。
import json import time from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) OPTIONS = { "ajax_wait": "true", "page_wait": 5000, } KEEP = ["Previous close", "Day range", "Year range", "Market cap", "P/E ratio"] def crawl(page_url): response = api.get(page_url, OPTIONS) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def text_in_main(soup, class_name): main = soup.find("main") if not main: return None el = main.find("div", class_name) return el.get_text(strip=True) if el else None def get_market_data(soup): rows = soup.find_all("div", {"class": "gyFHrc"}) data = {} for row in rows: label = row.find("div", {"class": "mfs7Fc"}) value = row.find("div", {"class": "P6K39c"}) if label and value: data[label.get_text(strip=True)] = value.get_text(strip=True) return data def extract_quote(html, url): soup = BeautifulSoup(html, "html.parser") market = get_market_data(soup) return { "url": url, "title": text_in_main(soup, "zzDege"), "price": text_in_main(soup, "AHmHk"), "change": text_in_main(soup, "JwB6zf"), "previous_close": market.get("Previous close"), "market_data": {key: market[key] for key in KEEP if key in market}, } def main(): urls = [ "https://www.google.com/finance/quote/GOOGL:NASDAQ", "https://www.google.com/finance/quote/AAPL:NASDAQ", "https://www.google.com/finance/quote/MSFT:NASDAQ", ] records = [] for url in urls: html = crawl(url) if html: records.append(extract_quote(html, url)) time.sleep(2) with open("finance_data.json", "w") as f: json.dump(records, f, indent=2) print(f"Saved {len(records)} quotes") if __name__ == "__main__": main()
单个 text_in_main 辅助函数取代了之前三个几乎相同的标题、价格和涨跌幅函数:它先找到 main 区域,查找一个生成的 class,并返回去除空白后的文本或 None。extract_quote 为每只股票组装一条记录,从市场数据字典中提取前收盘价,并将其余指标嵌套在 market_data 下。两秒的延时控制运行节奏,避免过于频繁地访问站点。编辑 urls 列表可抓取任何其他股票代码。
输出结果示例
用 python google_finance_scraper.py 运行完整脚本,你将获得每只股票的整洁结构化记录,可直接用于分析、入库或制作电子表格。以下数值仅为示例;你的运行结果会显示实时行情。
[ { "url": "https://www.google.com/finance/quote/GOOGL:NASDAQ", "title": "Alphabet Inc Class A", "price": "$163.79", "change": "+1.01 (0.62%)", "previous_close": "$162.78", "market_data": { "Previous close": "$162.78", "Day range": "$163.09 - $167.12", "Year range": "$103.71 - $174.71", "Market cap": "2.04T USD", "P/E ratio": "25.54" } }, { "url": "https://www.google.com/finance/quote/AAPL:NASDAQ", "title": "Apple Inc", "price": "$169.30", "change": "-0.61 (0.36%)", "previous_close": "$170.33", "market_data": { "Previous close": "$170.33", "Day range": "$169.11 - $172.71", "Year range": "$164.08 - $199.62", "Market cap": "2.61T USD", "P/E ratio": "26.34" } } ]
注意 change 字段现在携带了真实的值,而非 null。在非渲染的普通请求中,它返回为空,因为 Google 是通过客户端渲染来填充它的;通过 JS token 路由请求才能让它出现。从这里,记录可以直接导入 pandas 或任何电子表格,用于追踪价格走势、比较观察列表中各股的市盈率,或输入价格情报工作流。
扩展到观察列表并保持不被封锁
上面的脚本只抓取三只股票,但通过扩展 urls 列表,结构可以扩展到完整的观察列表。随着运行规模增大,以下几个习惯有助于保持其健康运行,这些习惯适用于任何高强度目标。
- 控制请求节奏。 在紧密循环中大量请求行情是被限速或发起挑战的最快方式。上面的两秒延时是下限,而非上限。对于较大的观察列表适当加宽,并分散股票代码而非以全速重复请求同一路径。
- 依赖轮换。 住宅 IP 池将请求分散到众多真实用户地址,使单个地址不会触发速率限制。Crawling API 会替你处理这个问题;如果你自行搭建,这是需要着重处理的部分。
-
关注状态码。 运行开始返回非 200 的
cb_status值,说明当前的速率或 IP 级别已经不够用了。将此视为退让的信号,而不是可以忽略的噪音。
对于较大规模的任务,异步 Crawler 可以将请求排入队列并通过 webhook 返回结果,适合在不保持长连接的情况下按计划刷新多只股票的行情。关于更广泛的操作指南,请参阅如何不被封锁地抓取网站。同样的渲染加可信 IP 方法同样适用于其他市场数据来源,例如从 CoinMarketCap 抓取加密货币价格。
抓取 Google Finance 是否合法?
抓取 Google Finance 是否被允许,取决于 Google 的服务条款、你所在的司法管辖区以及你对数据的使用方式。Google 的条款限制了对其服务的自动访问,因此无论你的工具多么谨慎,抓取行为都可能与这些条款相抵触。此处的代码并不改变这一点,只是让技术层面运作起来。请阅读 Google 的服务条款以及 Google Finance 路径的 robots.txt,并将两者视为你所收集内容和请求频率的边界。
本指南所针对的数据明确处于公开非个人的范畴:股票价格、当日涨跌幅、前收盘价、市值、市盈率以及日内和年内区间都是客观的市场数字,而非任何人的个人信息。这使工作范围比抓取包含用户内容的网站更为狭窄,但并不意味着你凌驾于 Google 的条款之上。请只访问任何访客无需账户即可加载的行情页面,将请求量控制在不给 Google 服务器造成负担的范围内,不要大量转发底层市场数据源,因为交易所和数据供应商对这些数据进行了授权,而授权会随数据一起转移。
本指南故意将范围限定在公开行情页面,因为这是保持工作可辩护的边界。它不涵盖任何需要登录的内容、付费或高级数据,或任何绕过身份验证或速率限制的尝试。如果你的项目需要有保障的正常运行时间、再分发权利或超出礼貌抓取所允许的更大流量,正确的路径是获取授权数据源:使用官方的市场数据 API 或授权的交易所数据源。这才是商业或批量使用的正确途径,而不是更激进的爬虫。
核心要点
- Google Finance 是客户端渲染的。 普通请求返回的薄壳缺少涨跌百分比,因此必须先渲染页面再解析。
-
你需要渲染和可信 IP 的结合。 带 JS token 的 Crawling API 可在一次调用中完成两者;
ajax_wait和page_wait控制等待数值加载的时长。 -
将字段映射到真实的 class 名称。 标题位于
zzDege,价格位于AHmHk,当日涨跌幅位于JwB6zf;指标块是一组gyFHrc行,将mfs7Fc标签与P6K39c数值配对。 -
循环并导出。 传入
SYMBOL:EXCHANGE行情 URL 列表,用短暂延时控制运行节奏,并将每只股票的记录写入 JSON,市场数据嵌套其中。 - 仅针对公开市场数据。 遵守 Google 的服务条款和 robots.txt,坚持客观公开的行情数据,生产或再分发场景请使用官方市场数据 API 或授权数据源。
常见问题
为什么普通请求中涨跌百分比为 null?
因为 Google Finance 是通过 JavaScript 在客户端渲染该值的。初始 HTML 是一个薄壳,持有当日涨跌幅的 JwB6zf 元素只有在浏览器中运行页面脚本后才会出现。原始 HTTP 请求返回的是状态 200 加上缺失涨跌幅的响应,这就是为什么该字段返回 None。通过 Crawling API 的 JS token 先渲染页面,可以让数值呈现,BeautifulSoup 再读取它。
抓取 Google Finance 需要普通 token 还是 JS token?
JS token。普通 token 获取的是静态 HTML,对于 Google Finance 来说,这与普通请求返回的薄壳相同,涨跌百分比同样缺失。JS token 先在真实浏览器中渲染页面,然后再返回 HTML,确保价格、涨跌幅和指标块在解析时都已完整呈现。
我可以从 Google Finance 行情页抓取哪些数据?
公开的市场字段:公司标题、当前价格、当日涨跌金额和百分比、前收盘价,以及指标块(包括市值、市盈率,以及日内和年内区间,如有)。这些都是任何访客无需账户即可加载的客观公开行情数据,而非个人信息。请坚守这一范围,如需再分发权利,请使用授权数据源。
我的选择器返回 None。发生了什么?
几乎可以肯定是 Google 的标记发生了变化。其生成的 class 名称(价格为 AHmHk,涨跌幅为 JwB6zf,标题为 zzDege,指标行为 gyFHrc、mfs7Fc、P6K39c)会在没有通知的情况下变化,所以上个月有效的选择器可能已经失效。在浏览器开发者工具中重新检查实时行情并更新 class 字符串。定期维护选择器对于任何生产爬虫来说都是正常的。
如何同时抓取多只股票?
Google Finance 以 SYMBOL:EXCHANGE 格式在行情 URL 中标识每只股票,例如 GOOGL:NASDAQ。构建这些 URL 的列表并循环遍历,在每个已渲染页面上调用相同的 extract_quote 函数,如上面的 main 函数所示。在请求之间保持短暂延时,并将列表扩展到覆盖你的完整观察列表。
生产使用有官方的 Google Finance API 吗?
Google 并没有提供公开的、受支持的 Google Finance 市场数据 API,因此对于生产或商业使用,可靠的路径是授权的市场数据提供商或官方交易所数据源,而非抓取网页。这些数据源附带正常运行时间保证和再分发条款,这是抓取公开页面所无法提供的,当数据准确性或使用权利成为需求的一部分时,这一点尤为重要。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
