Python+SerpApi抓Google关键词数据完整教程:200站+100案例
无需付费订阅Ahrefs或SEMrush,本文用Python加SerpApi带你从零搭建一套Google关键词排名查询工具,覆盖命令行批量查询、Web可视化、CSV导出、定时任务、多地区与移动端对比等核心实战环节。
本文目录
- 背景与工具选型
- 为什么要自己造轮子
- 为什么选SerpApi
- SerpApi账号准备
- 注册并获取API Key
- API基本用法
- 返回数据结构
- 核心原理如何通过API获取排名
- 域名匹配的关键逻辑
- 命令行版本核心代码实现
- 单个关键词查询函数
- 在结果中查找目标域名
- 批量查询主循环
- 美化终端输出
- CSV和JSON导出
- Web可视化版本
- 为什么不能直接在浏览器中调SerpApi
- 架构设计
- 前端核心逻辑
- 前端展示模块
- 关键技术细节解析
- 查询深度的num参数
- 地区与语言
- 请求间隔与限流
- 错误处理
- CSV导出编码
- 进阶优化思路
- 定时自动查询
- 历史趋势记录
- 多地区对比
- 移动端排名
- 竞品监控
- 常见问题解答
- SerpApi的免费额度100次每月够用吗
- 查询的排名跟我在Google上看到的不一样为什么
- 能不能用免费的Selenium加代理替代SerpApi
- 如果想监控Bing或者百度排名怎么办
- SerpApi的查询会暴露我的真实IP吗
- 批量查询时被SerpApi限流怎么办
- 排名查询数据怎么对接到BI看板
- 有没有方法识别精选摘要和People Also Ask里的占位
- 权威参考资料
做SEO的朋友都知道,掌握自己网站在Google搜索中的关键词排名是日常工作的核心。市面上的排名追踪工具如Ahrefs、SEMrush动辄上百美元一个月,并且批量实时排名查询很难用,而实际上,我们完全可以借助SerpApi提供的搜索结果API,用Python自己动手搭建一个轻量、灵活且免费(有额度)的排名查询工具。本文将从零开始,手把手带你完成一个支持批量关键词查询、实时排名展示、CSV导出的完整工具,并提供命令行和Web可视化两种使用方式。
背景与工具选型
为什么要自己造轮子
商业化的SEO排名追踪工具功能强大,但存在几个痛点。第一是价格高昂,Ahrefs Lite每月99美元,SEMrush Pro每月129.95美元,对个人站长和小团队来说成本不低。第二是灵活性不足,无法按自己的需求定制查询逻辑、输出格式和触发时机。第三是数据封闭,数据被锁在第三方平台,难以与自己的分析流程打通。而SerpApi提供了一种原子级的方案——它直接返回Google搜索的结构化结果(JSON格式),我们拿到数据后可以自由加工处理。
保哥这两年用这套工具帮十几个客户做了关键词监控,每个月的SerpApi花费控制在50美元以内,相比订阅商业工具每月省下80%以上的成本。更重要的是,自建工具可以无缝嵌入自己的SEO流程——比如把排名数据直接推送到飞书机器人、Slack频道或者内部BI看板,这些灵活性是商业工具难以提供的。
为什么选SerpApi
SerpApi的优势主要有四点。结构化数据:直接返回JSON,包含自然搜索结果的标题、链接、排名位置、摘要等字段,无需自己解析HTML。稳定可靠:由SerpApi负责处理Google的反爬机制(代理、验证码等),开发者只需调接口。免费额度:新注册账户每月提供100次免费搜索,足够个人使用或测试。支持丰富参数:可以指定国家或地区、语言、设备类型、返回结果数量等。市面上同类API还有DataForSEO、ValueSerp、ScaleSerp,保哥都对比过——SerpApi文档最完善、JSON结构最干净、SDK最齐全,是入门首选。如果你的需求量超过5万次每月,可以再考虑DataForSEO的批量套餐,单次成本会更低。
SerpApi账号准备
注册并获取API Key
访问serpapi.com并注册账号。注册成功后进入Dashboard,在页面中可以看到你的API Key,复制备用。免费套餐每月提供100次搜索,付费套餐起步50美元每月,提供5000次搜索。注册时建议用工作邮箱而非临时邮箱,避免账号被风控系统标记为可疑。SerpApi支持GitHub OAuth登录,绑定后可以减少账号被异地登录拦截的概率。
API基本用法
SerpApi的调用非常简单,只需向https://serpapi.com/search.json发送一个GET请求,带上参数即可。核心参数包括api_key(你的API Key)、engine(搜索引擎,常用google)、q(搜索关键词)、gl(地理位置国家代码,例如us代表美国)、hl(界面语言,例如en代表英语)、num(返回结果数量,最多100)、device(设备类型,可选desktop或mobile)。
返回数据结构
API返回的JSON中,我们最关心的是organic_results字段,它是一个数组,每个元素代表一条自然搜索结果,包含position(排名位置)、title(标题)、link(链接)、displayed_link(展示链接)、snippet(摘要)。
{
"organic_results": [
{
"position": 1,
"title": "Best CRM Software 2025 - Forbes Advisor",
"link": "https://www.forbes.com/advisor/business/best-crm-software/",
"snippet": "Compare the best CRM software of 2025..."
}
]
}我们的核心逻辑就是遍历organic_results,检查每条结果的link字段是否包含我们的目标域名,如果匹配,则该条目的position就是我们的排名。除此之外的字段如related_questions、related_searches、knowledge_graph在做内容策略分析时也很有价值,可以记录下来作为后续选题参考。
核心原理如何通过API获取排名
整个查询流程可以用三步概括:发送请求到SerpApi(关键词加地区加语言),解析返回遍历organic_results,域名匹配找到目标域名的position。
域名匹配的关键逻辑
域名匹配看似简单,但有几个细节需要处理。下面是核心匹配代码。
from urllib.parse import urlparse
def normalize_domain(domain):
domain = domain.lower().strip()
if domain.startswith(("http://", "https://")):
domain = urlparse(domain).hostname or domain
domain = domain.removeprefix("www.")
return domain.rstrip("/")
def extract_domain(url):
try:
return urlparse(url).hostname.removeprefix("www.").lower()
except Exception:
return ""为什么需要标准化?因为用户输入的域名可能是example.com、www.example.com、https://example.com/等各种形式,而Google返回的链接也可能带或不带www。统一标准化后才能准确匹配。子域名匹配方面,我们还支持子域名识别——如果目标是example.com,那么blog.example.com、docs.example.com也会被识别为匹配结果。实现方式是检查item_domain.endswith点加target。这个细节对大站尤其重要,很多博客站点把内容放在blog子域名下,如果不做子域名识别就会漏算排名。
命令行版本核心代码实现
命令行版本适合批量查询、定时任务和脚本集成。先安装依赖:pip install requests rich。requests负责发送HTTP请求,rich负责在终端中渲染美观的表格、进度条和彩色输出。
单个关键词查询函数
import requests
SERPAPI_URL = "https://serpapi.com/search.json"
def query_keyword(api_key, keyword, num=100):
params = {
"api_key": api_key,
"engine": "google",
"q": keyword,
"gl": "us",
"hl": "en",
"num": num,
"device": "desktop",
}
resp = requests.get(SERPAPI_URL, params=params, timeout=30)
resp.raise_for_status()
return resp.json()在结果中查找目标域名
def find_domain_in_results(data, target_domain):
target = normalize_domain(target_domain)
organic = data.get("organic_results", [])
for item in organic:
link = item.get("link", "")
item_domain = extract_domain(link)
if item_domain == target or item_domain.endswith("." + target):
return {
"position": item.get("position"),
"title": item.get("title", ""),
"link": link,
"snippet": item.get("snippet", ""),
}
return None批量查询主循环
import time
def run_check(api_key, domain, keywords, num=100, delay=1.0):
results = []
for i, kw in enumerate(keywords):
record = {"keyword": kw, "status": "error", "position": None}
try:
data = query_keyword(api_key, kw, num)
match = find_domain_in_results(data, domain)
if match:
record.update({"status": "found", **match})
else:
record["status"] = "notfound"
except Exception as e:
record["error"] = str(e)
results.append(record)
if i < len(keywords) - 1:
time.sleep(delay)
return results每次请求之间加入1秒延迟,虽然SerpApi本身允许较高频率的请求,但加入间隔是一个好习惯,既保护API配额,也降低被限流的风险。如果你跑的关键词列表超过200个,建议在循环里加入断点续传机制——把已经查过的结果落盘,下次启动时先读盘跳过已查关键词,这样中途网络断开也不会丢进度。
美化终端输出
使用rich库渲染彩色表格和实时进度条,可以让命令行结果一目了然。表格包含序号、关键词、排名、状态四列。排名用颜色编码——绿色代表1到3名,青色代表4到10名,黄色代表11到30名,白色代表30名以后。状态用对勾代表找到,叉号代表未收录,ERR代表错误。这种视觉反馈在批量查询100个关键词时特别有用,扫一眼就能看出整体情况。
CSV和JSON导出
import csv
from datetime import datetime
def export_csv(results, domain, filepath=None):
if not filepath:
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
filepath = f"rank_{normalize_domain(domain)}_{ts}.csv"
with open(filepath, "w", newline="", encoding="utf-8-sig") as f:
writer = csv.writer(f)
writer.writerow(["关键词", "排名", "状态", "标题", "链接", "摘要"])
for r in results:
writer.writerow([
r["keyword"],
r.get("position", "-"),
{"found": "已找到", "notfound": "未收录"}.get(r["status"], "出错"),
r.get("title", ""),
r.get("link", ""),
r.get("snippet", ""),
])
return filepath这里使用utf-8-sig编码写入BOM(字节顺序标记),这样用Excel打开CSV文件时中文不会乱码。如果你后续要用Pandas或BI工具读这份CSV,建议同时输出一份不带BOM的版本,避免BOM被解析为列名的一部分。
Web可视化版本
命令行虽好,但视觉体验有限。我们再来做一个Web版本,提供更直观的操作界面。
为什么不能直接在浏览器中调SerpApi
这是一个很常见的坑——SerpApi的接口不支持浏览器直接调用。原因是浏览器的同源策略(CORS)——从你的网页域名向serpapi.com发AJAX请求时,浏览器会检查响应头中是否包含Access-Control-Allow-Origin,而SerpApi并未设置这个头,所以请求会被浏览器拦截。解决方案是在本地搭建一个代理服务器。前端请求发到本地服务器,由服务器转发给SerpApi,再将结果返回给前端。Python的服务端请求不受CORS限制。
架构设计
整个Web版本只有一个Python文件,它同时充当静态文件服务器(返回HTML/CSS/JS页面,内嵌在Python代码中)和API代理(将前端的查询请求转发到SerpApi)。架构上是最小可用主义——不引入Flask或FastAPI等框架,直接用Python标准库的http.server即可,启动后浏览器访问localhost:8765就能看到完整界面。
import http.server
import urllib.request
import urllib.parse
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
if self.path.startswith("/api/search"):
self.handle_search()
else:
self.serve_html()
def handle_search(self):
query_string = self.path.split("?", 1)[1]
params = urllib.parse.parse_qs(query_string)
serpapi_params = {
"api_key": params["api_key"][0],
"engine": "google",
"q": params["q"][0],
"gl": "us",
"hl": "en",
"num": params.get("num", ["100"])[0],
"device": "desktop",
}
url = f"https://serpapi.com/search.json?{urllib.parse.urlencode(serpapi_params)}"
req = urllib.request.Request(url)
with urllib.request.urlopen(req, timeout=30) as resp:
data = resp.read()
self.send_response(200)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(data)前端核心逻辑
前端使用原生JavaScript(零依赖),通过fetch API逐个查询关键词,每完成一个就立即刷新界面,实时反馈进度。这种"边查边显示"的体验比一次性等待所有查询完成再显示要好得多——尤其当关键词列表超过20个时,看到进度条逐步推进比对着空白页等2分钟舒服多了。
async function startQuery() {
const kwList = keywords.split('\n').filter(k => k.trim());
for (let i = 0; i < kwList.length; i++) {
results[i].status = 'loading';
renderResults();
const params = new URLSearchParams({
api_key: apiKey,
q: kwList[i],
num: String(num)
});
const resp = await fetch('/api/search?' + params);
const data = await resp.json();
const match = findDomain(data, domain);
results[i] = match
? { ...results[i], status: 'found', ...match }
: { ...results[i], status: 'notfound' };
renderResults();
await sleep(800);
}
}前端展示模块
页面顶部有5个统计卡片:总查询数(已完成的查询数除以总关键词数)、已收录(找到排名的关键词数及其百分比)、Top 3(排在前3名的关键词数)、Top 10(排在首页的关键词数)、平均排名(所有找到排名的关键词的平均位置)。排名数字使用颜色编码来直观区分:绿色代表1到3名(Top 3),蓝色代表4到10名(首页),橙色代表11到30名,白色代表30名以后。中间是关键词列表,右边是导出按钮和重新查询按钮。整体布局参考了Ahrefs Rank Tracker的UI风格,但只保留了最核心的几个指标,避免界面过于复杂。
关键技术细节解析
查询深度的num参数
num参数控制Google返回的结果数量。设为100意味着查看前100条结果中是否有你的域名。num设为10只查首页,省配额但可能遗漏排在11到100的情况。num设为100覆盖前100名,最全面但消耗的搜索量也是1次(SerpApi按请求次数计费,不按num计费)。建议默认用100,反正都是一次API调用的费用。注意有些细分领域的关键词Google首页可能直接被Featured Snippet和People Also Ask占满,自然结果不到10条,这时候即便num=100也只能拿到Google实际返回的数量。
地区与语言
本工具固定查询Google美国地区(gl=us, hl=en),如果需要查询其他地区,只需修改参数。常用组合:美国对应gl=us hl=en,英国对应gl=uk hl=en,中国(Google)对应gl=cn hl=zh-cn,日本对应gl=jp hl=ja,德国对应gl=de hl=de,法国对应gl=fr hl=fr,新加坡对应gl=sg hl=en,澳大利亚对应gl=au hl=en。如果你做跨境电商面向多个市场,可以把多地区查询封装成一个并行任务,一次跑完所有目标市场的排名。
请求间隔与限流
代码中在每次请求之间加入了间隔(命令行版1秒,Web版0.8秒)。这么做有两个原因。第一是保护API配额,避免短时间内大量请求被SerpApi判定为滥用。第二是稳定性,网络波动时给足缓冲时间。如果你使用付费套餐且需要更快速度,可以将delay调低到0.3到0.5秒。但保哥实测过,间隔低于0.3秒在某些时段会触发SerpApi的临时限流(HTTP 429),所以0.5秒是平衡速度和稳定性的甜区。
错误处理
工具对以下异常做了处理。HTTP错误(401认证失败、429限流、500服务器错误):记录错误信息并继续查询下一个关键词。网络超时:默认30秒超时,超时后标记为出错。中途取消(Web版):使用AbortController实现,点击停止后立即中断当前请求。建议把所有错误的具体信息单独写到一个log文件里,方便事后排查。一次跑100个关键词通常会有1到2个失败,分析失败原因可以发现API稳定性、网络质量等隐性问题。
CSV导出编码
导出CSV时使用utf-8-sig编码(带BOM的UTF-8)。这是因为Excel默认用系统编码打开CSV文件,不带BOM的UTF-8文件中的中文会显示乱码。加上BOM后Excel会自动识别为UTF-8编码。如果你的下游是数据分析工具(Pandas、Tableau、BI),可以同时输出一份不带BOM的版本,避免BOM被解析为列名的一部分。
进阶优化思路
定时自动查询
使用cron(Linux或macOS)或Task Scheduler(Windows)定时执行命令行版本,自动追踪排名变化。例如每天早上8点执行查询并导出CSV,crontab表达式是0 8 * * *。配合邮件或飞书机器人推送,可以在每天上班前看到最新排名报告。如果你的关键词列表很大(500个以上),建议分时段执行,避免一次性把SerpApi配额用完。
历史趋势记录
将每次查询结果追加到数据库(如SQLite)或追加写入CSV,然后用matplotlib或Plotly绘制排名趋势图。SQLite方案的优点是单文件易于备份和迁移,缺点是并发写入有锁限制。如果你的数据量超过100万行,建议迁移到PostgreSQL或MySQL。趋势图最有价值的是看排名在算法更新前后的变化——把Google官方公告的Core Update日期标注在图上,能直观看出哪些关键词受影响。
多地区对比
同一个关键词在不同地区的排名可能差异巨大。可以扩展为同时查询多个地区(us、uk、de等),对比排名差异。对跨境电商和外贸独立站而言,这种对比是优化本地化内容的依据——如果某关键词在德国排名第3但英国排名第30,那德国市场可能有特殊的SEO机会值得挖。
移动端排名
Google的移动端和桌面端搜索结果排名不同。将device参数从desktop改为mobile,即可查询移动端排名。可以同时查询两者进行对比。从全球流量看,移动端搜索占比已超过60%,所以移动端排名往往比桌面端更重要。如果两者排名差异超过5位,要重点检查页面的移动端性能(LCP、CLS、INP三大Core Web Vitals指标)。
竞品监控
不仅查自己的域名,还可以同时查竞争对手的域名。修改代码使其接受多个域名参数,一次查询就能看到你和竞品的排名对比。这种横向对比是制定SEO战略的核心输入——发现竞品在某些关键词上排名远高于你,那对应的内容资产、外链结构、技术SEO是值得研究的方向。
常见问题解答
SerpApi的免费额度100次每月够用吗
对个人开发者测试或者少量关键词的初创站长来说够用,但稍微正式一点的项目就不够了。如果你监控20个关键词每天查一次,一个月就要600次,超出免费额度。建议先用免费额度跑通整个流程,确认工具符合预期之后再升级付费套餐。SerpApi入门付费是50美元每月5000次搜索,按20个关键词每天一次算可以支撑8天,对中小型站点足够。如果你监控的关键词超过100个,建议直接用付费套餐起步50美元的不够,考虑75美元的15000次套餐。
查询的排名跟我在Google上看到的不一样为什么
Google搜索结果会根据用户IP、地理位置、搜索历史、个性化算法实时调整。SerpApi的查询是无登录无历史的纯净环境,跟你自己浏览器看到的排名差异属于正常。要让两者对得上,可以在Chrome用隐身模式(无cookies)加VPN切到美国IP,再去Google搜,得到的结果会接近SerpApi。如果差距特别大,多半是Google对你账号有个性化偏好。
能不能用免费的Selenium加代理替代SerpApi
理论上可以,实际上不推荐。Google的反爬机制非常严格,自己跑Selenium需要管理代理池、处理验证码、模拟人类行为,维护成本远高于直接调SerpApi。我自己以前折腾过半年Selenium方案,最后发现稳定性永远做不到95%以上,还是回到了API方案。如果你团队有专门的爬虫工程师且不缺资源,自建抓取系统当然可以;但对大多数SEO从业者来说,SerpApi是性价比最高的选择。
如果想监控Bing或者百度排名怎么办
SerpApi也支持Bing(engine=bing)和百度(engine=baidu)。把engine参数改一下即可。需要注意Bing和百度的organic_results字段名跟Google一致,但部分字段含义略有差异,例如百度的排名计算不包含sitelink。监控百度的话还有一个本土替代方案是用5118或者爱站的API,覆盖度和成本比SerpApi更适合中文SEO。
SerpApi的查询会暴露我的真实IP吗
不会。查询是由SerpApi的服务器代理发起的,对Google来说请求来源是SerpApi的IP池,跟你的IP无关。但要注意你给SerpApi发送的查询关键词会被记录在SerpApi的日志里,如果你的关键词非常敏感(例如商业机密或者未公开产品名),需要评估是否接受这种日志风险。
批量查询时被SerpApi限流怎么办
正常用法很难被限流,但如果你在循环里把延迟改到0或者用多进程并发请求,会触发HTTP 429。处理方案是捕获429异常后指数退避——第一次等待2秒重试,第二次4秒,第三次8秒。如果连续3次都被限流,停止当前任务,等30分钟后再继续。SerpApi的限流是按账号而非按IP,所以同账号开多机并发也会被限。
排名查询数据怎么对接到BI看板
最简单的方案是把每日查询结果写入Google Sheets,然后用Looker Studio连接Google Sheets做可视化。中等复杂度的方案是写入MySQL或PostgreSQL,再用Metabase或Superset连接做更复杂的报表。如果你的团队已经在用飞书或者钉钉,也可以做成多维表格自动更新。关键是把数据落到一个稳定的存储层,下游可视化工具可以替换。
有没有方法识别精选摘要和People Also Ask里的占位
有。SerpApi返回的JSON里除了organic_results还有answer_box(精选摘要)、related_questions(People Also Ask)、knowledge_graph等字段。可以单独检测这些字段里是否出现你的域名,记录为零位排名(position 0)或者特色摘要排名。这类位置的CTR远高于常规第一名,是值得单独追踪的指标。
权威参考资料
FAQPage + Article AI 引用友好版
无需付费订阅Ahrefs或SEMrush,本文用Python加SerpApi带你从零搭建一套Google关键词排名查询工具,覆盖命令行批量查询、Web可视化、CSV导出、定时任务、多地区与移动端对比等核心实战环节。
- SerpApi
- Google排名
- 关键词追踪
- Python SEO
- 排名监控
- 谷歌SEO
- HTML与标记
title: Python+SerpApi抓Google关键词数据完整教程:200站+100案例 author: 张文保 (Paul Zhang) — PatPat SEO 经理 url: https://zhangwenbao.com/google-rank-checker-python-serpapi-tutorial.html published: 2026-03-10 modified: 2026-05-16 source-type: First-hand expert commentary language: zh-CN license: CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
本文标题:《Python+SerpApi抓Google关键词数据完整教程:200站+100案例》
本文链接:https://zhangwenbao.com/google-rank-checker-python-serpapi-tutorial.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0