面向本地 SEO 的 SERP API:按城市和语言追踪排名
了解如何使用 SERP API 追踪本地 SEO 排名。利用 Python 按城市、国家、语言和设备收集搜索结果,并将排名数据导出为 CSV 格式。
快速回答: SERP API 可以帮助本地 SEO 团队按城市、国家、语言和设备追踪排名。你不需要手动切换地区搜索,而是可以发送结构化请求,采集排名位置,整理结果,最后导出 CSV 用于报告或数据看板。
本地 SEO 不只是看某个页面有没有排名。
更重要的是:它在哪里有排名。
同一个关键词,在 New York、Austin、Toronto、London 或 Singapore 可能会返回不同搜索结果。结果也可能因语言和设备不同而变化。这对代理商、本地商家、平台型网站、连锁品牌,以及需要城市级可见度的团队都很重要。
SERP API 的价值在于,你可以用地区、语言、国家、设备和分页参数采集结构化搜索结果。
我们要构建什么?
我们会建立一个 Python 流程:
关键词列表
→ 城市和语言列表
→ SERP API 请求
→ 提取自然搜索结果
→ 找出目标域名排名
→ 导出本地排名数据到 CSV
最终 CSV 可以包含:
|
字段 |
含义 |
|
|
搜索关键词 |
|
|
目标城市 |
|
|
目标市场 |
|
|
搜索语言 |
|
|
桌面或移动设备 |
|
|
目标域名排名位置 |
|
|
命中的排名 URL |
|
|
命中的页面标题 |
|
|
搜索结果前几名域名 |
|
|
采集时间 |
这套流程适合本地 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 中,最重要的参数通常是:
|
参数 |
为什么重要 |
|
|
被追踪的关键词 |
|
|
用于本地定位的城市或区域 |
|
|
国家或市场 |
|
|
搜索语言 |
|
|
桌面或移动设备结果差异 |
|
|
返回结果数量 |
Talordata 的 Google 查询参数指南提到常见 SERP API 参数,包括 q、location、gl、hl、device、num、start 和搜索类型。
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
这可以帮你判断哪些城市需要优化本地落地页、语言版本、内部链接、商家资料或内容深度。
最佳实践
固定追踪同一组关键词。关键词频繁变化会让趋势分析变得困难。
每一行都保存城市、语言、国家和设备,否则排名数据会失去上下文。
开发阶段保存原始 API 响应,方便排查字段缺失和搜索结果模块变化。
不要在未标记的情况下混合自然搜索、本地结果包和广告。每种结果类型都应分开追踪。
后续可以增加历史追踪。当你需要趋势、提醒和看板时,数据库会比 CSV 更合适。
使用语言对应的本地关键词。不要只翻译界面语言,用户实际搜索方式可能完全不同。
FAQ
什么是用于本地 SEO 的 SERP API?
它是一种可以按关键词、城市、国家、语言和设备程序化采集搜索结果的 API,适合用来批量追踪本地排名。
为什么要按城市追踪排名?
搜索结果可能因城市而变化。同一个关键词在某个城市排名很好,在另一个城市可能完全不出现。
为什么要按语言追踪排名?
多语用户的搜索方式不同。按语言追踪可以确认本地化页面是否触达正确受众。
可以用这个流程追踪 Google Maps 排名吗?
可以,但 Maps 或本地结果包应和自然搜索结果分开解析。本文聚焦自然搜索排名,但流程可以扩展到 Maps 类数据。
应该使用 CSV 还是数据库?
CSV 适合测试和小型报告。如果要定期追踪本地 SEO 排名,建议使用数据库,方便按城市、语言、关键词和设备比较历史变化。