面向本地 SEO 的 SERP API:按城市和语言追踪排名

了解如何使用 SERP API 追踪本地 SEO 排名。利用 Python 按城市、国家、语言和设备收集搜索结果,并将排名数据导出为 CSV 格式。

面向本地 SEO 的 SERP API:按城市和语言追踪排名
Marcus Bennett
最后更新于
6 分钟阅读

快速回答: SERP API 可以帮助本地 SEO 团队按城市、国家、语言和设备追踪排名。你不需要手动切换地区搜索,而是可以发送结构化请求,采集排名位置,整理结果,最后导出 CSV 用于报告或数据看板。

本地 SEO 不只是看某个页面有没有排名。

更重要的是:它在哪里有排名

同一个关键词,在 New York、Austin、Toronto、London 或 Singapore 可能会返回不同搜索结果。结果也可能因语言和设备不同而变化。这对代理商、本地商家、平台型网站、连锁品牌,以及需要城市级可见度的团队都很重要。

SERP API 的价值在于,你可以用地区、语言、国家、设备和分页参数采集结构化搜索结果。

我们要构建什么?

我们会建立一个 Python 流程:

关键词列表
→ 城市和语言列表
→ SERP API 请求
→ 提取自然搜索结果
→ 找出目标域名排名
→ 导出本地排名数据到 CSV

最终 CSV 可以包含:

字段

含义

keyword

搜索关键词

city

目标城市

country

目标市场

language

搜索语言

device

桌面或移动设备

position

目标域名排名位置

matched_url

命中的排名 URL

matched_title

命中的页面标题

top_domains

搜索结果前几名域名

collected_at

采集时间

这套流程适合本地 SEO 报告、城市级排名检查、多语 SEO 追踪、连锁品牌 SEO、竞品监控和 AI 辅助 SEO 看板。

为什么本地 SEO 需要按城市和语言追踪?

单一全国排名报告可能会掩盖本地差异。

例如:

Keyword: emergency plumber
City: Dallas
Language: English

和下面这组条件可能返回不同结果:

Keyword: emergency plumber
City: Houston
Language: English

加入语言后,差异可能更明显:

Keyword: dentist near me
City: Miami
Language: English

Keyword: dentista cerca de mí
City: Miami
Language: Spanish

对严肃的本地 SEO 追踪来说,城市、语言、国家和设备都应该被保存到每一条排名数据中。

自然搜索结果 vs 本地结果包

本地 SEO 报告通常不只需要一种排名类型。

结果类型

可以说明什么

自然搜索结果

哪些页面出现在标准搜索结果中

本地结果包

哪些商家出现在地图型本地结果中

Maps 结果

商家在 Google Maps 类搜索中的呈现方式

PAA 问题

本地用户常问哪些问题

广告

哪些竞品正在付费获取可见度

本文重点是提取自然搜索排名,但如果 SERP API 响应中包含本地结果包或 Maps 模块,也可以沿用同样结构进行扩展。

Step 1:设置环境变量

不要把 API 密钥直接写进代码。

export TALORDATA_API_KEY="your_api_key_here"
export TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"

Windows PowerShell:

$env:TALORDATA_API_KEY="your_api_key_here"
$env:TALORDATA_SERP_ENDPOINT="your_talordata_serp_endpoint_here"

安装 Python 依赖:

pip install requests

Talordata 查询参数文档提供 SERP API 请求参数和搜索流程说明。

Step 2:定义关键词、城市和语言

先从一组小型测试数据开始。

KEYWORDS = [
    "emergency plumber",
    "dentist near me",
    "coffee shop",
]

LOCATIONS = [
    {
        "city": "Austin",
        "country": "us",
        "location": "Austin, Texas, United States",
        "language": "en",
    },
    {
        "city": "Miami",
        "country": "us",
        "location": "Miami, Florida, United States",
        "language": "en",
    },
    {
        "city": "Miami",
        "country": "us",
        "location": "Miami, Florida, United States",
        "language": "es",
    },
]

TARGET_DOMAIN = "example.com"
DEVICE = "desktop"

这样可以比较同一组关键词在不同本地市场和语言下的表现。

Step 3:建立 SERP API 请求辅助函数

把请求层和解析逻辑分开。

import os
import requests


def require_env(name):
    value = os.getenv(name)
    if not value:
        raise RuntimeError(f"Missing required environment variable: {name}")
    return value


TALORDATA_API_KEY = require_env("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = require_env("TALORDATA_SERP_ENDPOINT")


def call_serp_api(payload):
    headers = {
        "Authorization": f"Bearer {TALORDATA_API_KEY}",
        "Content-Type": "application/json",
    }

    response = requests.post(
        TALORDATA_SERP_ENDPOINT,
        json=payload,
        headers=headers,
        timeout=30,
    )

    response.raise_for_status()
    return response.json()

有些 API 使用 GET,也有些会把 API key 放在查询参数中。如果你的设置不同,只需要修改这个辅助函数。

Step 4:获取本地 SERP 结果

接着建立一个发送本地化搜索请求的函数。

def fetch_local_serp(keyword, location, country, language, device="desktop"):
    payload = {
        "engine": "google",
        "q": keyword,
        "location": location,
        "gl": country,
        "hl": language,
        "device": device,
        "num": 10,
        "output": "json",
    }

    return call_serp_api(payload)

本地 SEO 中,最重要的参数通常是:

参数

为什么重要

q

被追踪的关键词

location

用于本地定位的城市或区域

gl

国家或市场

hl

搜索语言

device

桌面或移动设备结果差异

num

返回结果数量

Talordata 的 Google 查询参数指南提到常见 SERP API 参数,包括 qlocationglhldevicenumstart 和搜索类型。

Step 5:提取自然搜索结果

不同 SERP API 可能使用不同字段名称,所以解析器要保持弹性。

from urllib.parse import urlparse


def clean_text(value):
    if not value:
        return ""
    return " ".join(str(value).split())


def get_domain(url):
    if not url:
        return ""

    parsed = urlparse(url)
    return parsed.netloc.replace("www.", "")


def get_organic_results(serp_json):
    return serp_json.get("organic_results") or serp_json.get("organic") or []


def normalize_organic_results(serp_json):
    results = get_organic_results(serp_json)
    rows = []

    for index, item in enumerate(results, start=1):
        url = item.get("link") or item.get("url") or ""

        rows.append({
            "position": item.get("position") or item.get("rank") or index,
            "title": clean_text(item.get("title")),
            "url": url,
            "domain": get_domain(url),
            "snippet": clean_text(item.get("snippet") or item.get("description")),
        })

    return rows

这会为每个城市和语言生成一份干净的排名结果列表。

Step 6:找出目标域名排名

接着检查目标域名是否出现在前几名搜索结果中。

def find_domain_ranking(results, target_domain):
    target = target_domain.replace("www.", "").lower()

    for item in results:
        domain = item["domain"].lower()

        if domain == target or domain.endswith("." + target):
            return {
                "position": item["position"],
                "matched_url": item["url"],
                "matched_title": item["title"],
            }

    return {
        "position": None,
        "matched_url": "",
        "matched_title": "",
    }


def summarize_top_domains(results, limit=10):
    domains = []

    for item in results[:limit]:
        if item["domain"]:
            domains.append(item["domain"])

    return " | ".join(domains)

如果没有找到目标域名,就把排名保留为 None,不要填入假排名。

Step 7:导出本地排名数据到 CSV

import csv
from datetime import datetime, timezone


CSV_COLUMNS = [
    "keyword",
    "city",
    "location",
    "country",
    "language",
    "device",
    "target_domain",
    "position",
    "matched_url",
    "matched_title",
    "top_domains",
    "collected_at",
]


def export_to_csv(rows, filename="local_seo_rankings.csv"):
    if not rows:
        print("No ranking rows found.")
        return

    with open(filename, mode="w", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=CSV_COLUMNS)
        writer.writeheader()
        writer.writerows(rows)

    print(f"Exported {len(rows)} ranking rows to {filename}")

完整 Python 示例

import os
import csv
import requests

from datetime import datetime, timezone
from urllib.parse import urlparse


KEYWORDS = [
    "emergency plumber",
    "dentist near me",
    "coffee shop",
]

LOCATIONS = [
    {
        "city": "Austin",
        "country": "us",
        "location": "Austin, Texas, United States",
        "language": "en",
    },
    {
        "city": "Miami",
        "country": "us",
        "location": "Miami, Florida, United States",
        "language": "en",
    },
    {
        "city": "Miami",
        "country": "us",
        "location": "Miami, Florida, United States",
        "language": "es",
    },
]

TARGET_DOMAIN = "example.com"
DEVICE = "desktop"

CSV_COLUMNS = [
    "keyword",
    "city",
    "location",
    "country",
    "language",
    "device",
    "target_domain",
    "position",
    "matched_url",
    "matched_title",
    "top_domains",
    "collected_at",
]


def require_env(name):
    value = os.getenv(name)
    if not value:
        raise RuntimeError(f"Missing required environment variable: {name}")
    return value


TALORDATA_API_KEY = require_env("TALORDATA_API_KEY")
TALORDATA_SERP_ENDPOINT = require_env("TALORDATA_SERP_ENDPOINT")


def call_serp_api(payload):
    headers = {
        "Authorization": f"Bearer {TALORDATA_API_KEY}",
        "Content-Type": "application/json",
    }

    response = requests.post(
        TALORDATA_SERP_ENDPOINT,
        json=payload,
        headers=headers,
        timeout=30,
    )

    response.raise_for_status()
    return response.json()


def fetch_local_serp(keyword, location, country, language, device="desktop"):
    payload = {
        "engine": "google",
        "q": keyword,
        "location": location,
        "gl": country,
        "hl": language,
        "device": device,
        "num": 10,
        "output": "json",
    }

    return call_serp_api(payload)


def clean_text(value):
    if not value:
        return ""
    return " ".join(str(value).split())


def get_domain(url):
    if not url:
        return ""

    parsed = urlparse(url)
    return parsed.netloc.replace("www.", "")


def get_organic_results(serp_json):
    return serp_json.get("organic_results") or serp_json.get("organic") or []


def normalize_organic_results(serp_json):
    results = get_organic_results(serp_json)
    rows = []

    for index, item in enumerate(results, start=1):
        url = item.get("link") or item.get("url") or ""

        rows.append({
            "position": item.get("position") or item.get("rank") or index,
            "title": clean_text(item.get("title")),
            "url": url,
            "domain": get_domain(url),
            "snippet": clean_text(item.get("snippet") or item.get("description")),
        })

    return rows


def find_domain_ranking(results, target_domain):
    target = target_domain.replace("www.", "").lower()

    for item in results:
        domain = item["domain"].lower()

        if domain == target or domain.endswith("." + target):
            return {
                "position": item["position"],
                "matched_url": item["url"],
                "matched_title": item["title"],
            }

    return {
        "position": None,
        "matched_url": "",
        "matched_title": "",
    }


def summarize_top_domains(results, limit=10):
    domains = []

    for item in results[:limit]:
        if item["domain"]:
            domains.append(item["domain"])

    return " | ".join(domains)


def export_to_csv(rows, filename="local_seo_rankings.csv"):
    if not rows:
        print("No ranking rows found.")
        return

    with open(filename, mode="w", newline="", encoding="utf-8") as file:
        writer = csv.DictWriter(file, fieldnames=CSV_COLUMNS)
        writer.writeheader()
        writer.writerows(rows)

    print(f"Exported {len(rows)} ranking rows to {filename}")


if __name__ == "__main__":
    collected_at = datetime.now(timezone.utc).isoformat()
    output_rows = []

    for keyword in KEYWORDS:
        for loc in LOCATIONS:
            serp_json = fetch_local_serp(
                keyword=keyword,
                location=loc["location"],
                country=loc["country"],
                language=loc["language"],
                device=DEVICE,
            )

            organic_results = normalize_organic_results(serp_json)
            ranking = find_domain_ranking(organic_results, TARGET_DOMAIN)

            output_rows.append({
                "keyword": keyword,
                "city": loc["city"],
                "location": loc["location"],
                "country": loc["country"],
                "language": loc["language"],
                "device": DEVICE,
                "target_domain": TARGET_DOMAIN,
                "position": ranking["position"],
                "matched_url": ranking["matched_url"],
                "matched_title": ranking["matched_title"],
                "top_domains": summarize_top_domains(organic_results),
                "collected_at": collected_at,
            })

    export_to_csv(output_rows)

执行后会得到:

local_seo_rankings.csv

输出示例

keyword,city,location,country,language,device,target_domain,position,matched_url,matched_title,top_domains,collected_at
emergency plumber,Austin,"Austin, Texas, United States",us,en,desktop,example.com,3,https://example.com/austin-plumber,Example Plumbing Austin,domain1.com | domain2.com | example.com,2026-06-22T00:00:00Z
dentist near me,Miami,"Miami, Florida, United States",us,es,desktop,example.com,,,,domain3.com | domain4.com | domain5.com,2026-06-22T00:00:00Z

如何使用输出结果?

本地 SEO 报告可以按以下方式分组:

分组方式

可以看出什么

关键词

哪些词有排名,哪些词没有排名

城市

哪些地区可见度强或弱

语言

多语页面是否有排名

设备

桌面和移动设备是否存在差异

前排域名

哪些竞品最常出现

例如:

Keyword: emergency plumber
Austin: position 3
Dallas: position 8
Houston: not found
Miami Spanish: not found

这可以帮你判断哪些城市需要优化本地落地页、语言版本、内部链接、商家资料或内容深度。

注册并领取1000次API响应>>

最佳实践

固定追踪同一组关键词。关键词频繁变化会让趋势分析变得困难。

每一行都保存城市、语言、国家和设备,否则排名数据会失去上下文。

开发阶段保存原始 API 响应,方便排查字段缺失和搜索结果模块变化。

不要在未标记的情况下混合自然搜索、本地结果包和广告。每种结果类型都应分开追踪。

后续可以增加历史追踪。当你需要趋势、提醒和看板时,数据库会比 CSV 更合适。

使用语言对应的本地关键词。不要只翻译界面语言,用户实际搜索方式可能完全不同。

FAQ

什么是用于本地 SEO 的 SERP API?

它是一种可以按关键词、城市、国家、语言和设备程序化采集搜索结果的 API,适合用来批量追踪本地排名。

为什么要按城市追踪排名?

搜索结果可能因城市而变化。同一个关键词在某个城市排名很好,在另一个城市可能完全不出现。

为什么要按语言追踪排名?

多语用户的搜索方式不同。按语言追踪可以确认本地化页面是否触达正确受众。

可以用这个流程追踪 Google Maps 排名吗?

可以,但 Maps 或本地结果包应和自然搜索结果分开解析。本文聚焦自然搜索排名,但流程可以扩展到 Maps 类数据。

应该使用 CSV 还是数据库?

CSV 适合测试和小型报告。如果要定期追踪本地 SEO 排名,建议使用数据库,方便按城市、语言、关键词和设备比较历史变化。

立即开展您的数据业务

加入全球最强大的代理网络

免费试用