如何使用 GSC API 和 BigQuery 为多语言网站构建 SEO 分析

如何使用 GSC API 和 BigQuery 为多语言网站构建 SEO 分析
Markus_automation
Markus_automation

Expert in data parsing and automation

对于多语言项目,整合 SEO 分析的任务很快就会超出标准 Web 界面的能力范围。分析中涉及的语言版本、地区和 URL 越多,需要收集、规范化并相互匹配的数据量和维度就越大。

对于较小的项目,可以通过手动导出和现成的报告来处理这一任务。随着项目规模扩大到数十种语言,这种方法就会变得效率低下:专家需要花费大量时间来收集和准备数据,而不是进行分析。分析界面本身的局限性也造成了额外的限制。例如,Google Search Console 显示的行数有限,这对于拥有成千上万个 URL 的项目来说是远远不够的。

这就是为什么对于大型多语言网站来说,将 SEO 指标的收集和处理移至编码层面是很有意义的。这使您能够自动进行定期导出,保留所需的指标详细程度,并构建一个对浏览器界面限制依赖较小的监控系统。在本文中,我们将探讨如何为多语言项目组织这样的架构,以及在实施时需要考虑哪些 Google Search Console API 限制。

内容

使用Octo Browser维护您的在线匿名性。您真实的数字指纹无法被追踪。

想以优惠价试用 Octo 浏览器吗?
使用优惠码 OCTOBLOG,即可享受任意订阅 30% 折扣。此优惠仅限新用户使用。

为什么要告别网页界面

一个可靠的 SEO 分析系统始于消除手动导出。Google Search Console 界面便于快速检查某些指标,但其功能不足以进行深入的产品分析。

GSC API 使获取更细粒度的数据成为可能——通过单个 URL、搜索查询和其他维度。这使您不仅可以处理聚合报告,还可以处理底层数据,并从中构建您自己的细分和指标。

为了理解这种架构的优势,考虑几个关键因素至关重要。

数据基数和网页界面局限性

GSC 界面的主要问题之一是它显示的数据量有限。这对于多语言项目尤为关键:当存在大量的页面、国家/地区、设备和查询时,很大一部分信息会留在标准报告之外。

这就是基数的重要性所在——数据集中唯一参数组合的数量。例如,如果一个网站以 10 种语言运行,接收来自 50 个国家/地区的流量,使用 3 种类型的设备,并对 10,000 个搜索查询进行排名,则可能的组合数量可达数百万行。

通过网页界面处理如此庞大的数据量几乎是不可能的。使用 API 可以检索多得多的数据,并将导出拆分为单独的细分。通过过滤和顺序处理,您不仅可以收集结果的头部部分,还可以收集通常在标准报告中丢失的长尾查询和 URL。

数据湖与历史存储

Google Search Console 在其界面中仅存储过去 16 个月的历史数据,这对于长期 SEO 分析来说是远远不够的。

专门的数据仓库(例如 BigQuery)可以解决这个问题。您可以定期将通过 API 获取的原始数据保存到其中,而不受 GSC 界面保留期的限制。这为您提供了一个历史 SEO 数据库,可用于长期分析、报告构建以及在任何必要维度中重新处理数据。

对外部服务的依赖以及扩展成本

现成的 ETL 连接器可用于自动执行数据导出,例如 Supermetrics 或 Fivetran 等 SaaS 服务。它们允许您快速设置数据传输,而无需内部开发,但随着项目规模的扩大,这种方法可能会变得昂贵,并将您的基础设施与特定服务过于紧密地绑定在一起。

SaaS 平台根据处理的行数或连接器数量对其服务收费。当您的多语言项目开始每天产生数千兆字节的原始 SEO 数据时,此类服务的成本可能会数倍于在 BigQuery 中存储数据和为 Python 脚本租用小型服务器的成本。

此外,一个服务融入数据收集和转换过程的程度越深,日后替换它就越困难:迁移可能需要重新配置连接器、处理逻辑和报告。

数据粒度级别

在通过 API 收集数据时,尽可能多地保留细节至关重要。如果您在导出阶段就已经合并了数据,则稍后将无法重构原始维度。

例如,在设计 SEO 数据库时,您应该将 Device 类型和 Country 等参数分开存储。如果脚本在请求数据时未按国家/地区细分,GSC 将返回该查询的总点击次数。此后,将无法确定有多少点击来自德国,有多少来自法国。

这就是为什么最好以详细的形式存储数据,并且仅在分析或可视化阶段合并数据并计算最终指标。这保留了在未来构建任何必要数据细分的能力,即使这些细分最初在报告中并未预料到。

导出大量数据时的 GSC API 局限性

乍一看,使用 GSC API 似乎很简单:授权脚本、检索数据、将其保存到数据库,然后继续进行分析。在实践中,大型项目很快就会遇到 API 的技术限制。

如果您在不考虑这些限制的情况下尝试导出大量数据,您可能会遇到超时、429 Too Many Requests 错误和不完整的导出。结果,只有部分数据会到达数据仓库,系统本身也会变得不稳定。

这就是为什么数据管道应该提前考虑 API 限制、数据更新延迟、查询配额以及针对失败请求的重试机制。让我们来看看主要的 GSC API 限制以及如何正确处理它们。

50,000 行限制和导出细分

根据 Google 的文档rowLimit 参数允许您在单次请求中检索不超过 25,000 行。startRow 参数用于分页:您可以最初请求前 25,000 行,然后再请求接下来的 25,000 行。

然而,还有一个附加限制startRow + rowLimit 的总和不能超过 50,000。因此,对于单个日期和所选的参数组合,您无法通过这种方式检索第 50,001 行。

如果您的每日数据基数超过 50,000 行,您需要使用维度过滤器(Dimension Filters)将导出拆分为单独的细分。例如,您可以单独查询语言文件夹(例如 /de//fr/ 等)的数据,或者另外使用正则表达式按模式划分 URL。

这使您能够通过针对不同数据细分的几个独立请求来获取完整的数据集。

数据延迟和处理 dataState

GSC API 存在 48 至 72 小时的系统性数据更新延迟。因此,在每日导出数据时,区分初步数据和最终数据至关重要。

dataState 参数对此进行控制。它有两个值:

  • "final"(默认)——仅返回完全聚合且验证过的数据。

  • "all"——包含尚未经过最终处理的新数据。

这在构建数据管道时在速度和准确性之间创造了权衡。让我们来看看这两种情况。

  • 使用 dataState: "final"(默认行为)。在此模式下,过去 24 小时的数据可能尚不可用。如果您的脚本尝试使用默认值导出 yesterday 的数据,API 将返回一个空数组。您需要应用 current_date — 3 days 的固定偏移量。

  • 使用 dataState: "all"。您将收到前 24 小时所需的数据集。然而,Google 警告称,新数据是初步的。系统尚未整合所有重复项、过滤掉垃圾机器人或重新计算异常值。2-3 天后,这些数字在 Google 自己的服务器上会发生变化。

这会如何影响您的存储架构:如果您的 Python 脚本只是简单地将新的原始数据附加(Append)到 BigQuery,您的历史数据库将会失真。当您写入“新鲜”指标时,您存储的是一个草稿,它永远不会与 GSC 界面中的最终报告完全一致。

因此,对于运营分析,最好使用两阶段法:

  1. 使用 dataState: "all" 导出前一天的数据。

  2. 同时,脚本应使用 dataState: "final" 重新导出 current_date — 4 days 的数据。

  3. 在 BigQuery 中,使用 MERGE 运算符(或 Upsert 逻辑)代替简单的 Append。脚本应在数据库中找到四天前的初步数据,并用最终的、整合后的值覆写它。

这种方法使您能够在仪表板上看到最新指标,同时在数据完成最终处理后保留正确的历史数据库。

API 限制和指数退避

GSC API 限制请求数量以保护其基础设施免受过大负载的影响。GSC API 有严格的配额:每个项目每秒 50 次查询 (QPS) 和每分钟 1,200 次查询 (QPM)。因此,在进行大规模导出时,需要提前考虑这些限制。

当为了解决 50,000 行限制而必须将数据拆分为数百个细分时,该问题尤为明显。为了加快这一过程,开发人员经常使用异步请求 (asyncio) 或线程池 (ThreadPoolExecutor)。但这会很快耗尽 50 QPS 的限制,API 开始返回 429 Too Many Requests503 Service Unavailable 错误。

使用 time.sleep() 进行简单延迟在这里效果并不理想。如果几个并行线程同时收到错误,然后休眠相同的时间,它们将几乎同时恢复运行并创造另一个流量峰值。

正确的脚本架构应该包含一个带有添加随机噪音(Jitter)的指数退避(exponential backoff)模式。这会导致重试请求之间的间隔逐渐增加。并非所有线程都会在同一时间重试,从而降低它们超出限制的几率。

在 Python 中,您不一定需要手动实现此逻辑:您可以使用 tenacity 库中的装饰器。

收集子域名和语言文件夹的 SEO 数据

在将 GSC API 限制和延迟考虑在内之后,下一个重要问题是多语言网站的具体结构是怎样的。项目结构直接影响导出、规范化和合并数据的逻辑。

在多语言 SEO 中,有两种截然相反的网站结构方法:国家子域名和语言文件夹。对于用户来说,差异极小,但对于构建导出,不同的结构会彻底改变方法。

子域名和独立域名

如果语言版本托管在独立的域名(site.desite.fr)或子域名(de.site.comfr.site.com)上,则必须将每个版本的数据作为来自独立资源的数据进行收集。

为什么这有利于业务? 区域隔离使您能够更严格地控制抓取预算。搜索引擎不会将德国机器人的抓取预算花在扫描法国版本的网站上。从 SEO 的角度来看,这是扩展最安全的路线。

对于分析管道,这会产生两个问题:

  • 更多的连接点。 如果项目有 10 个语言版本,脚本需要顺次或并行查询多个 GSC 资源。数据源越多,API 配额、错误处理以及整个导出架构的弹性就越重要。

  • URL 规范化复杂性(拼接数据)。 在不同域名上具有相同用途的页面将具有不同的地址——例如,site.de/productsite.fr/product。为了将它们的表现作为一个整体进行比较,需要对它们的 URL 进行规范化。

在 Python 的 Pandas 库中,这种规范化如下所示:

import pandas as pd
from urllib.parse import urlparse

# Keep only the path for stitching metrics across countries
df['normalized_url'] = df['page'].apply(lambda x: urlparse(x).path) 
# Result: /product
import pandas as pd
from urllib.parse import urlparse

# Keep only the path for stitching metrics across countries
df['normalized_url'] = df['page'].apply(lambda x: urlparse(x).path) 
# Result: /product

如果不进行这种规范化,不同本地化的指标仍将分散在不同的 URL 中。这将使计算相同页面或模板在不同语言中的整体表现变得更加困难。

语言文件夹和数据细分

如果语言版本放在文件夹中,例如 site.com/de/site.com/fr/,则整个项目都保持在单个域名内。这简化了数据收集:您无需向多个资源发出单独的请求,而是可以使用 Google Search Console 中的单个网域属性 (Domain Property)

无需进行 10 次独立的查询,您可以进行一次大型导出,使用过滤来解决 50,000 行的限制。这节省了 Google API 配额并减轻了网络负载。

大型导出仍需要划分为细分。但架构本身变得更简单:连接点更少、请求更少,且 API 负载更低。

因为 API 给我们提供的是连续的 URL 流,所以脚本必须自己给行分配国家标记。这可以通过正则表达式 (RegEx) 来完成。

使用 Pandas 库,我们可以直接从 URL 中提取语言标记:

df['language_market'] = df['page'].str.extract(r'\.com/([a-z]{2})/')
# Exception handling: if RegEx returns NaN, this is the main version of the site
df['language_market'].fillna('en', inplace=True)
df['language_market'] = df['page'].str.extract(r'\.com/([a-z]{2})/')
# Exception handling: if RegEx returns NaN, this is the main version of the site
df['language_market'].fillna('en', inplace=True)

这使您能够从 site.com/de/product 之类的 URL 中提取 de 标记,并将其用于进一步分析。

这种方法的主要限制是它对 URL 结构的依赖。正则表达式必须与用于构建语言版本的规则完全匹配。如果某些页面使用不同的模式,例如 site.com/category-de/product,则这些 URL 可能会被错误分类,或者可能根本无法进入所需的细分。

这就是为什么在配置正则表达式之前,检查所有可能的语言-URL 模式并单独处理异常至关重要。

Pandas 中的数据转换和指标计算

在收集并规范化数据后,需要将数据合并并准备进行分析。Pandas 非常适合处理此项任务:该库使处理大型表格、合并数据源以及在行级和组级计算指标成为可能。

通过 URL 合并 GSC 和 GA4 数据

Google Search Console 显示搜索指标——展示次数、点击次数、CTR 和排名。GA4 用行为和商业指标(如会话数和转化次数)对其进行补充。

为了获得更全面的 SEO 流量表现,可以使用通用键——规范化的着陆页 URL,来合并 GSC 和 GA4 数据。

在 Pandas 中,这是通过使用通用键(规范化的 URL)联接表格来完成的:

import pandas as pd

# df_gsc export from Search Console
# df_ga4 export from GA4 (Sessions, Conversions)

# Join the data by landing page (Left Join so that pages without traffic are not lost)
merged_df = pd.merge(df_gsc, df_ga4, how='left', left_on='landing_page', right_on='page_path')

# We can now calculate the conversion rate of a specific SEO cluster:
merged_df['seo_conversion_rate'] = (merged_df['conversions'] / merged_df['clicks']) * 100
import pandas as pd

# df_gsc export from Search Console
# df_ga4 export from GA4 (Sessions, Conversions)

# Join the data by landing page (Left Join so that pages without traffic are not lost)
merged_df = pd.merge(df_gsc, df_ga4, how='left', left_on='landing_page', right_on='page_path')

# We can now calculate the conversion rate of a specific SEO cluster:
merged_df['seo_conversion_rate'] = (merged_df['conversions'] / merged_df['clicks']) * 100

正确计算 CTR 和平均排名

在合并来自多个语言版本的数据时,您不能简单地通过算术平均值来计算 CTR 和平均排名。这会使结果失真,因为它没有考虑不同的展示量。

例如:

  • 法国子域名:4 次展示获得 2 次点击,CTR = 50%;

  • 德国子域名:1,000 次展示获得 20 次点击,CTR = 2%。

如果您只是简单地求 CTR 的平均值,您会得到:

(50% + 2%) / 2 = 26%

但在这两个子域名上的实际 CTR 是:

22 次点击 / 1004 次展示 = 2.19%

因此,在聚合来自不同本地化的数据时,需要从基础数据重新计算指标:

  • CTR 计算为总点击次数与总展示次数的比率。

  • 平均排名 应该按展示量进行加权:每行的排名乘以展示次数,然后将这些值的总和除以总展示次数。

在 Pandas 中,这可以实现如下:

# Group the data by search query across all countries
def weighted_metrics(x):
    # Weighted average position = Sum (Position * Impressions) / Sum (Impressions)
    weighted_pos = (x['position'] * x['impressions']).sum() / x['impressions'].sum()
    # Actual CTR
    real_ctr = (x['clicks'].sum() / x['impressions'].sum()) * 100
    
    return pd.Series({
        'total_clicks': x['clicks'].sum(),
        'total_impressions': x['impressions'].sum(),
        'weighted_position': weighted_pos,
        'real_ctr': real_ctr
    })

# Apply the function to the grouped dataframe
final_cluster_data = merged_df.groupby('query').apply(weighted_metrics)
# Group the data by search query across all countries
def weighted_metrics(x):
    # Weighted average position = Sum (Position * Impressions) / Sum (Impressions)
    weighted_pos = (x['position'] * x['impressions']).sum() / x['impressions'].sum()
    # Actual CTR
    real_ctr = (x['clicks'].sum() / x['impressions'].sum()) * 100
    
    return pd.Series({
        'total_clicks': x['clicks'].sum(),
        'total_impressions': x['impressions'].sum(),
        'weighted_position': weighted_pos,
        'real_ctr': real_ctr
    })

# Apply the function to the grouped dataframe
final_cluster_data = merged_df.groupby('query').apply(weighted_metrics)

自动化 hreflang 和蚕食监测

专门的数据仓库不仅可以用于报告,还可以用于自动检测技术性 SEO 问题。对于多语言项目,有两种类型的问题尤为重要:损坏的本地化关系以及多个页面对同一搜索需求的内部竞争。

自动化 hreflang 监测

国际项目的技术性 SEO 优化取决于本地化标签的一致性和双向性。hreflang 标签作为一个严格的双向交叉引用系统运行。例如,如果法国页面指向一个德国页面作为备用,则该德国页面必须包含一个互惠链接。破坏这个链条就会破坏 Google 眼中的整个集群。

对于大型项目,手动检查这些链接是不可能的,因此监测应基于至少两个数据源的对比:

  • Screaming Frog。 按计划运行爬虫。它会扫描 HTML 代码中标签的实际存在情况,并验证语言代码 (ISO 639-1) 和国家代码 (ISO 3166-1 Alpha 2)。结果会导出到数据库中。

  • GSC API。 Search Console API 提供索引报告,并展示 Google 在最近一次抓取过程中如何解释这些关系。

抓取结果和 Google 数据可以存储在同一个数据库中,然后使用 SQL 进行匹配。例如,FULL OUTER JOIN 可以在聚合数据中识别出关键错误:有些页面在代码中技术上具有正确的标签,但却被搜索引擎忽略了。

造成这种情况的原因可能有很多。一种可能是客户端渲染:JavaScript 框架(React、Vue)需要很长时间才能在 <head> 中渲染标签,或者性能指标 (Core Web Vitals) 太差,导致 Googlebot 在读取 hreflang 之前就超时了。自动化能在流量开始下降之前捕获这些问题。

流量蚕食和寻找冲突的 URL

第二个多语言问题是内部竞争。当搜索引擎对相关性产生困惑,并开始针对来自德国的查询对英文版的页面进行排名时,就会发生蚕食现象,即使您有一个专门的德语着陆页。

在标准的 GSC 界面中很难大规模分析此类重叠。如果原始数据存储在 BigQuery 中,则可以使用 SQL 自动检测潜在的蚕食。

蚕食检测算法如下:

  1. 按两个参数对数据进行分组:querycountry

  2. 计算此组合获得展示的唯一 URL 数量 (COUNT(DISTINCT page))。

  3. 使用 HAVING count > 1 过滤结果。

如果多个页面针对相同的 query + country 组合获得展示,这就是一个潜在相关性冲突的信号。

可以通过检查相关区域的实际搜索结果来补充 GSC 数据。当分析已经确定了异常情况,但仅凭数字无法弄清用户具体看到了什么以及 Google 在特定国家/地区显示的是哪个版本的页面时,这尤其有用。

我们已在另一篇文章中介绍了自动收集 SERP 的方法。而当您需要在不同的区域环境中手动检查单个查询和本地化时,您可以使用反检测浏览器,例如 Octo Browser。

Octo Browser 如何补充 SEO 分析

GSC 和 BigQuery 非常适合在海量数据中寻找问题。它们可以帮助您注意到,例如,德国页面的展示量开始下降、针对特定国家的查询排在错误的本地化版本中,或者几个 URL 正在相互竞争。

但在发现此类问题后,通常会出现一个问题:用户在搜索结果中实际看到了什么?

GSC 数据有助于识别异常情况并了解其规模。要诊断具体案例,查看所需地区的搜索结果并检查网站的实际行为是很有用的。

这就是 Octo Browser 派上用场的地方。对于不同的国家,您可以创建独立的配置文件,连接具有所需地理位置的代理,并在隔离的浏览器会话中执行检查。当您经常需要处理多个地理位置,并且不想在检查之间混淆 cookie、历史记录和其他数据时,这非常方便。

例如,我们的脚本检测到英文 URL 正在接收来自德国的查询的展示,即使该网站在 /de/ 处有一个完整的德语版本。这并不一定意味着问题肯定与本地化有关。首先,值得检查一下实际搜索结果中发生了什么。

使用德国代理打开一个 Octo 配置文件,并检查 Google 针对目标查询显示的是哪个 URL。同时,您可以检查是否打开了正确的网站版本、是否存在指向另一个区域设置的自动重定向,以及页面内容是否与所选地区匹配。

您可以使用相同的方法有选择地检查:

  • 在 SERP 中出现错误的语言版本;

  • 多个地区搜索结果的差异;

  • 区域重定向的运作情况;

  • 内容、价格和其他页面元素的本地化;

  • 在修复 hreflang、canonical 或内部重定向链后的变化。

通过这种方式,GSC API 和 BigQuery 帮助识别整个项目中的可疑案例,而 Octo Browser 帮助在隔离环境中使用所需的地理位置调查个别异常情况。

结论

将 SEO 分析从网页界面转移到自定义的数据收集和存储系统,是对整个项目管理水平的一次全新升级。

在开始时,这种架构对技术要求很高:您需要配置授权、错误处理、数据规范化、定期导出以及流程管理。但最终,您会获得一个可以随项目扩展且不受网页界面限制的系统。

您将拥有一个包含历史记录的单一数据库,可以在其中分析所有语言版本、正确重新计算指标、识别技术问题,并构建您需要的数据细分,而不会丢失粒度,从而免去了手动收集报告的麻烦。

使用Octo Browser维护您的在线匿名性。您真实的数字指纹无法被追踪。

想以优惠价试用 Octo 浏览器吗?
使用优惠码 OCTOBLOG,即可享受任意订阅 30% 折扣。此优惠仅限新用户使用。

为什么要告别网页界面

一个可靠的 SEO 分析系统始于消除手动导出。Google Search Console 界面便于快速检查某些指标,但其功能不足以进行深入的产品分析。

GSC API 使获取更细粒度的数据成为可能——通过单个 URL、搜索查询和其他维度。这使您不仅可以处理聚合报告,还可以处理底层数据,并从中构建您自己的细分和指标。

为了理解这种架构的优势,考虑几个关键因素至关重要。

数据基数和网页界面局限性

GSC 界面的主要问题之一是它显示的数据量有限。这对于多语言项目尤为关键:当存在大量的页面、国家/地区、设备和查询时,很大一部分信息会留在标准报告之外。

这就是基数的重要性所在——数据集中唯一参数组合的数量。例如,如果一个网站以 10 种语言运行,接收来自 50 个国家/地区的流量,使用 3 种类型的设备,并对 10,000 个搜索查询进行排名,则可能的组合数量可达数百万行。

通过网页界面处理如此庞大的数据量几乎是不可能的。使用 API 可以检索多得多的数据,并将导出拆分为单独的细分。通过过滤和顺序处理,您不仅可以收集结果的头部部分,还可以收集通常在标准报告中丢失的长尾查询和 URL。

数据湖与历史存储

Google Search Console 在其界面中仅存储过去 16 个月的历史数据,这对于长期 SEO 分析来说是远远不够的。

专门的数据仓库(例如 BigQuery)可以解决这个问题。您可以定期将通过 API 获取的原始数据保存到其中,而不受 GSC 界面保留期的限制。这为您提供了一个历史 SEO 数据库,可用于长期分析、报告构建以及在任何必要维度中重新处理数据。

对外部服务的依赖以及扩展成本

现成的 ETL 连接器可用于自动执行数据导出,例如 Supermetrics 或 Fivetran 等 SaaS 服务。它们允许您快速设置数据传输,而无需内部开发,但随着项目规模的扩大,这种方法可能会变得昂贵,并将您的基础设施与特定服务过于紧密地绑定在一起。

SaaS 平台根据处理的行数或连接器数量对其服务收费。当您的多语言项目开始每天产生数千兆字节的原始 SEO 数据时,此类服务的成本可能会数倍于在 BigQuery 中存储数据和为 Python 脚本租用小型服务器的成本。

此外,一个服务融入数据收集和转换过程的程度越深,日后替换它就越困难:迁移可能需要重新配置连接器、处理逻辑和报告。

数据粒度级别

在通过 API 收集数据时,尽可能多地保留细节至关重要。如果您在导出阶段就已经合并了数据,则稍后将无法重构原始维度。

例如,在设计 SEO 数据库时,您应该将 Device 类型和 Country 等参数分开存储。如果脚本在请求数据时未按国家/地区细分,GSC 将返回该查询的总点击次数。此后,将无法确定有多少点击来自德国,有多少来自法国。

这就是为什么最好以详细的形式存储数据,并且仅在分析或可视化阶段合并数据并计算最终指标。这保留了在未来构建任何必要数据细分的能力,即使这些细分最初在报告中并未预料到。

导出大量数据时的 GSC API 局限性

乍一看,使用 GSC API 似乎很简单:授权脚本、检索数据、将其保存到数据库,然后继续进行分析。在实践中,大型项目很快就会遇到 API 的技术限制。

如果您在不考虑这些限制的情况下尝试导出大量数据,您可能会遇到超时、429 Too Many Requests 错误和不完整的导出。结果,只有部分数据会到达数据仓库,系统本身也会变得不稳定。

这就是为什么数据管道应该提前考虑 API 限制、数据更新延迟、查询配额以及针对失败请求的重试机制。让我们来看看主要的 GSC API 限制以及如何正确处理它们。

50,000 行限制和导出细分

根据 Google 的文档rowLimit 参数允许您在单次请求中检索不超过 25,000 行。startRow 参数用于分页:您可以最初请求前 25,000 行,然后再请求接下来的 25,000 行。

然而,还有一个附加限制startRow + rowLimit 的总和不能超过 50,000。因此,对于单个日期和所选的参数组合,您无法通过这种方式检索第 50,001 行。

如果您的每日数据基数超过 50,000 行,您需要使用维度过滤器(Dimension Filters)将导出拆分为单独的细分。例如,您可以单独查询语言文件夹(例如 /de//fr/ 等)的数据,或者另外使用正则表达式按模式划分 URL。

这使您能够通过针对不同数据细分的几个独立请求来获取完整的数据集。

数据延迟和处理 dataState

GSC API 存在 48 至 72 小时的系统性数据更新延迟。因此,在每日导出数据时,区分初步数据和最终数据至关重要。

dataState 参数对此进行控制。它有两个值:

  • "final"(默认)——仅返回完全聚合且验证过的数据。

  • "all"——包含尚未经过最终处理的新数据。

这在构建数据管道时在速度和准确性之间创造了权衡。让我们来看看这两种情况。

  • 使用 dataState: "final"(默认行为)。在此模式下,过去 24 小时的数据可能尚不可用。如果您的脚本尝试使用默认值导出 yesterday 的数据,API 将返回一个空数组。您需要应用 current_date — 3 days 的固定偏移量。

  • 使用 dataState: "all"。您将收到前 24 小时所需的数据集。然而,Google 警告称,新数据是初步的。系统尚未整合所有重复项、过滤掉垃圾机器人或重新计算异常值。2-3 天后,这些数字在 Google 自己的服务器上会发生变化。

这会如何影响您的存储架构:如果您的 Python 脚本只是简单地将新的原始数据附加(Append)到 BigQuery,您的历史数据库将会失真。当您写入“新鲜”指标时,您存储的是一个草稿,它永远不会与 GSC 界面中的最终报告完全一致。

因此,对于运营分析,最好使用两阶段法:

  1. 使用 dataState: "all" 导出前一天的数据。

  2. 同时,脚本应使用 dataState: "final" 重新导出 current_date — 4 days 的数据。

  3. 在 BigQuery 中,使用 MERGE 运算符(或 Upsert 逻辑)代替简单的 Append。脚本应在数据库中找到四天前的初步数据,并用最终的、整合后的值覆写它。

这种方法使您能够在仪表板上看到最新指标,同时在数据完成最终处理后保留正确的历史数据库。

API 限制和指数退避

GSC API 限制请求数量以保护其基础设施免受过大负载的影响。GSC API 有严格的配额:每个项目每秒 50 次查询 (QPS) 和每分钟 1,200 次查询 (QPM)。因此,在进行大规模导出时,需要提前考虑这些限制。

当为了解决 50,000 行限制而必须将数据拆分为数百个细分时,该问题尤为明显。为了加快这一过程,开发人员经常使用异步请求 (asyncio) 或线程池 (ThreadPoolExecutor)。但这会很快耗尽 50 QPS 的限制,API 开始返回 429 Too Many Requests503 Service Unavailable 错误。

使用 time.sleep() 进行简单延迟在这里效果并不理想。如果几个并行线程同时收到错误,然后休眠相同的时间,它们将几乎同时恢复运行并创造另一个流量峰值。

正确的脚本架构应该包含一个带有添加随机噪音(Jitter)的指数退避(exponential backoff)模式。这会导致重试请求之间的间隔逐渐增加。并非所有线程都会在同一时间重试,从而降低它们超出限制的几率。

在 Python 中,您不一定需要手动实现此逻辑:您可以使用 tenacity 库中的装饰器。

收集子域名和语言文件夹的 SEO 数据

在将 GSC API 限制和延迟考虑在内之后,下一个重要问题是多语言网站的具体结构是怎样的。项目结构直接影响导出、规范化和合并数据的逻辑。

在多语言 SEO 中,有两种截然相反的网站结构方法:国家子域名和语言文件夹。对于用户来说,差异极小,但对于构建导出,不同的结构会彻底改变方法。

子域名和独立域名

如果语言版本托管在独立的域名(site.desite.fr)或子域名(de.site.comfr.site.com)上,则必须将每个版本的数据作为来自独立资源的数据进行收集。

为什么这有利于业务? 区域隔离使您能够更严格地控制抓取预算。搜索引擎不会将德国机器人的抓取预算花在扫描法国版本的网站上。从 SEO 的角度来看,这是扩展最安全的路线。

对于分析管道,这会产生两个问题:

  • 更多的连接点。 如果项目有 10 个语言版本,脚本需要顺次或并行查询多个 GSC 资源。数据源越多,API 配额、错误处理以及整个导出架构的弹性就越重要。

  • URL 规范化复杂性(拼接数据)。 在不同域名上具有相同用途的页面将具有不同的地址——例如,site.de/productsite.fr/product。为了将它们的表现作为一个整体进行比较,需要对它们的 URL 进行规范化。

在 Python 的 Pandas 库中,这种规范化如下所示:

import pandas as pd
from urllib.parse import urlparse

# Keep only the path for stitching metrics across countries
df['normalized_url'] = df['page'].apply(lambda x: urlparse(x).path) 
# Result: /product

如果不进行这种规范化,不同本地化的指标仍将分散在不同的 URL 中。这将使计算相同页面或模板在不同语言中的整体表现变得更加困难。

语言文件夹和数据细分

如果语言版本放在文件夹中,例如 site.com/de/site.com/fr/,则整个项目都保持在单个域名内。这简化了数据收集:您无需向多个资源发出单独的请求,而是可以使用 Google Search Console 中的单个网域属性 (Domain Property)

无需进行 10 次独立的查询,您可以进行一次大型导出,使用过滤来解决 50,000 行的限制。这节省了 Google API 配额并减轻了网络负载。

大型导出仍需要划分为细分。但架构本身变得更简单:连接点更少、请求更少,且 API 负载更低。

因为 API 给我们提供的是连续的 URL 流,所以脚本必须自己给行分配国家标记。这可以通过正则表达式 (RegEx) 来完成。

使用 Pandas 库,我们可以直接从 URL 中提取语言标记:

df['language_market'] = df['page'].str.extract(r'\.com/([a-z]{2})/')
# Exception handling: if RegEx returns NaN, this is the main version of the site
df['language_market'].fillna('en', inplace=True)

这使您能够从 site.com/de/product 之类的 URL 中提取 de 标记,并将其用于进一步分析。

这种方法的主要限制是它对 URL 结构的依赖。正则表达式必须与用于构建语言版本的规则完全匹配。如果某些页面使用不同的模式,例如 site.com/category-de/product,则这些 URL 可能会被错误分类,或者可能根本无法进入所需的细分。

这就是为什么在配置正则表达式之前,检查所有可能的语言-URL 模式并单独处理异常至关重要。

Pandas 中的数据转换和指标计算

在收集并规范化数据后,需要将数据合并并准备进行分析。Pandas 非常适合处理此项任务:该库使处理大型表格、合并数据源以及在行级和组级计算指标成为可能。

通过 URL 合并 GSC 和 GA4 数据

Google Search Console 显示搜索指标——展示次数、点击次数、CTR 和排名。GA4 用行为和商业指标(如会话数和转化次数)对其进行补充。

为了获得更全面的 SEO 流量表现,可以使用通用键——规范化的着陆页 URL,来合并 GSC 和 GA4 数据。

在 Pandas 中,这是通过使用通用键(规范化的 URL)联接表格来完成的:

import pandas as pd

# df_gsc export from Search Console
# df_ga4 export from GA4 (Sessions, Conversions)

# Join the data by landing page (Left Join so that pages without traffic are not lost)
merged_df = pd.merge(df_gsc, df_ga4, how='left', left_on='landing_page', right_on='page_path')

# We can now calculate the conversion rate of a specific SEO cluster:
merged_df['seo_conversion_rate'] = (merged_df['conversions'] / merged_df['clicks']) * 100

正确计算 CTR 和平均排名

在合并来自多个语言版本的数据时,您不能简单地通过算术平均值来计算 CTR 和平均排名。这会使结果失真,因为它没有考虑不同的展示量。

例如:

  • 法国子域名:4 次展示获得 2 次点击,CTR = 50%;

  • 德国子域名:1,000 次展示获得 20 次点击,CTR = 2%。

如果您只是简单地求 CTR 的平均值,您会得到:

(50% + 2%) / 2 = 26%

但在这两个子域名上的实际 CTR 是:

22 次点击 / 1004 次展示 = 2.19%

因此,在聚合来自不同本地化的数据时,需要从基础数据重新计算指标:

  • CTR 计算为总点击次数与总展示次数的比率。

  • 平均排名 应该按展示量进行加权:每行的排名乘以展示次数,然后将这些值的总和除以总展示次数。

在 Pandas 中,这可以实现如下:

# Group the data by search query across all countries
def weighted_metrics(x):
    # Weighted average position = Sum (Position * Impressions) / Sum (Impressions)
    weighted_pos = (x['position'] * x['impressions']).sum() / x['impressions'].sum()
    # Actual CTR
    real_ctr = (x['clicks'].sum() / x['impressions'].sum()) * 100
    
    return pd.Series({
        'total_clicks': x['clicks'].sum(),
        'total_impressions': x['impressions'].sum(),
        'weighted_position': weighted_pos,
        'real_ctr': real_ctr
    })

# Apply the function to the grouped dataframe
final_cluster_data = merged_df.groupby('query').apply(weighted_metrics)

自动化 hreflang 和蚕食监测

专门的数据仓库不仅可以用于报告,还可以用于自动检测技术性 SEO 问题。对于多语言项目,有两种类型的问题尤为重要:损坏的本地化关系以及多个页面对同一搜索需求的内部竞争。

自动化 hreflang 监测

国际项目的技术性 SEO 优化取决于本地化标签的一致性和双向性。hreflang 标签作为一个严格的双向交叉引用系统运行。例如,如果法国页面指向一个德国页面作为备用,则该德国页面必须包含一个互惠链接。破坏这个链条就会破坏 Google 眼中的整个集群。

对于大型项目,手动检查这些链接是不可能的,因此监测应基于至少两个数据源的对比:

  • Screaming Frog。 按计划运行爬虫。它会扫描 HTML 代码中标签的实际存在情况,并验证语言代码 (ISO 639-1) 和国家代码 (ISO 3166-1 Alpha 2)。结果会导出到数据库中。

  • GSC API。 Search Console API 提供索引报告,并展示 Google 在最近一次抓取过程中如何解释这些关系。

抓取结果和 Google 数据可以存储在同一个数据库中,然后使用 SQL 进行匹配。例如,FULL OUTER JOIN 可以在聚合数据中识别出关键错误:有些页面在代码中技术上具有正确的标签,但却被搜索引擎忽略了。

造成这种情况的原因可能有很多。一种可能是客户端渲染:JavaScript 框架(React、Vue)需要很长时间才能在 <head> 中渲染标签,或者性能指标 (Core Web Vitals) 太差,导致 Googlebot 在读取 hreflang 之前就超时了。自动化能在流量开始下降之前捕获这些问题。

流量蚕食和寻找冲突的 URL

第二个多语言问题是内部竞争。当搜索引擎对相关性产生困惑,并开始针对来自德国的查询对英文版的页面进行排名时,就会发生蚕食现象,即使您有一个专门的德语着陆页。

在标准的 GSC 界面中很难大规模分析此类重叠。如果原始数据存储在 BigQuery 中,则可以使用 SQL 自动检测潜在的蚕食。

蚕食检测算法如下:

  1. 按两个参数对数据进行分组:querycountry

  2. 计算此组合获得展示的唯一 URL 数量 (COUNT(DISTINCT page))。

  3. 使用 HAVING count > 1 过滤结果。

如果多个页面针对相同的 query + country 组合获得展示,这就是一个潜在相关性冲突的信号。

可以通过检查相关区域的实际搜索结果来补充 GSC 数据。当分析已经确定了异常情况,但仅凭数字无法弄清用户具体看到了什么以及 Google 在特定国家/地区显示的是哪个版本的页面时,这尤其有用。

我们已在另一篇文章中介绍了自动收集 SERP 的方法。而当您需要在不同的区域环境中手动检查单个查询和本地化时,您可以使用反检测浏览器,例如 Octo Browser。

Octo Browser 如何补充 SEO 分析

GSC 和 BigQuery 非常适合在海量数据中寻找问题。它们可以帮助您注意到,例如,德国页面的展示量开始下降、针对特定国家的查询排在错误的本地化版本中,或者几个 URL 正在相互竞争。

但在发现此类问题后,通常会出现一个问题:用户在搜索结果中实际看到了什么?

GSC 数据有助于识别异常情况并了解其规模。要诊断具体案例,查看所需地区的搜索结果并检查网站的实际行为是很有用的。

这就是 Octo Browser 派上用场的地方。对于不同的国家,您可以创建独立的配置文件,连接具有所需地理位置的代理,并在隔离的浏览器会话中执行检查。当您经常需要处理多个地理位置,并且不想在检查之间混淆 cookie、历史记录和其他数据时,这非常方便。

例如,我们的脚本检测到英文 URL 正在接收来自德国的查询的展示,即使该网站在 /de/ 处有一个完整的德语版本。这并不一定意味着问题肯定与本地化有关。首先,值得检查一下实际搜索结果中发生了什么。

使用德国代理打开一个 Octo 配置文件,并检查 Google 针对目标查询显示的是哪个 URL。同时,您可以检查是否打开了正确的网站版本、是否存在指向另一个区域设置的自动重定向,以及页面内容是否与所选地区匹配。

您可以使用相同的方法有选择地检查:

  • 在 SERP 中出现错误的语言版本;

  • 多个地区搜索结果的差异;

  • 区域重定向的运作情况;

  • 内容、价格和其他页面元素的本地化;

  • 在修复 hreflang、canonical 或内部重定向链后的变化。

通过这种方式,GSC API 和 BigQuery 帮助识别整个项目中的可疑案例,而 Octo Browser 帮助在隔离环境中使用所需的地理位置调查个别异常情况。

结论

将 SEO 分析从网页界面转移到自定义的数据收集和存储系统,是对整个项目管理水平的一次全新升级。

在开始时,这种架构对技术要求很高:您需要配置授权、错误处理、数据规范化、定期导出以及流程管理。但最终,您会获得一个可以随项目扩展且不受网页界面限制的系统。

您将拥有一个包含历史记录的单一数据库,可以在其中分析所有语言版本、正确重新计算指标、识别技术问题,并构建您需要的数据细分,而不会丢失粒度,从而免去了手动收集报告的麻烦。

随时获取最新的Octo Browser新闻

通过点击按钮,您同意我们的 隐私政策

随时获取最新的Octo Browser新闻

通过点击按钮,您同意我们的 隐私政策

随时获取最新的Octo Browser新闻

通过点击按钮,您同意我们的 隐私政策

立即加入Octo Browser

或者随时联系客户服务,如果您有任何问题。

立即加入Octo Browser

或者随时联系客户服务,如果您有任何问题。

立即加入Octo Browser

或者随时联系客户服务,如果您有任何问题。

©

2026年

Octo Browser

©

2026年

Octo Browser

©

2026年

Octo Browser