返回博客

构建一个用于SEO研究的Google SERP快照数据集

Michael Lee
Michael Lee

Expert Network Defense Engineer

11-Sep-2026

TL;DR:

  • 谷歌SERP跟踪需要记录搜索,而不仅仅是排名列。 保存确切的请求、客户端时间戳、处理状态和原始响应。
  • 从你自己的观察中建立历史。 当前搜索请求无法填补收集开始之前的日子。
  • 在固定的上下文中比较有机结果。 查询、国家、语言或收集深度的变化会创建不同的比较组。

当电子表格不再记录哪个搜索生成了排名值时,排名值失去了意义。同一页面可以在不同的查询中出现,在不同的市场中出现,并且出现在不同的结果模块旁边。一个有用的数据集将这些条件附加在每个观察上。

谷歌SERP跟踪从可重复的收集和明确的可比较结果定义开始。Scrapeless Google Search API 提供结构化的搜索数据;该应用程序提供存储政策和历史。本文章明确建立了这个边界,从捕获文件到另一个分析师可以检查的数据集。

定义观察前的计划

快照是一个配置搜索请求的保存结果。它包括提交的输入和客户端收到的响应。它并不建立用户在该查询上看到的所有内容。

在安排收集之前写下数据集的范围。选择查询列表、市场上下文、语言、结果类型和分页政策。将每个查询分配给一个研究主题,以便稍后的报告可以解释为什么包含该查询。已审查的小样本比每周意图变化的大量集合更易于解释。

将收集计划与搜索上下文分开。上下文确定哪些观察可以进行比较;时间戳确定应用程序请求和接收它们的时间。客户端收据时间戳并不是关于谷歌生成页面的确切时刻的声明。

保留原始记录和衍生行

原始记录应保留请求、响应和收集状态。衍生结果行应通过稳定的运行标识符指向该记录。

在有机结果投影中保留字段,如 positiontitlelinksnippet。如果需要保留源顺序,还应将数组顺序作为单独字段保留。缺失的返回位置必须保持缺失;数组索引不应默默替代它。

JSON数据模型 为数组、对象和空值赋予不同的含义。即使报告层后来使用更简单的表,也要在档案中保持这些区别。原始响应使得可以更改映射而无需重新收集无法再现的观察。

与衍生数据一起存储解析器版本。当解析器被修正时,重新生成受影响的投影并标记新版本。将解析器更新视为市场变化会导致假趋势。

捕获文件的前提条件

使用安装了 requests 包的Python,并通过 SCRAPELESS_API_KEY 提供Scrapeless API密钥。使用 python3 -m pip install requests 安装客户端。其余导入使用标准库。

将以下脚本保存为 capture_snapshot.py 并运行 python3 capture_snapshot.py。它在 snapshots 下写入一个唯一命名的JSON文件。确保该目录可写并包含在你的存储政策中。该示例是一个本地捕获程序;它不提供调度程序、数据库或任务结果检索服务。

身份验证的运行需要你自己的账户密钥。此处不请求任何实时账户结果。请求接口已与当前的谷歌搜索请求工作流核对;周围的文件和状态处理可以在本地测试。

捕获请求及其处理状态

捕获程序发送 actor: scraper.google.search,其中包含 input 内部的搜索设置。身份验证使用 x-api-token 头。

注意:此块需要 SCRAPELESS_API_KEY 和服务访问。本文未针对实时账户执行。待处理的任务响应已保存以供检查;任务结果检索超出了这个示例。

python Copy
import json
import os
import uuid
from datetime import datetime, timezone
from pathlib import Path
import requests

def capture(input_parameters, directory="snapshots"):
    key = os.environ["SCRAPELESS_API_KEY"]
    request = {"actor": "scraper.google.search", "input": input_parameters}
    record = {
        "schema_version": 1,
        "run_id": str(uuid.uuid4()),
        "requested_at": datetime.now(timezone.utc).isoformat(),
        "request": request,
    }
    try:
        response = requests.post(
            "https://api.scrapeless.com/api/v1/scraper/request",
            headers={"x-api-token": key}, json=request, timeout=120,
        )
        record["http_status"] = response.status_code
        record["received_at"] = datetime.now(timezone.utc).isoformat()
        try:
            payload = response.json()
        except ValueError:
            payload = None
            record["response_text"] = response.text
        record["response"] = payload
        organic = payload.get("organic_results") if isinstance(payload, dict) else None
        if response.status_code == 201:
            record["state"] = "pending"
        elif response.status_code != 200:
            record["state"] = "http_error"
        elif not isinstance(organic, list) or any(not isinstance(x, dict) for x in organic):
            record["state"] = "unmapped"
        else:
            record["state"] = "observed" if organic else "empty"
    except requests.RequestException as exc:
        record["state"] = "transport_error"
        record["error_type"] = type(exc).__name__
    root = Path(directory)
    root.mkdir(parents=True, exist_ok=True)
    path = root / (record["run_id"] + ".json")
    with path.open("x", encoding="utf-8") as handle:
        json.dump(record, handle, ensure_ascii=False, indent=2)
    print(path, record["state"])
    return path

if __name__ == "__main__":
    capture({"q": "coffee", "gl": "us", "hl": "en", "start": 0,
             "google_domain": "google.com", "device": "desktop"})

脚本在HTTP操作后写入记录,包括对收集覆盖有用的错误结果。其异常记录包含异常类型,而不是可能暴露不必要请求细节的完整诊断字符串。将密钥与存档搜索数据分开存储。

120 超时是一个应用选择。这不是服务响应时间的保证。Python的时区感知时间戳使客户端时间显式;当另一个收集者写入数据集时使用相同的约定。

使用Scrapeless开始抓取

通过Scrapeless提升您的网页抓取和自动化工作流程!
今天注册获取**$5的免费信用**——不需要信用卡

立即在Scrapeless仪表板中领取您的免费信用。

将缺失收集视为其自身结果

观察失败并不意味着被追踪的域消失。状态字段保护报告免受该错误的影响。

observed 意味着成功的数据响应包含非空的有机对象数组。empty 意味着数组存在但为空。unmapped 意味着成功的响应与该最小形状不匹配。这些是应用标签,而不是额外的API状态代码。

pending 记录记录的HTTP 201任务状态。将返回的任务标识符与原始响应一起保留,并在将该运行计入观察之前使用单独验证的结果检索工作流。HTTP错误和传输错误应归入收集报告,而不是排名运动图表中。

在准备每周视图时,展示多少计划观察是可用的。在结果比较旁边保持缺失或未解决运行的列表。一个安静地排除失败收集的图表可能看起来稳定,而其证据却逐渐变薄。

比较同类有机结果

比较键应包括每个提交的搜索设置,这些设置可能会影响观察。最简单的保守实现序列化整个请求对象,并按键排序,仅排除应用时间戳和运行标识符。

Python的确定性JSON序列化选项支持排序字典键。这为您的应用提供了一种可重复的提交设置表示。它并不能证明不同的设置在语义上是等同的,也不会冻结上游搜索行为。

在比较页面切片时,保持start在关键中。如果报告将多个页面合并为一个收集窗口,请单独定义该更高级别的窗口,并标记缺失页面。不要将第一页观察与更深层次的收集混合,并将差异称为排名增益。

仅在得到您收集的响应支持的解释中使用返回的有机位置。保持图像、本地和其他模块独立。搜索控制台的位置测量规则描述了一个不同的报告系统;一个抽样的API位置不应被重新标记为搜索控制台的平均位置。

构建可审查的变更日志

有用的变更记录识别旧运行、新运行、上下文、受影响的URL和检测更改的规则。它应该描述观察,然后再建议原因。

对于域,区分“在两个捕获切片中均存在”、“在该切片中新观察到”和“在后来的切片中未观察到”。最后一个标签比“从Google移除”要狭窄。该域可能在收集的深度之外,而URL替换可以使域的存在保持不变。

对于URL,保留精确链接和规范主机的比较。规范化可以帮助分组页面,但丢弃路径、参数或子域也可能合并研究关心的内容。记录每个规范化规则,并将原始链接保留在派生值旁边。

将变更路线引导至人工审查,提供保存的证据。页面更新、查询上下文和更广泛的搜索变更都可能值得调查。仅凭一对快照无法确定哪一个导致了移动。

结论

以明确的范围和原始捕获记录开始数据集。仅在理解运行状态后,添加派生的有机行,然后比较请求设置匹配的记录。结果是您团队可以审查的历史,收集的空白可见,而不是转变为排名声明。
一个 Python 搜索集 演练提供了额外的背景;使用此处显示的当前请求接口来进行此数据集的收集。

构建您下一次搜索观察

围绕您的团队需要回答的问题配置 Google Search API。在设定收集频率之前,查看 Scrapeless 定价Google Search 参数模型 解释了在此工作流程中使用的上下文控制。

DiscordTelegram 上与社区讨论您的实施方案。

常见问题解答

问:这是否会获取收集开始之前的历史 Google 排名?

不。这一工作流程依据您保存的观察数据建立历史记录。它不会创建早期快照或提供历史排名数据库。

问:一个空的有机数组是否与请求失败相同?

不。目前存在的空数组记录为 empty;HTTP 错误、传输错误、待处理任务和未映射的响应有各自的状态。

问:不同国家可以共享相同的排名历史吗?

它们可以共享一个存储系统,但应保持独立的比较组。国家是请求上下文的一部分。

问:结果位置是否衡量访问量或收入?

不。它描述的是返回的搜索观察结果。流量和商业结果需要它们自己的证据和匹配定义。

问:快照应该多久收集一次?

选择一个与研究问题、预算和审查能力相匹配的节奏。记录遗漏的观察结果,并避免暗示采样时间表捕捉到每一个变化。

在Scrapeless,我们仅访问公开可用的数据,并严格遵循适用的法律、法规和网站隐私政策。本博客中的内容仅供演示之用,不涉及任何非法或侵权活动。我们对使用本博客或第三方链接中的信息不做任何保证,并免除所有责任。在进行任何抓取活动之前,请咨询您的法律顾问,并审查目标网站的服务条款或获取必要的许可。

最受欢迎的文章

目录