Google 搜索结果是 SEO 的记分板。某个页面在关键词上的排名、哪些标题和摘要赢得点击、"People also ask"框中显示的问题,以及 Google 建议的相关搜索, 每一个都是您可以直接从结果页面读取的公开信号。跨目标关键词收集这些数据,可以告诉您与谁竞争、当前所处位置,以及哪些话题尚未覆盖。
本指南向您展示如何用 Python 从 Google 提取并分析 SEO 数据。您将构建一个小型可运行工作流,通过 Crawling API 获取渲染后的 SERP,解析对 SEO 有价值的字段,对关键词列表运行相同查询,并将结果加载到 pandas 中,以发现排名位置、竞争对手重叠和内容缺口。整个流程仅限于公开搜索结果数据,文末的合法性部分并非套话,请在大规模爬取前务必阅读。
您将构建的内容
一个 Python 工作流,接受目标关键词列表,通过 Crawling API 获取每个 SERP 的 HTML,并为每个自然搜索结果和页面 SEO 特性提取结构化记录。我们从每次搜索中提取以下字段:
- 排名位置:每个结果的自然排名,从页面顶部开始计数。
- 标题:每个结果显示的 meta 标题,影响点击率。
- 链接:目标 URL,我们还从中提取域名用于竞争对手分析。
- 摘要:Google 在每个结果下方显示的 meta 描述。
- People also ask:Google 为该查询附加的 PAA 问题,可用于内容创意。
- 相关搜索:页面底部的相关查询建议,是长尾关键词的来源。
这些字段涵盖了 SERP 上值得追踪的 SEO 数据点:自然排名、PAA 等 SERP 特性、meta 标题和描述,以及超越您排名的页面的 URL 和域名。搜索量和 CPC 不在页面上,因此仍需通过 Google Keyword Planner、Ahrefs 或 SEMrush 等关键词工具获取;本工作流为您提供结果页面上的全部信息。
为什么普通请求在 Google 上会失败
如果您从脚本向 Google 搜索 URL 发送裸 HTTP 请求,很少能得到您在浏览器中看到的干净 SERP。Google 实施了强大的反爬措施。看起来不像真实浏览器的请求会被 CAPTCHA 挑战、重定向到"sorry"中间页,或在几次调用后被速率限制。数据中心 IP 发出快速、重复的请求是最明显的特征,Google 会迅速屏蔽它。
此外,现代 SERP 的许多内容是用 JavaScript 拼装的。PAA 框等特性在初始 HTML 加载后才填充,因此即使请求成功,也可能缺失您需要的数据。因此,一个可用的 Google SEO 爬虫需要在单次请求中实现两件事:平台视为真实访客的 IP,以及渲染页面的浏览器。您可以自己构建包含无头浏览器和轮换住宅代理的技术栈,但维护该技术栈才是大部分工作。Crawling API 将两者整合进一次调用:您发送 URL,它从可信 IP 获取并在需要时渲染,然后返回完整 HTML 供您解析。
Google 的防护机制高度依赖请求模式和 IP 信誉。单个数据中心地址翻页浏览关键词很快就会触发限制。Crawling API 在服务器端将请求轮换到众多真实用户地址,并解决 Google 抛出的 CAPTCHA,让您无需自行获取和维护该代理池。您可以从 1,000 次免费请求开始,无需信用卡。
前提条件
在编写代码之前,您需要准备几样东西,都不会花太长时间。
基础 Python 知识。您应当能够编写和运行 Python 脚本,并使用 pip 安装包。如果 BeautifulSoup 对您来说是新工具,我们的 BeautifulSoup in Python 使用指南涵盖本教程所需的解析基础;我们的 pandas 数据分析指南则涵盖 DataFrame 部分。
Python 3.8 或更高版本。使用 python --version 确认版本。如果尚未安装,请从 python.org 或通过 Anaconda 等发行版安装。
Crawlbase 账户和 token。注册后,打开控制台,从账户文档页面复制请求 token。前 1,000 次请求免费,且仅对成功请求收费。请像对待密码一样保管 token,勿将其纳入版本控制。
搭建项目
创建虚拟环境以隔离项目依赖,然后安装工作流所需的三个库。
python --version python -m venv seo_env source seo_env/bin/activate pip install requests beautifulsoup4 pandas
在 Windows 上,请使用 seo_env\Scripts\activate 代替 source 命令。三个依赖项各司其职:requests 向 Crawling API 发送 HTTP 调用,beautifulsoup4 解析返回的 HTML 以便按 CSS 选择器提取各字段,pandas 将爬取的数据保存到 DataFrame 中用于最后的分析。
步骤一:通过 Crawling API 获取 SERP
首先获取 HTML。编写一个小型 crawl() 函数,将目标 Google 搜索 URL 连同您的 token 发送到 Crawling API,检查底层页面是否以状态码 200 返回,并返回 HTML 正文。传入 "javascript": "true" 使 API 在返回前用真实浏览器渲染页面,这正是填充 PAA 框等脚本加载特性所必需的。在解析之前检查状态,可以让失败清晰呈现而非被静默忽略。
import json import requests from urllib.parse import quote_plus API_TOKEN = "YOUR_CRAWLBASE_TOKEN" # replace with your token API_ENDPOINT = "https://api.crawlbase.com/" def search_url(keyword): return f"https://www.google.com/search?q={quote_plus(keyword)}&hl=en&gl=us" def crawl(url): params = {"token": API_TOKEN, "url": url, "javascript": "true"} response = requests.get(API_ENDPOINT, params=params) response.raise_for_status() data = json.loads(response.text) if data["original_status"] != 200: raise Exception(f"Unable to crawl '{url}'") return data["body"] if __name__ == "__main__": html = crawl(search_url("web scraping api")) print(html[:500])
API 返回一个 JSON 信封,因此您用 json.loads 加载响应,并读取两个字段:original_status 是 Google 本身返回的状态码,body 是渲染后的页面 HTML。对 original_status 进行守护意味着 CAPTCHA 页面或屏蔽将作为异常浮现,而非将垃圾数据送入解析器。search_url() 辅助函数对关键词进行 URL 编码,并使用 hl=en 和 gl=us 固定语言和地区,使结果在每次运行中保持一致。运行 python crawling.py,您应该在前 500 个字符中看到真实的 SERP 标记,这表示在编写任何选择器之前获取已正常工作。
original_status 检查能读到 200,正是因为请求作为真实访客到达 Google,CAPTCHA 已被处理。Crawling API 从轮换 IP 获取每个 SERP,渲染填充 PAA 框和相关搜索的 JavaScript,并为您返回完整 HTML,让您无需运行无头浏览器集群和自行获取住宅代理池。先在免费套餐上指向公开搜索 URL 进行测试。
步骤二:用 BeautifulSoup 解析 SEO 字段
拿到 HTML 后,将其加载到 BeautifulSoup 中,并通过选择器提取每个结果。Google 将每个自然结果包裹在一个容器中,标题在 h3 标签中,目标地址在其周围的锚点中,摘要在旁边的描述块中。PAA 问题和相关搜索位于页面下方各自的块中。在浏览器的开发者工具中检查实时 SERP(右键单击,然后选择"检查")以确认当前类名;以下选择器与撰写本文时的布局相匹配。
from urllib.parse import urlparse from bs4 import BeautifulSoup def scrape_serp(html, keyword): soup = BeautifulSoup(html, "html.parser") results = [] for position, block in enumerate(soup.select("div.g"), start=1): heading = block.select_one("h3") link = block.select_one("a[href]") snippet = block.select_one("div.VwiC3b") if not heading or not link: continue url = link["href"] results.append({ "keyword": keyword, "position": position, "title": heading.get_text(strip=True), "url": url, "domain": urlparse(url).netloc, "snippet": snippet.get_text(strip=True) if snippet else None, }) paa = [ q.get_text(strip=True) for q in soup.select("div.related-question-pair") if q.get_text(strip=True) ] related = [ r.get_text(strip=True) for r in soup.select("div.y6Uyqe div.b2Rnsc, a.k8XOCe") if r.get_text(strip=True) ] return { "keyword": keyword, "results": results, "people_also_ask": paa, "related_searches": related, }
选择器 div.g 是 Google 用于每个自然结果的包装器,标题在 h3 标签中,目标地址在其周围的锚点中,摘要在 div.VwiC3b 中。enumerate(..., start=1) 免费给出排名,因此排名来自页面顺序而非脆弱的属性。urlparse(url).netloc 从每个链接派生域名,这是后续竞争对手分析的关键字段。if not heading or not link: continue 守护将广告和杂乱的标记排除在输出之外。PAA 问题来自 div.related-question-pair,相关搜索来自页面底部的建议块。
Google 在重新部署前端时会轮换其结果容器类名(如 VwiC3b 和 related-question-pair),有时还会同时 A/B 测试多种布局。请将上述选择器视为起始模板,而非固定合约。当某个字段对所有结果都返回为空时,请在浏览器开发者工具中重新检查实时 SERP 并更新选择器。定期维护选择器是任何生产爬虫的正常工作,不是出了什么问题的标志。
步骤三:对您的关键词列表运行
现在将获取和解析整合到一个可运行的脚本中,循环处理目标关键词列表,收集每个关键词的结构化 SEO 数据,并将所有内容写入 JSON。在循环中用短暂睡眠限速可以使多关键词运行健康进行,而非连续触发请求。
import json import time import requests from urllib.parse import quote_plus, urlparse from bs4 import BeautifulSoup API_TOKEN = "YOUR_CRAWLBASE_TOKEN" API_ENDPOINT = "https://api.crawlbase.com/" KEYWORDS = [ "web scraping api", "how to scrape google", "rotating proxy service", ] def search_url(keyword): return f"https://www.google.com/search?q={quote_plus(keyword)}&hl=en&gl=us" def crawl(url): params = {"token": API_TOKEN, "url": url, "javascript": "true"} response = requests.get(API_ENDPOINT, params=params) response.raise_for_status() data = json.loads(response.text) if data["original_status"] != 200: raise Exception(f"Unable to crawl '{url}'") return data["body"] def scrape_serp(html, keyword): soup = BeautifulSoup(html, "html.parser") results = [] for position, block in enumerate(soup.select("div.g"), start=1): heading = block.select_one("h3") link = block.select_one("a[href]") snippet = block.select_one("div.VwiC3b") if not heading or not link: continue url = link["href"] results.append({ "keyword": keyword, "position": position, "title": heading.get_text(strip=True), "url": url, "domain": urlparse(url).netloc, "snippet": snippet.get_text(strip=True) if snippet else None, }) paa = [q.get_text(strip=True) for q in soup.select("div.related-question-pair") if q.get_text(strip=True)] related = [r.get_text(strip=True) for r in soup.select("div.y6Uyqe div.b2Rnsc, a.k8XOCe") if r.get_text(strip=True)] return {"keyword": keyword, "results": results, "people_also_ask": paa, "related_searches": related} def main(): serps = [] for keyword in KEYWORDS: html = crawl(search_url(keyword)) serps.append(scrape_serp(html, keyword)) print(f"Scraped '{keyword}': {len(serps[-1]['results'])} results") time.sleep(2) with open("google_seo_data.json", "w", encoding="utf-8") as f: json.dump(serps, f, ensure_ascii=False, indent=2) if __name__ == "__main__": main()
运行 python main.py。它循环处理三个示例关键词,获取并渲染每个 SERP,提取自然结果以及 PAA 和相关搜索块,并将所有内容写入 google_seo_data.json。替换为您自己的目标关键词,相同的两个函数即可处理任何返回的结果。如需更深入了解爬取本身(包括跨结果页分页),请参阅我们的专题指南 用 Python 爬取 Google 搜索结果。
输出示例
每个关键词对应一个结构化对象:按排名顺序排列的自然结果(各自派生了域名),以及 Google 为该查询附加的 PAA 问题和相关搜索。
{ "keyword": "web scraping api", "results": [ { "keyword": "web scraping api", "position": 1, "title": "Crawling API - Crawlbase", "url": "https://crawlbase.com/crawling-api-avoid-captchas-blocks", "domain": "crawlbase.com", "snippet": "Crawl any website with a single API call and skip blocks and CAPTCHAs." }, { "keyword": "web scraping api", "position": 2, "title": "Best Web Scraping APIs Compared", "url": "https://example-blog.com/web-scraping-apis", "domain": "example-blog.com", "snippet": "A side-by-side look at popular scraping APIs and how they handle blocks." } ], "people_also_ask": [ "What is a web scraping API?", "Is using a scraping API legal?" ], "related_searches": [ "free web scraping api", "web scraping api python" ] }
这是回答所有 SEO 问题的原始素材:排名在 position 中,竞争格局在 domain 中,点击率信号在 title 和 snippet 中,内容创意在 people_also_ask 和 related_searches 中。
用 pandas 分析 SEO 数据
爬取提供数据,pandas 将其转化为洞见。将自然结果的扁平列表加载到 DataFrame 中,您就可以回答驱动大多数 SEO 工作的三个问题:我的排名在哪里、我与谁竞争、我还没有覆盖什么。读取 CSV 并统计顶级域名的传统方法仍然有效;这里我们直接从爬取对象进行分析。
import json import pandas as pd with open("google_seo_data.json", encoding="utf-8") as f: serps = json.load(f) # Flatten every organic result across all keywords into one table rows = [r for serp in serps for r in serp["results"]] df = pd.DataFrame(rows) # 1. Where do I rank? Find your own domain's position per keyword MY_DOMAIN = "crawlbase.com" mine = df[df["domain"] == MY_DOMAIN][["keyword", "position", "title"]] print("Your rankings:") print(mine.to_string(index=False)) # 2. Who am I competing with? Domains that appear most across SERPs top_domains = df["domain"].value_counts().head(10) print("\nTop competing domains:") print(top_domains) # 3. Content gaps: PAA and related searches you have not covered yet ideas = [] for serp in serps: for q in serp["people_also_ask"] + serp["related_searches"]: ideas.append({"keyword": serp["keyword"], "idea": q}) gaps = pd.DataFrame(ideas).drop_duplicates() print(f"\n{len(gaps)} content ideas from PAA and related searches") print(gaps.head(10).to_string(index=False)) df.to_csv("google_seo_results.csv", index=False)
这里产出了三种分析。第一种将表格过滤到您自己的域名,打印每个关键词的排名位置,即排名追踪。第二种对 domain 列运行 value_counts(),找出在您的关键词集合中出现最多的网站(即主导您所在细分市场的竞争对手)。第三种将所有 PAA 问题和相关搜索汇集成去重后的内容创意列表:您可以回答的真实用户查询,以赢得精选摘要和长尾流量。最后一行将扁平化结果写入 google_seo_results.csv,使您可以逐周比较排名差异。
搜索量、关键词难度和 CPC 不在 SERP 上,因此本工作流不会凭空生成它们。请从 Google Keyword Planner、Ahrefs 或 SEMrush 等关键词工具获取这些数据,然后通过 keyword 列将其与 DataFrame 合并,将排名位置与机会规模结合起来。更多关于将爬取信号转化为策略的内容,请参阅我们的 用数据改善 SEO 指南。
跨关键词和竞争对手扩展
三个关键词是演示;真实任务需要追踪几十或数百个,有时还包括广告结果。结构保持不变:扩展 KEYWORDS 列表,通过 Crawling API 获取每个 SERP,并用相同的函数解析。以下几个习惯可以保持较大规模的运行健康。
-
限制请求速率。在关键词之间保留
time.sleep(),避免在紧密循环中运行数百次搜索。分散工作是保持不被屏蔽的最重要因素。 - 免费重试被屏蔽的 URL。API 返回的任何 5XX 响应都是免费的,因此重试被屏蔽或不可用的关键词不花任何费用。
- 字段为空时重新检查。Google 经常更改其标记。如果某列全部为空,请在开发者工具中打开实时 SERP 并更新选择器。
- 安排定期重新运行。排名会变动,因此每周对同一关键词集运行一次,并将每次运行的结果与日期列一起存储,以便随时间追踪排名变化。
如果您还想研究 SERP 的付费侧,同样的获取解析模式适用于赞助结果;我们关于 分析竞争对手 Google Ads 的指南对此进行了说明。有关更广泛的防屏蔽策略,请参阅 如何在不被屏蔽的情况下爬取网站。
爬取 Google SEO 数据是否合法?
爬取 Google 是否被允许,取决于 Google 的服务条款、您所在的司法管辖区以及您对数据的用途。Google 的条款对其搜索服务的自动访问设有限制,因此无论您的技术手段多么谨慎,爬取都可能违反这些条款。这里的代码无法改变这一点,它只是让技术部分得以实现。请阅读 Google 的条款及其 robots.txt,并将两者视为您采集内容的边界。
以下几条值得遵守。仅采集公开搜索结果数据:任何人无需账号即可在结果页面上看到的排名、标题、摘要、PAA 问题和相关搜索。将请求量控制在不给 Google 服务器造成负担的水平,并限制爬取速率而非全速运行。页面上的标题和摘要是 Google 对其他网站内容的渲染,请将其用作分析信号,而非批量转载的媒体。
本工作流刻意将范围限定在公开 SERP 数据,因为这是使工作具有可辩护性的边界。它不涵盖任何登录后的内容、账户或个人数据,以及从链接目标爬取的受版权保护媒体。对于经过授权的大规模 Google 数据访问,Google 提供官方产品,如 Custom Search JSON API 以及用于广告数据的 Google Ads API;当项目超出公开 SERP 分析范围时,这些才是正确的路径。使用爬虫读取页面上的内容,而非越过它去获取其他内容。
核心要点
- SEO 数据就在 SERP 上。排名、标题、摘要、PAA 问题和相关搜索都公开显示在结果页面上,它们共同映射出您的位置、竞争对手和内容缺口。
- 普通请求会被屏蔽。Google 用 CAPTCHA 挑战数据中心 IP,并用 JavaScript 渲染特性,因此您需要可信 IP 加上渲染能力,Crawling API 在一次调用中就能处理。
-
BeautifulSoup 负责提取。选择每个
div.g获取自然结果,从每个 URL 派生域名,从各自块中提取 PAA 和相关搜索;预期类名会发生漂移。 -
pandas 将数据转化为洞见。将结果扁平化到 DataFrame,过滤到您的域名进行排名追踪,对域名运行
value_counts()发现竞争对手,合并 PAA 和相关搜索挖掘内容创意。 - 坚守公开数据。遵守 Google 的服务条款和 robots.txt,保持低请求量,从关键词工具获取搜索量和 CPC,并使用 Google 的官方 API 进行授权的大规模访问。
常见问题
我可以从 Google 搜索结果中提取哪些 SEO 数据?
您可以提取自然搜索排名、meta 标题、meta 描述(摘要)、结果 URL 及其域名、"People also ask"问题和相关搜索。这些涵盖排名追踪、竞争对手分析和内容创意。搜索量和 CPC 不在页面上,需从 Google Keyword Planner、Ahrefs 或 SEMrush 等关键词工具获取并合并。
如何在不被屏蔽的情况下爬取 Google 搜索结果?
Google 用 CAPTCHA 和速率限制挑战自动流量,尤其是来自数据中心 IP 的流量。轮换 IP,限制请求速率,渲染 JavaScript 以便脚本加载的特性出现,并让托管服务处理 CAPTCHA 部分。通过 Crawling API 获取(它在服务器端轮换 IP 并解决 CAPTCHA),使每次请求看起来像普通访客,您就能得到真实的 SERP。
提取和分析 Google SEO 数据需要哪些工具?
三个库就足够了:Crawling API 用于在不被屏蔽的情况下获取和渲染每个 SERP,BeautifulSoup 用于从 HTML 中解析自然结果和 SERP 特性,pandas 用于将所有内容加载到 DataFrame 进行排名、竞争对手和内容缺口分析。Ahrefs 或 SEMrush 等关键词工具补充搜索量和难度数据。
如何从 SERP 中发现内容缺口?
收集每个目标关键词的"People also ask"问题和相关搜索,将其汇集成一个去重列表,并与您网站已覆盖的话题对比。剩余的问题就是您尚未回答的真实用户查询,这恰好是新内容和精选摘要机会的所在。
我可以随时间追踪排名变化吗?
可以。按计划(通常是每周一次)对同一关键词集运行,并将每次运行的结果写入带有日期列的 CSV。将合并表过滤到您自己的域名,即可得到每个关键词的排名历史,您可以用 pandas 或电子表格绘图,查看 SEO 工作是否推动了变化。
Google 是否为这些数据提供官方 API?
Google 为其中一些数据提供官方产品,如用于程序化搜索的 Custom Search JSON API 和用于广告数据的 Google Ads API。这些是大规模或商业用途的授权路径。对于本指南涵盖的公开 SERP 信号,只要遵守 Google 的条款并坚守公开数据,通过 Crawling API 爬取渲染后的结果页面是一个实用的选择。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
