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

了解如何使用 SERP API 追蹤本地 SEO 排名。利用 Python 按城市、國家、語言和裝置收集搜尋結果,並將排名資料匯出為 CSV 格式。

talor ai
Last updated on
6 min read

快速回答: 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 排名,建議使用資料庫,方便按城市、語言、關鍵字和裝置比較歷史變化。

Scale Your Data
Operations Today.

Join the world's most robust proxy network.

Start Free Trial