JA
ガイド

SERPの類似度によるキーワードクラスタリング

Googleで同じページが表示される2つのキーワードは、同じ検索意図に応えています。1つのページで両方を狙うべきです。手法と適切なしきい値、すぐに使えるスクリプトを紹介します。

この記事のポイント
  • 2つのキーワードは、上位10件で共通するURLが十分にあれば同じグループになります。最も一般的なしきい値は共通3〜4件です。
  • この手法は語句の似かたではなく、Googleが実際に表示するものに基づきます。表現が似た2つのキーワードでも、同じグループになるとは限りません。
  • Semscraper APIなら1ページ目の1,000キーワードで€0.30。あとはこの記事のPythonスクリプトが処理します。

考え方

SERPの類似度によるクラスタリングの出発点はシンプルです。Googleが2つのクエリに同じページを表示するなら、Googleはそれらを同じ検索意図と理解している、ということです。その場合、サイトの1つのページで両方のキーワードで上位を狙えます。逆に、見た目は似ていてもSERPに共通点がない2つのキーワードには、別々のページが必要です。

この手法は語句そのものでグループ化するより信頼できます。文字列の類似度ではなく、Googleの判断に基づくからです。「ランニングシューズ レディース」と「女性用 ジョギング スニーカー」は共通する語がほとんどありませんが、SERPは大きく重なることがよくあります。

手順

  1. 上位10件を収集:各キーワードについて、適切な国、言語、デバイスで取得します。
  2. 自然検索結果のURLを抽出:広告、動画などのブロックは無視します。
  3. キーワードを2つずつ比較:共通するURLはいくつあるか。
  4. グループ化:しきい値を超えるペアをまとめます。

しきい値の選び方

上位10件で共通するURL数効果
2グループが大きくなるが、近い別の検索意図が混ざるおそれがある
3または4最も一般的な設定。細かく分かれすぎず、まとまりのあるグループになる
5以上非常に絞り込まれたグループ。孤立するキーワードが多くなる

まず3から始め、十数グループを目で確認してから調整しましょう。適切なしきい値はテーマによって異なります。少数の大手サイトが占めるECのSERPは、メディア系のSERPより重なりやすい傾向があります。

厳密なグループと緩やかなグループ

アプローチは2つあります。厳密なクラスタリングでは、グループ内のすべてのキーワードが、軸となるキーワード(通常はグループ内で最も検索されるもの)としきい値以上のURLを共有している必要があります。グループはすっきりし、以下のスクリプトもこの方式です。緩やかなクラスタリングでは、キーワードはグループのいずれか1つと結びついていれば十分です。グループは連鎖的に大きくなり、ある検索意図から別の検索意図へずれていくおそれがあります。

作成または統合するページを決めるなら、ほぼ常に厳密なクラスタリングが適しています。

スクリプト全文

このスクリプトはキーワードをAPIに送り、SERPを取得してからグループを作成します。APIキー、キーワードのリスト、必要に応じてしきい値を書き換えてください。キーワードは検索回数の多い順に並べておくと、各グループの軸がメインのクエリになります。

clustering.py
import time
import requests

API_KEY = "API_KEY"
API = "https://api.semscraper.com/v1/serp"
HEADERS = {"Authorization": "Bearer " + API_KEY}
THRESHOLD = 3

# 検索回数の多い順
keywords = ["ランニングシューズ レディース", "女性用 ジョギング スニーカー", "トレイルランニングシューズ レディース"]

# 1. 収集を作成:上位10件、google.co.jp、デスクトップ
items = [{"search_engine": "google_search", "keyword": k, "device": "desktop",
          "location": "jp", "language": "ja", "depth": 1} for k in keywords]
created = requests.post(API, json=items, headers=HEADERS).json()
ids = [row["id"] for row in created["data"]]

# 2. 待機してからSERPを取得、最長10分
serps, attempts = {}, 0
while len(serps) != len(ids) and attempts < 60:
    attempts += 1
    time.sleep(10)
    res = requests.get(API, params={"ids": ",".join(ids), "output": "json"}, headers=HEADERS).json()
    for row in res["data"]:
        if row["status"] == "done":
            urls = set()
            for block in row["results"]:
                if block["type"] == "organic":
                    urls.update(item["url"].rstrip("/") for item in block["items"])
            serps[row["keyword"]] = urls

# 3. 軸キーワードを中心にした厳密なクラスタリング
clusters, seen = [], set()
for pivot in keywords:
    if pivot in seen or pivot not in serps:
        continue
    group = [pivot]
    seen.add(pivot)
    for other in keywords:
        if other not in seen and other in serps:
            if len(serps[pivot] & serps[other]) >= THRESHOLD:
                group.append(other)
                seen.add(other)
    clusters.append(group)

for group in clusters:
    print(" | ".join(group))

作成時に返されたIDは、カンマ区切りで取得用の呼び出しに渡します。大量に処理する場合は、代わりにコールバックURLを使いましょう。結果の準備ができ次第APIから送信されるため、ポーリングのループが不要になります。

避けたい落とし穴

  • 地域やデバイスを混ぜる:同じ条件で収集したSERPを比べましょう。そうしないと、差が検索意図ではなく条件の違いから生まれます。ローカルなテーマなら、すべてのクエリを同じ都市に位置指定します。
  • 日付の異なるSERPを比べる:一連のキーワードは同じ日にまとめて収集しましょう。
  • URLをそのまま使う:少なくとも末尾のスラッシュ、できればパラメータも正規化し、同一のURLを見逃さないようにします。
  • 自然検索以外のブロックを数える:動画、ニュース、広告は時間ごとに変わり、比較を歪めます。

費用

上位10件の比較には検索結果1ページで十分です。Semscraperなら、1,000キーワードで€0.30、10,000キーワードで€3です。登録時の無料1,000リクエストで、実際のキーワードを使って手法を試せます。収集パラメータはGoogle検索APIのページで詳しく説明しています。

ご自身のキーワードで試してみましょう

1,000リクエストまで無料、クレジットカード登録は不要です。