用搜索引擎 API 搭一个 SEO 关键词监控工具:定时追踪排名、广告和相关搜索

用搜索引擎 API 搭一个 SEO 关键词监控工具:定时追踪排名、广告和相关搜索

一、为什么要做关键词监控,而不是手动搜一遍?

做 SEO、广告投放、竞品分析和品牌舆情时,很多团队都会定期查看搜索结果。 比如一个跨境电商品牌,可能每天都要看这些问题: - 自己的网站在目标关键词下排第几? - 竞品官网有没有超过自己? - 搜索结果里有没有新的测评文章或比价页面? - 品牌词下面有没有负面内容? - 竞品有没有投放广告结果? - Google / Bing 上的搜索结果有没有发生变化? 如果只是偶尔查一次,人工打开搜索引擎就可以。但如果要持续监控几十个关键词,比如:

wireless earbuds portable monitor brand name review brand name coupon competitor name

人工搜索就很容易出问题。

首先,手动搜索无法保证稳定性。不同地区、不同语言、不同设备、不同浏览器状态下,搜索结果可能不一样。其次,人工记录排名很低效,也不方便做历史趋势对比。今天排第 3,明天排第 5,如果没有结构化记录,很难判断排名变化是偶然波动,还是竞争环境发生了变化。

所以这次我不再只是单次请求搜索结果,而是尝试用搜索引擎 API 搭一个简单的 SEO 关键词监控工具。

目标很明确:

  1. 配置一组关键词;

  2. 每隔 1 小时调用一次搜索引擎 API;

  3. 获取自然搜索结果、相关搜索和广告结果;

  4. 解析目标域名排名;

  5. 保存历史数据;

  6. 输出排名变化趋势图。

整体流程如下:

Plain 复制代码
关键词列表
  ↓
搜索引擎 API 请求
  ↓
解析自然结果 / 广告结果 / 相关搜索
  ↓
保存每小时数据
  ↓
生成排名趋势图
  ↓
用于 SEO、广告、舆情和竞品分析

这类监控工具本身并不复杂,但它解决的核心问题,是把原本零散、依赖人工判断的搜索行为,转化为可重复执行、可持续积累的数据监控流程。只有不断记录同一关键词在相同地区、语言和设备条件下的搜索结果,后续的排名趋势、广告竞争和内容变化分析才有可靠的数据基础。

因此,在正式编写监控脚本之前,首先需要解决两个问题:一是如何把人工输入关键词、选择搜索引擎和设置地区等操作转换为标准化请求;二是如何保证每次请求使用相同的参数条件,减少搜索环境变化对结果的影响。搜索引擎 API 正好提供了这样的能力,它可以把关键词、地区、语言、设备和输出格式统一封装为请求参数,使后续的定时调用和批量监控成为可能。

二、准备 API 请求:认证、关键词和参数构造

搭建关键词监控工具的第一步,是先完成搜索引擎 API 的请求配置。与手动打开搜索页面相比,API 请求不仅可以批量提交关键词,还可以明确指定搜索引擎、地区、语言、设备类型和返回格式,从而让不同时间获得的数据具有更强的可比性。

在此基础上,后续脚本只需要按照设定的时间间隔重复发送请求,再对返回结果进行解析和保存,就能够逐步形成连续的关键词排名历史数据。

立即体验:dataify.com?utm_source=undoom&utm_term=01

这次我使用的请求地址是:

Plain 复制代码
https://scraperapi.dataify.com/request

请求时需要在 Header 中带上 API Token:

Plain 复制代码
Authorization: Bearer YOUR_TOKEN

这里的 YOUR_TOKEN 要替换成自己的 Token。真实 Token 属于敏感信息,不要放到公开截图、文章或者代码仓库里。

一个基础请求可以写成:

Bash 复制代码
curl -X POST 'https://scraperapi.dataify.com/request' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'engine=google' \
  -d 'q=wireless earbuds' \
  -d 'json=1' \
  -d 'google_domain=google.com' \
  -d 'gl=us' \
  -d 'hl=en' \
  -d 'device=desktop'

如果要监控 Bing,也可以把 engine 换成对应的搜索引擎类型。实际使用时,可以根据业务目标配置多个搜索引擎。

这里我可以先定义一组关键词:

Python 复制代码
keywords = [
    "wireless earbuds",
]

运行结果如下:

这样后面就可以判断:每个关键词下,目标网站有没有出现在搜索结果前 10 名,具体排名第几。

三、解析搜索结果:判断 apple.com 排名

搜索引擎 API 返回 JSON 后,重点不是直接看完整结果,而是提取几个核心字段。

这次主要关注三类数据:

Plain 复制代码
自然搜索结果:判断 apple.com 排名
广告结果:观察关键词下的广告竞争情况
相关搜索:发现新的关键词机会

先定义目标域名和关键词列表:

Plain 复制代码
TARGET_DOMAIN = "apple.com"
KEYWORDS = [
    "iphone 16",
    "macbook air",
    "apple watch",
    "airpods pro",
    "apple store"
]

接下来写一个函数,从自然搜索结果中查找 apple.com 出现的位置:

Plain 复制代码
def extract_organic_rank(result, target_domain):
    organic_results = result.get("organic_results", [])

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

        if target_domain in link:
            return {
                "rank": index,
                "title": item.get("title"),
                "link": link,
                "snippet": item.get("snippet")
            }

    return {
        "rank": None,
        "title": None,
        "link": None,
        "snippet": None
    }

这段代码的逻辑很简单:

  1. 读取 organic_results

  2. 从第 1 条结果开始遍历;

  3. 判断链接里是否包含 apple.com

  4. 如果包含,就记录当前排名;

  5. 如果没有出现,就返回空值。

比如搜索 iphone 16 时,如果 apple.com 出现在第 1 条,就会返回:

Plain 复制代码
{
  "rank": 1,
  "title": "iPhone 16 - Apple",
  "link": "https://www.apple.com/iphone-16/",
  "snippet": "Explore iPhone 16 features, design, camera and more..."
}

如果搜索某个关键词时,前几条结果中没有 apple.com,就记录为 None。后续画图时,可以把 None 单独处理,比如表示"未进入监控范围"。

除了自然搜索排名,还可以统计广告结果数量:

Plain 复制代码
def extract_ads_count(result):
    ads = result.get("ads", [])
    return len(ads)

广告结果数量可以反映某个关键词的广告竞争程度。比如 iphone 16 这种高商业价值关键词,广告结果数量可能会更高。

再提取相关搜索:

Plain 复制代码
def extract_related_searches(result):
    related = result.get("related_searches", [])

    queries = []
    for item in related:
        query = item.get("query")
        if query:
            queries.append(query)

    return " | ".join(queries)

相关搜索可以帮助扩展关键词池。比如搜索 iphone 16 后,相关搜索中可能出现:

Plain 复制代码
iphone 16 pro
iphone 16 price
iphone 16 pro max
iphone 16 colors
iphone 16 release date

这些词可以继续加入后续监控列表。

四、Python 实现:每小时监控一次关键词排名

下面把 API 请求、结果解析和 CSV 保存组合成一个完整脚本。

这个脚本会完成以下任务:

  1. 依次请求关键词列表;

  2. 调用搜索引擎 API;

  3. 判断 apple.com 在谷歌上面,自然搜索结果中的排名;

  4. 统计广告结果数量;

  5. 提取相关搜索;

  6. 保存到 CSV;

  7. 每隔 1 小时自动运行一次。

其中,glhl 是两个会直接影响搜索结果的重要参数:

  • gl="us" 表示将搜索结果的目标地区设置为美国;

  • hl="en" 表示将搜索结果的界面语言设置为英文。

这两个参数并不是简单的显示设置。搜索引擎可能会根据地区和语言返回不同的自然搜索结果、广告内容及相关搜索词。例如,同一个关键词在美国地区和其他地区搜索时,出现的网站、页面排序和广告数量都可能不同。

因此,在进行长期关键词监控时,应尽量固定 glhl 的取值,保证不同时间采集的数据处于相对一致的搜索环境中。否则,排名变化可能并不是网站实际排名发生了波动,而是地区或语言条件不同造成的结果差异。

本次监控面向美国英文搜索环境,因此设置为:

Plain 复制代码
"gl": "us",
"hl": "en",

如果后续需要监控其他市场,也可以根据目标地区和语言进行调整。例如,监控英国英文搜索环境时,可以将 gl 修改为 uk,同时继续使用 hl="en"

Python 复制代码
import requests
import csv
import time
from datetime import datetime

API_URL = "https://scraperapi.dataify.com/request"
API_TOKEN = "YOUR_TOKEN"

TARGET_DOMAIN = "apple.com"

KEYWORDS = [
    "iphone 16",
    "macbook air",
    "apple watch",
    "airpods pro",
    "apple store"
]

HEADERS = {
    "Authorization": f"Bearer {API_TOKEN}",
    "Content-Type": "application/x-www-form-urlencoded",
}


def call_search_api(keyword):
    data = {
        "engine": "google",
        "q": keyword,
        "json": "1",
        "google_domain": "google.com",
        "gl": "us",
        "hl": "en",
        "device": "desktop",
    }

    response = requests.post(API_URL, headers=HEADERS, data=data, timeout=60)
    response.raise_for_status()
    return response.json()


def extract_organic_rank(result, target_domain):
    organic_results = result.get("organic_results", [])

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

        if target_domain in link:
            return {
                "rank": index,
                "title": item.get("title"),
                "link": link,
                "snippet": item.get("snippet")
            }

    return {
        "rank": None,
        "title": None,
        "link": None,
        "snippet": None
    }


def extract_ads_count(result):
    ads = result.get("ads", [])
    return len(ads)


def extract_related_searches(result):
    related = result.get("related_searches", [])

    queries = []
    for item in related:
        query = item.get("query")
        if query:
            queries.append(query)

    return " | ".join(queries)


def save_rows(rows, file_path="apple_seo_keyword_monitor.csv"):
    fieldnames = [
        "created_at",
        "keyword",
        "target_domain",
        "rank",
        "title",
        "link",
        "snippet",
        "ads_count",
        "related_searches"
    ]

    try:
        with open(file_path, "r", encoding="utf-8-sig") as f:
            file_exists = True
    except FileNotFoundError:
        file_exists = False

    with open(file_path, "a", newline="", encoding="utf-8-sig") as f:
        writer = csv.DictWriter(f, fieldnames=fieldnames)

        if not file_exists:
            writer.writeheader()

        writer.writerows(rows)


def run_once():
    rows = []
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    for keyword in KEYWORDS:
        print(f"正在监控关键词:{keyword}")

        try:
            result = call_search_api(keyword)
            rank_info = extract_organic_rank(result, TARGET_DOMAIN)
            ads_count = extract_ads_count(result)
            related_searches = extract_related_searches(result)

            rows.append({
                "created_at": now,
                "keyword": keyword,
                "target_domain": TARGET_DOMAIN,
                "rank": rank_info["rank"],
                "title": rank_info["title"],
                "link": rank_info["link"],
                "snippet": rank_info["snippet"],
                "ads_count": ads_count,
                "related_searches": related_searches
            })

        except Exception as e:
            rows.append({
                "created_at": now,
                "keyword": keyword,
                "target_domain": TARGET_DOMAIN,
                "rank": None,
                "title": None,
                "link": None,
                "snippet": f"ERROR: {e}",
                "ads_count": None,
                "related_searches": None
            })

    save_rows(rows)
    print("本轮监控完成")


if __name__ == "__main__":
    while True:
        run_once()
        print("等待 1 小时后继续下一轮监控")
        time.sleep(3600)

运行后结果如下,程序会生成一个 CSV 文件:

里面会持续记录每个关键词的搜索结果表现。数据如下:

这张表的价值在于,它不是一次性的搜索截图,而是一份可以持续增长的历史数据。

通过它可以观察:

  • apple.com 在不同关键词下是否稳定排名靠前;

  • 某个关键词是否突然下降;

  • 广告结果数量是否增加;

  • 相关搜索中是否出现新的热门词;

  • 不同产品词之间的搜索竞争强度是否不同。

五、生成趋势图:观察排名变化

有了 CSV 数据后,就可以进一步画趋势图。

这里以 iphone 16 为例,查看 apple.com 在这个关键词下的排名变化。

Python 复制代码
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("apple_seo_keyword_monitor.csv")

keyword = "iphone 16"
target_df = df[df["keyword"] == keyword].copy()

target_df["created_at"] = pd.to_datetime(target_df["created_at"])
target_df["rank"] = pd.to_numeric(target_df["rank"], errors="coerce")

plt.figure(figsize=(10, 5))
plt.plot(target_df["created_at"], target_df["rank"], marker="o")
plt.gca().invert_yaxis()

plt.title(f"SEO Ranking Trend: {keyword}")
plt.xlabel("Time")
plt.ylabel("Rank")
plt.grid(True)
plt.tight_layout()
plt.savefig("iphone_16_ranking_trend.png", dpi=200)
plt.show()

运行结果如下:

这里有一个小细节:SEO 排名越小越好。第 1 名比第 5 名更好,这样排名越靠前,图上位置越高,看起来更符合直觉。

iphone 16 这个关键词下,10:00 和 11:00 都保持在第 1 位,说明这两个时间点官网在搜索结果中占据最靠前位置。到了 12:00,排名从第 1 位下降到第 2 位。这个变化虽然不大,但已经说明搜索结果存在波动。如果只是人工偶尔搜索一次,很容易忽略这种变化;但通过定时调用搜索引擎 API,就可以把这种排名变化记录下来,并形成可视化趋势。

如果还想观察广告竞争强度,可以画广告数量变化:

Python 复制代码
import pandas as pd
import matplotlib.pyplot as plt

df = pd.read_csv("apple_seo_keyword_monitor.csv")

keyword = "iphone 16"
target_df = df[df["keyword"] == keyword].copy()

target_df["created_at"] = pd.to_datetime(target_df["created_at"])
target_df["ads_count"] = pd.to_numeric(target_df["ads_count"], errors="coerce")

plt.figure(figsize=(10, 5))
plt.plot(target_df["created_at"], target_df["ads_count"], marker="o")

plt.title(f"Ads Count Trend: {keyword}")
plt.xlabel("Time")
plt.ylabel("Ads Count")
plt.grid(True)
plt.tight_layout()
plt.savefig("iphone_16_ads_count_trend.png", dpi=200)
plt.show()

这样就可以同时观察两个指标:

Plain 复制代码
自然排名变化:apple.com 排第几
广告数量变化:这个关键词竞争是否变强

如果某个时间点排名下降,同时广告数量上升,就可能说明该关键词下的搜索竞争正在变激烈。

六、这个监控工具可以用在哪些场景?

虽然这次使用的是 Apple 产品关键词,但这套方法并不局限于监控 apple.com。实际应用中,可以保持关键词列表不变,同时记录搜索结果中出现的不同网站、页面类型和对应排名,从而分析同一关键词下的竞争格局。

例如,继续监控以下关键词:

  • iphone 16

  • macbook air

  • apple watch

  • airpods pro

  • apple store

在每次获取搜索结果后,除了记录 apple.com 的排名,还可以进一步提取 Amazon、Walmart、Best Buy 等电商平台,以及科技媒体、测评网站、论坛和视频网站的域名与排名。

SEO:监控目标网站与竞品网站排名

SEO 团队可以围绕一组固定关键词,持续记录目标网站和竞品网站在自然搜索结果中的位置。

例如,在监控 iphone 16 时,可以同时记录:

这样不仅能判断目标网站自身排名是否发生变化,还可以观察排名下降是否由竞品官网、电商平台、新闻页面或测评内容上升造成。

相比只记录 apple.com 是否进入前 10 名,同时保存主要竞争域名及其排名,更有利于分析搜索结果整体结构的变化。

电商:观察固定产品词下的平台竞争格局

电商分析不一定需要重新配置一组关键词,也可以继续使用原有的 Apple 产品词,重点观察搜索结果中出现了哪些电商平台。

例如,在 airpods pro 的搜索结果中,可以记录:

通过持续记录,可以判断同一个产品词下,哪些平台长期占据靠前位置,哪些平台的排名正在上升,以及品牌官网与第三方销售平台之间的搜索流量竞争关系。

这类数据可以用于渠道分析、竞品监测和商品页面优化。

广告:观察相同关键词下的广告竞争变化

广告监控也可以继续使用相同关键词,不需要单独改成带有 dealdiscountsale 的搜索词。

在每次请求中,可以记录:

  • 广告结果数量;

  • 广告主域名;

  • 广告出现位置;

  • 广告标题与落地页;

  • 自然搜索结果是否受到广告区域挤压。

例如,在 iphone 16 关键词下,如果广告数量从 1 条增加到 4 条,同时 apple.com 的自然排名虽然没有变化,但页面中的实际展示位置有所下移,就说明该关键词的广告竞争正在增强。

因此,广告数量和广告主变化可以与自然排名数据结合分析,而不是单独更换关键词进行监控。

内容与社媒:识别搜索结果中的内容来源变化

对于内容和社媒分析,同样可以保留原来的关键词,观察搜索结果中是否开始出现新的内容平台。

例如,在 apple watch 的搜索结果中,可以记录是否出现:

  • YouTube 视频;

  • Reddit 讨论;

  • TikTok 页面;

  • 科技媒体测评;

  • 新闻报道;

  • 问答或论坛页面。

如果某段时间内,视频、论坛讨论或测评文章的排名持续上升,就说明该关键词下的搜索结果结构正在发生变化,用户可能不再只关注品牌官网和商品页面,而是更关注使用体验、测评内容和社区讨论。通过这种方式,可以在不改变关键词的情况下,同时完成品牌排名监控、电商平台分析、广告竞争观察和内容来源变化分析。

七、总结

这篇文章的重点不是单次获取搜索结果,而是把搜索引擎 API 做成一个可以持续运行的 SEO 关键词监控工具。

完整流程是:

Plain 复制代码
配置目标域名 apple.com
  ↓
配置关键词列表
  ↓
调用搜索引擎 API
  ↓
解析自然搜索结果、广告结果和相关搜索
  ↓
记录 apple.com 当前排名
  ↓
每小时保存一次 CSV
  ↓
生成排名趋势图

相比手动搜索,这种方式更适合长期监控。它可以持续记录关键词排名变化,也可以观察广告竞争强度、相关搜索变化和搜索结果中的新竞争者。

对于 SEO、电商、广告、社媒和舆情团队来说,搜索结果不是一个静态页面,而是一组可以持续追踪的数据。通过搜索引擎 API 把这些数据保存下来,后续就可以做趋势分析、异常告警、竞品监测和内容优化。

相关推荐
程序大爆炸1 小时前
gcsfuse的TypeCache
后端
心运软件1 小时前
Python实战:中国大学排行榜数据采集与可视化大屏
后端·python
GISer_Jing2 小时前
前端转全栈须知后端知识
前端·后端·ai·前端框架
爱勇宝2 小时前
我做了一个排版器,也踩了一遍富文本复制的坑
前端·后端·微信
布朗克1682 小时前
Go 入门到精通-33-unsafe 与 CGO
开发语言·后端·golang·unsafe·cgo
铁皮饭盒2 小时前
面试官:如何用 Bun + JS 实现安全的文件 MCP 工具集
前端·javascript·后端
小Ti客栈3 小时前
Spring Boot 整合 Swagger2 和 Knife4j实现接口文档与可视化调试
java·spring boot·后端
明月_清风3 小时前
💰 DeFi 入门完全指南:从 Uniswap 到 Aave,一文读懂去中心化金融
后端·web3
Conan在掘金3 小时前
鸿蒙报错速查:struct 里嵌套 @Component struct 就炸,Unexpected keyword 编译报错,根因 + 真解法
后端