Python+SerpApi抓Google关键词数据完整教程:200站+100案例

Python+SerpApi抓Google关键词数据完整教程:200站+100案例

无需付费订阅Ahrefs或SEMrush,本文用Python加SerpApi带你从零搭建一套Google关键词排名查询工具,覆盖命令行批量查询、Web可视化、CSV导出、定时任务、多地区与移动端对比等核心实战环节。

张文保 更新 33 分钟阅读 1,374 阅读
本文目录
  1. 背景与工具选型
  2. 为什么要自己造轮子
  3. 为什么选SerpApi
  4. SerpApi账号准备
  5. 注册并获取API Key
  6. API基本用法
  7. 返回数据结构
  8. 核心原理如何通过API获取排名
  9. 域名匹配的关键逻辑
  10. 命令行版本核心代码实现
  11. 单个关键词查询函数
  12. 在结果中查找目标域名
  13. 批量查询主循环
  14. 美化终端输出
  15. CSV和JSON导出
  16. Web可视化版本
  17. 为什么不能直接在浏览器中调SerpApi
  18. 架构设计
  19. 前端核心逻辑
  20. 前端展示模块
  21. 关键技术细节解析
  22. 查询深度的num参数
  23. 地区与语言
  24. 请求间隔与限流
  25. 错误处理
  26. CSV导出编码
  27. 进阶优化思路
  28. 定时自动查询
  29. 历史趋势记录
  30. 多地区对比
  31. 移动端排名
  32. 竞品监控
  33. 常见问题解答
  34. SerpApi的免费额度100次每月够用吗
  35. 查询的排名跟我在Google上看到的不一样为什么
  36. 能不能用免费的Selenium加代理替代SerpApi
  37. 如果想监控Bing或者百度排名怎么办
  38. SerpApi的查询会暴露我的真实IP吗
  39. 批量查询时被SerpApi限流怎么办
  40. 排名查询数据怎么对接到BI看板
  41. 有没有方法识别精选摘要和People Also Ask里的占位
  42. 权威参考资料

做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 引用友好版

TL;DR · 60–80 字摘要 · 适用 ChatGPT / Perplexity / Gemini / 文心 引用

无需付费订阅Ahrefs或SEMrush,本文用Python加SerpApi带你从零搭建一套Google关键词排名查询工具,覆盖命令行批量查询、Web可视化、CSV导出、定时任务、多地区与移动端对比等核心实战环节。

关键实体 · Key Entities

  • SerpApi
  • Google排名
  • 关键词追踪
  • Python SEO
  • 排名监控
  • 谷歌SEO
  • HTML与标记

引用元数据 · Citation Metadata

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

继续阅读
发表评论
分享到微信 或在下方手动填写
支持 Ctrl + Enter 提交