Google 每天处理数十亿次搜索,每次查询背后的结果页面是某个词语排名情况的结构化快照:自然结果的标题、它们指向的链接,以及每条下方的摘要文本。这使得公开的 Google SERP 成为关键词研究、排名追踪和竞争对手分析的有用信号。数据就摆在结果页面上,关键在于如何稳定地获取它。

这正是一份聚焦、可运行的操作教程。你将设置 Python,通过 Crawling API 获取已渲染的 Google 结果页面,将每条自然结果解析为标题、链接和摘要,翻页获取更深层的结果,并导出为 JSON 和 CSV。本教程范围限定在任何人无需账号即可看到的公开搜索结果数据。若想全面了解 SERP 结构、多种方案和扩展策略,请阅读综合指南如何抓取 Google 搜索页面;本文专注于交付可用的爬虫。

你将构建什么

一个 Python 脚本,接收一个搜索查询词,通过 Crawling API 获取已渲染的 Google 结果页面,并为页面上的每条自然结果返回一条整洁的记录。我们将使用一个示例查询作为贯穿全文的例子,并从每条结果中提取以下字段:

  • 排名位置:结果在页面上的排名,从顶部开始计数。
  • 标题:结果的标题文字,与列表中显示的一致。
  • 链接:结果指向的目标 URL。
  • 摘要:标题下方显示的描述或摘要。
  • 相关搜索:Google 在页面底部列出的建议延伸查询。

为什么普通请求在 Google 上会失败

从脚本向 google.com/search 发起一个裸 HTTP 请求,几乎不会得到你在浏览器中看到的页面。两个问题与你作对。首先,Google 现在依赖 JavaScript 来渲染结果页面:截至 2025 年,SERP 需要启用脚本才能加载,因此不执行 JavaScript 的普通请求只能得到一个空壳而非列表。其次,Google 会监控自动化流量。来自单个 IP 的过多请求、请求速度过快,或不像真实浏览器的请求,都会遭遇 CAPTCHA、限速或拦截。

因此,一个有效的 Google 爬虫需要在单次请求中同时具备两项能力:一个被搜索引擎视为普通访客的 IP,以及一个能渲染页面的浏览器。你可以自行组合无头浏览器加轮换住宅代理池,但维持这套设施健康运行才是绝大部分工作量。Crawling API 将两者集成到单次调用中。它还内置了 google-serp 爬虫,能从可信的轮换 IP 获取已渲染页面,并将自然结果解析为 JSON,让你无需针对 Google 频繁变化的标记编写脆弱的选择器。

前置条件

在编写任何代码之前,你需要准备几样东西。都不会花太长时间。

基础 Python。你应该能够编写和运行 Python 脚本,并使用 pip 安装包。如果你对网络爬取还比较陌生,我们的使用 Python 抓取网站指南涵盖了本教程所假设的基础知识。

Python 3.8 或更高版本。使用 python --version 确认你的版本。如果没有,可从 python.org 安装或通过 Anaconda 等发行版获取。任何编辑器都可以;VS Code、PyCharm 和 Jupyter 都适用。

Crawlbase 账号和 token。注册后打开控制台,复制你的请求 token。前 1,000 次请求免费,无需信用卡,内置的 google-serp 爬虫可与普通 token 配合使用。请像对待密码一样保管 token:它用于验证你的请求,因此不要将其提交到版本控制中。

设置项目

创建虚拟环境以隔离项目依赖,然后安装 Crawlbase Python 库,它将 Crawling API 封装在一个小型客户端中。

bash
python --version

python -m venv google_env
source google_env/bin/activate

pip install crawlbase

在 Windows 上,使用 google_env\Scripts\activate 代替 source 行来激活环境。crawlbase 包为你提供了一个 CrawlingAPI 客户端,无需手动构建请求 URL。由于 google-serp 爬虫返回的是解析后的 JSON,本教程中处理自然结果时甚至不需要 HTML 解析库。

步骤 1:通过 Crawling API 获取已渲染的 SERP

先获取数据。编写一个小的 scrape_google_results() 函数,它构建搜索 URL,启用 google-serp 爬虫后将其发送至 Crawling API,检查状态,并返回解析后的 body。Google 以 10 条为一组进行分页,因此该函数接受页码并将其转换为 Google 在 URL 中期望的 start 偏移量。

python
import json
from urllib.parse import quote_plus
from crawlbase import CrawlingAPI

# Replace with your token from the Crawlbase dashboard
crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def scrape_google_results(query, page=0):
    encoded = quote_plus(query)
    url = f"https://www.google.com/search?q={encoded}&start={page * 10}"
    options = {"scraper": "google-serp"}
    response = crawling_api.get(url, options)

    if response["headers"]["pc_status"] == "200":
        data = json.loads(response["body"].decode("latin1"))
        return data.get("body", {})

    print(f"Failed to fetch results for '{query}' (page {page}).")
    return {}

if __name__ == "__main__":
    results = scrape_google_results("web scraping tools", page=0)
    print(json.dumps(results.get("searchResults", [])[:2], indent=2))

options 字典是关键所在:传入 {"scraper": "google-serp"} 告诉 Crawling API 渲染页面、绕过机器人检测,并返回已解析的 SERP。客户端返回一个响应,其 headers["pc_status"] 是 Crawlbase 对本次爬取的状态,因此在解析前判断 "200" 可以让拦截或渲染失败明显显示,而不是将错误数据传入下游。body 以字节形式返回,用 latin1 解码后以 JSON 加载,然后从 body 键中读取解析后的 SERP。运行脚本,你应该会看到前两条自然结果以 JSON 形式打印出来,这确认了获取和解析在继续构建之前已正常工作。

Crawlbase Google Scraper

pc_status 返回 200,正是因为请求以真实访客身份到达了 Google,页面已渲染且机器人检测已处理,之后 google-serp 爬虫才将其转换为你刚刚打印出的 JSON。Crawling API 在服务器端进行 JavaScript 渲染并轮换住宅 IP,让你无需自行运行无头浏览器集群和代理池。先在免费层将其指向公开结果 URL 试试看。

步骤 2:将自然结果解析为整洁记录

google-serp 爬虫返回已结构化的 SERP,因此解析只需读取你想要的字段,而无需编写 CSS 选择器。自然列表位于 searchResults 键下,每条包含 positiontitleurldescription。页面还提供 relatedSearches,爬虫在查询触发时还会暴露 adspeopleAlsoAsk 和本地 snackPack。编写一个小函数,挑选你关心的字段并规范化摘要字段名。

python
def parse_results(serp):
    organic = []
    for item in serp.get("searchResults", []):
        organic.append({
            "position": item.get("position"),
            "title": item.get("title"),
            "link": item.get("url"),
            "snippet": item.get("description"),
        })

    related = [r.get("title") for r in serp.get("relatedSearches", [])]

    return {"organic": organic, "relatedSearches": related}

这保留了大多数排名追踪和研究任务所需的四个字段:直接来自 Google 的 positiontitle、目标 link(从结果的 url 字段读取)以及 snippet(从 description 读取)。免费附带 relatedSearches 为关键词扩展提供了建议延伸查询。由于爬虫完成了字段提取,你可以免受 Google 标记变化的影响,只需始终访问这些稳定的 JSON 键。

为什么使用解析爬虫而非原始 HTML

你可以获取原始渲染 HTML 并用 BeautifulSoup 解析,但 Google 的结果容器类名是经过混淆的且频繁变化,手写选择器会频繁失效。google-serp 爬虫返回稳定的 JSON 键,如 searchResultspositionrelatedSearches,维护成本低得多。如果你想了解选择器方法用于其他网站,我们的 XPath 和 CSS 选择器指南有详细介绍。

步骤 3:处理分页

一页是演示;真正的任务需要深入更多结果。Google 通过 start 参数以 10 条为一组分页,第二页是 start=10,第三页是 start=20,以此类推,scrape_google_results 中页码到偏移量的转换已经处理了这一点。遍历一个页码范围,当某页为空时提前停止,并将所有自然记录收集到一个列表中。在循环中加入短暂的 sleep 可以让长时间运行保持健康。

python
import time

def scrape_all_pages(query, max_pages=3):
    all_organic = []
    for page in range(max_pages):
        print(f"Scraping page {page + 1}...")
        serp = scrape_google_results(query, page)
        parsed = parse_results(serp)

        if not parsed["organic"]:
            print("No more results, stopping.")
            break

        all_organic.extend(parsed["organic"])
        time.sleep(2)

    return all_organic

循环从第 0 页运行至 max_pages,获取每一页,解析并扩展运行列表。if not parsed["organic"]: break 判断会在某页没有自然结果时立即停止,防止你继续为列表末尾之后的空页付费。页面之间的 time.sleep(2) 将请求分散开来而不是密集发送,这是长时间运行保持不被封锁的最佳单一习惯。

步骤 4:组装完整脚本并导出

现在将获取、解析和分页整合为一个可运行脚本,并将输出写入 JSON 和 CSV。JSON 保留完整的嵌套结构;CSV 提供一个平铺表格,可在电子表格中打开进行快速排名检查。

python
import csv
import json
import time
from urllib.parse import quote_plus
from crawlbase import CrawlingAPI

crawling_api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def scrape_google_results(query, page=0):
    encoded = quote_plus(query)
    url = f"https://www.google.com/search?q={encoded}&start={page * 10}"
    options = {"scraper": "google-serp"}
    response = crawling_api.get(url, options)
    if response["headers"]["pc_status"] == "200":
        data = json.loads(response["body"].decode("latin1"))
        return data.get("body", {})
    print(f"Failed to fetch results for '{query}' (page {page}).")
    return {}

def parse_results(serp):
    organic = []
    for item in serp.get("searchResults", []):
        organic.append({
            "position": item.get("position"),
            "title": item.get("title"),
            "link": item.get("url"),
            "snippet": item.get("description"),
        })
    return organic

def scrape_all_pages(query, max_pages=3):
    all_organic = []
    for page in range(max_pages):
        print(f"Scraping page {page + 1}...")
        organic = parse_results(scrape_google_results(query, page))
        if not organic:
            print("No more results, stopping.")
            break
        all_organic.extend(organic)
        time.sleep(2)
    return all_organic

def save_json(rows, filename):
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(rows, f, ensure_ascii=False, indent=2)

def save_csv(rows, filename):
    fields = ["position", "title", "link", "snippet"]
    with open(filename, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(rows)

if __name__ == "__main__":
    query = "web scraping tools"
    rows = scrape_all_pages(query, max_pages=2)
    save_json(rows, "google_results.json")
    save_csv(rows, "google_results.csv")
    print(f"Saved {len(rows)} results to JSON and CSV")

使用 python main.py 运行。它抓取 "web scraping tools" 的两页结果,将每条自然结果平铺到一个列表中,并同时写入 google_results.jsongoogle_results.csv。要抓取不同词语,更改 query 字符串;要获取更深层的结果,增大 max_pages。设置 ensure_ascii=False 可使 JSON 文件中的非拉丁字符保持可读,而不是将其转义为 \u 序列。

输出示例

JSON 文件是一个有序的自然记录列表,每条包含排名位置、标题、链接和摘要,可直接导入数据库或排名追踪表格。

json
[
  {
    "position": 1,
    "title": "Web Scraper - The #1 web scraping extension",
    "link": "https://webscraper.io/",
    "snippet": "The most popular web scraping extension. Start scraping in minutes."
  },
  {
    "position": 2,
    "title": "ParseHub | Free web scraping - The most powerful web scraper",
    "link": "https://www.parsehub.com/",
    "snippet": "ParseHub is a free web scraping tool. Turn any site into a spreadsheet or API."
  }
]

CSV 文件以平铺表格的形式保存相同记录,每条结果一行,列为 positiontitlelinksnippet,这正是大多数电子表格式排名追踪所期望的格式。

跨查询扩展规模与保持不被封锁

上述脚本抓取单个查询的几个页面。生产任务通常需要运行大量查询,并可能随时间追踪,但结构不变:遍历你的查询列表,对每个查询调用 scrape_all_pages,并在保存前为每条记录打上来源查询的标签。Crawling API 在每次请求时处理渲染和 IP 轮换,因此扩展主要取决于你对自己的节奏控制和状态监控。以下几个习惯能让长时间运行保持健康。

  • 控制请求节奏。在页面和查询之间保持 sleep,而不是密集循环发送。将流量分散开来是防止被挑战的最佳单一防御手段。
  • 关注状态码。监控每个响应的 pc_status。一次开始失败的运行是在告诉你需要降速,而不是可以忽略的噪音。
  • 对瞬时失败进行重试。来自 API 的任何 5XX 响应都不计费,因此重试失败的爬取不产生成本;在重试前进行短暂退避通常能解决问题。
  • 依赖 IP 轮换。内置的住宅 IP 轮换将请求分散到众多真实用户地址。如果你想路由自己的流量,Smart AI Proxy 提供与之相同的轮换能力作为直接替换的代理端点。

更广泛的操作手册请参阅如何不被封锁地抓取网站。如果你还想拉取 Google 在页面中间显示的问题框,我们专注于抓取 Google 相关问题的指南专门介绍了该模块。

抓取 Google 搜索结果合法吗?

抓取 Google 是否被允许,取决于 Google 的服务条款、你所在的司法管辖区以及你对数据的使用方式。Google 的条款对自动化访问设有限制,因此无论你的工具多么谨慎,抓取行为都可能违反这些条款。这里的任何代码都不改变这一事实,它只是使技术部分可行。请阅读 Google 的条款和 robots.txt,并将两者视为你收集范围的边界。

以下几条值得坚守。只收集公开搜索结果数据:任何人在结果页面上无需账号即可看到的标题、链接、摘要和排名位置。将你的请求量控制在不会给 Google 服务器造成压力的范围内,控制爬取节奏而不是全速运行。不要抓取个人数据,不要获取登录墙后的内容,也不要重新发布你通过结果链接访问的受版权保护的媒体。这些是让工作保持合理性的界限。

如果你的项目需要经过授权的大规模访问,Google 为此提供了官方产品,包括 Custom Search JSON API 和通过 Google Cloud 进行的程序化搜索,这才是该规模下的正确路径,而不是更聪明的爬虫。本指南刻意将范围限定在公开 SERP 页面,因为这是保持工作干净的边界。有关 Google 在爬取者面前设置的具体障碍及如何负责任地处理它们的更多内容,请参阅我们关于抓取 Google 搜索结果的挑战的文章。

回顾

核心要点

  • 普通请求在 Google 上会失败。2025 年的 SERP 需要 JavaScript 渲染,且 Google 会拦截看起来像爬虫的流量,因此你需要在一次请求中同时具备渲染能力和可信 IP。
  • google-serp 爬虫承担了繁重工作。传入 {"scraper": "google-serp"},Crawling API 就会渲染页面、处理机器人检测,并以解析后的 JSON 形式返回 SERP。
  • 读取稳定的 JSON 键,而非选择器。searchResults 中提取 positiontitleurldescription,这样你就与 Google 的标记变化隔离开来。
  • 用 start 偏移量分页。以 10 的倍数增加 start 以深入结果,当某页返回空结果时停止,并在页面之间加入 sleep。
  • 坚守公开数据。遵守 Google 的服务条款和 robots.txt,保持合理请求量,对高流量场景优先使用官方 Custom Search API,永远不要碰个人数据或登录后的数据。

常见问题

为什么普通请求从 Google 返回一个空页面?

截至 2025 年,Google 的结果页面需要 JavaScript 才能渲染,因此不执行脚本的裸 HTTP 请求只能得到一个空壳。Google 还会拦截看起来像自动化的流量。通过 Crawling API 使用 google-serp 爬虫进行获取,可以从可信的轮换 IP 渲染页面,并以解析后的 JSON 形式返回列表,这样你得到的是真实结果而非空页面。

我可以使用 Python 抓取 Google 搜索结果吗?

可以。使用 Crawlbase Python 库调用 Crawling API,启用内置的 google-serp 爬虫,就能直接从返回的 JSON 中读取标题、链接、摘要和排名位置。API 充当桥梁,从可信 IP 将你的请求送达 Google 并完成页面渲染,让请求顺利处理而不被拦截。若想全面了解 SERP 和其他方案,请参阅抓取 Google 搜索页面综合指南

这与更广泛的 Google 抓取指南有何不同?

本文是聚焦的、可运行的 Python 教程:设置、获取、解析自然结果、分页和导出,附带可直接复制粘贴的代码。综合指南如何抓取 Google 搜索页面涵盖了完整的 SERP 结构(广告、相关问题、知识面板、本地信息包)、多种方案和扩展策略。如果你想立即获取代码,从这里开始;如果你想了解更广泛的全貌,则阅读综合指南。

如何分页获取更多 Google 结果?

Google 通过 start 查询参数以 10 条为一组分页:第二页是 start=10,第三页是 start=20,以此类推。scrape_google_results 函数将页码转换为该偏移量,scrape_all_pages 遍历页码范围,当某页没有自然结果时停止。在页面之间保持短暂 sleep 以控制爬取节奏。

抓取 Google 时需要自己处理 JavaScript 吗?

不需要。Google 的 SERP 需要 JavaScript 才能渲染,但 google-serp 爬虫在服务器端为你渲染页面,所以你不需要运行无头浏览器或管理渲染集群。只需发送附带爬虫参数的搜索 URL,就能获得完整渲染、已解析的结果。

如何存储抓取到的 Google 结果?

任何结构化格式都可以。本指南中的完整脚本同时写入 JSON 文件(保留嵌套结构)和 CSV 文件(提供包含排名位置、标题、链接和摘要的平铺表格,供电子表格使用)。从那里你可以将记录加载到数据库或排名追踪管道中。只收集公开搜索结果数据,避免任何登录墙后的内容。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量