はじめに
Google Search Console の問題
Google Search Console でサイトの検索パフォーマンスを分析する際、
以下のような問題に直面することが多いのではないでしょうか。
- 検索クエリとその検索結果で表示されるページ(URL)の対応を1つずつ確認する必要がある
- 上位表示されているのに CTR が低い記事を見つけるのが大変
- 1つのページに対して複数のクエリが関連している状況を把握しづらい

本記事での解決方法
この記事では、Google Search Console API を使って以下の作業を自動化します。
- ページ(URL)とクエリの連結データを一括で取得
- リライト候補を自動で選定
- 分析しやすい Excel 形式でレポートを出力
ソースコード一式
本記事で使用するソースコードは、以下で公開しています。
GitHub - SANACHAN-prog / google-search-console-rewrite-finder
必要な環境
本ツールを実行するには、以下の環境が必要となります。
関連
なお、前編となる「Google Search Console API を Python で利用するための環境構築」
に記載の設定が完了していることを前提としています。
アプリ/ライブラリ名 | バージョン |
Python | v3.11.11(v3.8 以上必須) |
pandas | v2.2.3 |
openpyxl | v3.1.5 |
google-api-python-client | v2.164.0 |
oauth2client | v4.1.3 |
プログラムの全体構成
ファイル構成と概要
本プログラムは、以下の 3 つの Python ファイルで構成されています。
- メインスクリプト(main.py)
・プログラムのエントリーポイント
・処理全体の制御
・設定値の記述 - Search Console API 操作クラス(GoogleAPIClientWrapper.py)
・Google Search Console API との通信
・データの取得 - Excel 出力クラス(SearchReportWriter.py)
・データフレームの Excel 形式への変換
・リライト候補の判定
・レポートの書式設定
ディレクトリ構成
Tree
google-search-console-rewrite-finder/
│
├── main.py # メインスクリプト
├── key_file.json # API認証キー(前編の記事で取得)
├── GoogleAPIClientWrapper.py # API操作クラス
├── SearchReportWriter.py # Excel出力クラス
└── requirements.txt # 依存ライブラリ
メインスクリプト
公開しているコードの main.py では、以下のように処理を順番に実行します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from datetime import datetime, timedelta from GoogleAPIClientWrapper import * from SearchReportWriter import * API_SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly'] API_MAX_ROWS = 1000 API_KEY_FILE = 'key_file.json' SITE_URL = 'https://progzakki.sanachan.com/' PERIOD_DAYS = 90 OUTPUT_FILE_CSV = 'page_query_mapping.csv' OUTPUT_FILE_EXCEL = 'page_query_mapping.xlsx' def main(): # 期間の設定 end_date = datetime.now().strftime('%Y-%m-%d') start_date = (datetime.now() - timedelta(days=PERIOD_DAYS)).strftime('%Y-%m-%d') # APIサービスの初期化 gapi = GoogleAPIClientWrapper(API_KEY_FILE, API_SCOPES, API_MAX_ROWS) # データ取得 df = gapi.fetch_url_query_data(SITE_URL, start_date, end_date) print("データの取得が完了しました。") # CSVとして保存 #df.to_csv(OUTPUT_FILE_CSV, index=False, encoding='utf-8') #print("データをCSVファイルに出力しました。") # Excelとして保存 writer = SearchReportWriter(OUTPUT_FILE_EXCEL) writer.save_to_excel(df) print("データをExcelファイルに出力しました。") if __name__ == "__main__": main() |

お好みに合わせて切り替えてください。
設定項目の説明
設定項目 | 説明 | 変更の必要性 |
API_SCOPES | API の権限スコープ | 変更不要 |
API_MAX_ROWS | 取得する最大行数 | サイト規模に応じて調整 |
API_KEY_FILE | API 認証キーが保存されているファイルのパス | 必要に応じて変更 |
SITE_URL | 分析対象のサイトURL(ドメイン) | 要変更 |
PERIOD_DAYS | 取得するデータの期間(日数) | 必要に応じて調整 |
OUTPUT_FILE_CSV | 出力ファイル名 / パス(CSV) | 必要に応じて変更 |
OUTPUT_FILE_EXCEL | 出力ファイル名 / パス(Excel) | 必要に応じて変更 |
このメインスクリプトを起点として、以降の章で各クラスの詳細な実装を説明していきます。
ページとクエリの連結データ取得
Search Console API のデータ構造
Google Search Console API から取得できるデータには、以下の項目があります。
- page:ページ(URL)
- query:検索クエリ
- clicks:クリック数
- impressions:表示回数
- ctr:クリック率
- position:平均掲載順位
データ取得の実装
以下のコードは、「ページ URL」と「検索クエリ」を連結し、その関連データを取得するクラスです。
公開しているコードの GoogleAPIClientWrapper.py です。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import json import pandas as pd from google.oauth2.credentials import Credentials from google.oauth2 import service_account from googleapiclient.discovery import build class GoogleAPIClientWrapper(): def __init__(self, key_file_path, scopes, row_limit): """Search Console APIのサービスを初期化""" self.__credentials = service_account.Credentials.from_service_account_file( key_file_path, scopes=scopes ) self.__service = build('searchconsole', 'v1', credentials=self.__credentials) self.__row_limit = row_limit def fetch_url_query_data(self, site_url, start_date, end_date): """URLとクエリの関連データを取得""" request = { 'startDate': start_date, 'endDate': end_date, 'dimensions': ['page', 'query'], 'rowLimit': self.__row_limit, 'startRow': 0, # クリック数で降順ソート 'orderBy': [ { 'fieldName': 'clicks', # clicks/impressions/ctr/position 'sortOrder': 'DESCENDING' # ASCENDING/DESCENDING } ] } response = self.__service.searchanalytics().query( siteUrl=site_url, body=request ).execute() rows = [] if 'rows' in response: for row in response['rows']: rows.append({ 'URL': row['keys'][0], 'クエリ': row['keys'][1], 'クリック数': row['clicks'], '表示回数': row['impressions'], 'CTR': f"{(row['ctr'] * 100):.2f}%", '掲載順位': f"{row['position']:.2f}" }) return pd.DataFrame(rows) |

ハイライトのコメントに書いてある値を指定でき、ソート種別と方向を変更できます。
データ取得時の注意点
- 取得期間の指定
・90 日以内のデータを取得可能
・日付は YYYY-MM-DD 形式で指定 - データの制限
・1回のリクエストで最大 25,000 行
取得データの確認
以下のコードで、取得したデータ(データフレーム)の内容を確認できます。
1 2 3 4 5 6 7 8 |
def check_data(df): """取得データの確認""" print(f"データ総数: {len(df)}行") print("\nデータサンプル:") print(df.head()) print("\n各列の統計:") print(df.describe()) |
リライト候補の判定
判定条件の設定
リライト候補の判定には、以下の 3 つの条件を使用しています。
- 検索順位が上位(10 位以内)
- 十分な表示回数(100 回以上)
- 低い CTR (3%未満)
Excel ファイルへ出力する SearchReportWrite.py に集約されていますが、
L.40 付近の以下のコードが該当します。
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
def __create_formatted_row(self, row): """1行分のデータを整形""" ctr_value = float(row['CTR'].strip('%')) position_value = float(row['掲載順位']) return { 'クエリ': row['クエリ'], 'URL': row['URL'], 'クリック数': row['クリック数'], '表示回数': row['表示回数'], 'CTR': row['CTR'], '掲載順位': row['掲載順位'], 'リライト推奨': self.__check_rewrite_recommendation( position_value, row['表示回数'], ctr_value) } def __check_rewrite_recommendation(self, position, impressions, ctr): """リライト推奨の判定""" return '*' if (position <= 10 and impressions >= 100 and ctr < 3.0) else '' |
判定条件のカスタマイズ
条件は以下のような観点で、変更・調整が可能です。
ご自身の「リライト候補」の見つけ方に合わせて、適宜変更してください。
- 順位の範囲:競争の激しさに応じて調整(例:5 位以内)
- 表示回数の閾値:サイトの規模に応じて調整
- CTR の基準:記事内容で触れる業界平均などを考慮して設定
Excel 出力の実装
以下のコードは、取得したデータを整形し、Excel ファイルに出力するクラスです。
公開しているコードの SearchReportWrite.py です。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import pandas as pd import unicodedata from openpyxl import load_workbook from openpyxl.styles import Alignment def get_east_asian_width(text): """ 文字列の表示幅を計算する 全角文字は2、半角文字は1として計算 """ width = 0 for c in str(text): # East Asian Width (EA)の判定 if unicodedata.east_asian_width(c) in ['F', 'W', 'A']: width += 2 else: width += 1 return width class SearchReportWriter: def __init__(self, output_file): """初期化""" self.output_file = output_file def __format_search_data(self, df): """検索データの整形とリライト推奨の判定""" df_sorted = df.sort_values('URL') formatted_data = [] for _, row in df_sorted.iterrows(): formatted_row = self.__create_formatted_row(row) formatted_data.append(formatted_row) return pd.DataFrame(formatted_data) def __create_formatted_row(self, row): """1行分のデータを整形""" ctr_value = float(row['CTR'].strip('%')) position_value = float(row['掲載順位']) return { 'クエリ': row['クエリ'], 'URL': row['URL'], 'クリック数': row['クリック数'], '表示回数': row['表示回数'], 'CTR': row['CTR'], '掲載順位': row['掲載順位'], 'リライト推奨': self.__check_rewrite_recommendation( position_value, row['表示回数'], ctr_value) } def __check_rewrite_recommendation(self, position, impressions, ctr): """リライト推奨の判定""" return '*' if (position <= 10 and impressions >= 100 and ctr < 3.0) else '' def __adjust_column_widths(self, worksheet, df): """列幅の調整""" for idx, col in enumerate(worksheet.columns): max_length = get_east_asian_width(df.columns[idx]) column = col[0].column_letter for cell in col: try: width = get_east_asian_width(cell.value) if width > max_length: max_length = width except: pass worksheet.column_dimensions[column].width = (max_length + 2) def __set_cell_alignments(self, worksheet, df): """セルの配置設定""" for idx, col in enumerate(worksheet.columns): if df.columns[idx] in ['CTR', '掲載順位']: for cell in col: cell.alignment = Alignment(horizontal='right') def save_to_excel(self, df): """Excelファイルとして保存""" formatted_df = self.__format_search_data(df) with pd.ExcelWriter(self.output_file, engine='openpyxl') as writer: formatted_df.to_excel(writer, index=False, sheet_name='検索データ') worksheet = writer.sheets['検索データ'] self.__adjust_column_widths(worksheet, formatted_df) self.__set_cell_alignments(worksheet, formatted_df) |
データの整形と Excel への出力
取得した検索パフォーマンスデータを Excel に出力する際、以下の処理を実行します。
- データの整形(URL でソート)
- リライト推奨の判定結果を追加
- Excel 形式での出力と書式設定
全体の流れは、以下のコードが該当します。
85 86 87 88 89 90 91 92 93 94 95 |
def save_to_excel(self, df): """Excelファイルとして保存""" formatted_df = self.__format_search_data(df) with pd.ExcelWriter(self.output_file, engine='openpyxl') as writer: formatted_df.to_excel(writer, index=False, sheet_name='検索データ') worksheet = writer.sheets['検索データ'] self.__adjust_column_widths(worksheet, formatted_df) self.__set_cell_alignments(worksheet, formatted_df) |
書式設定:日本語対応の列幅調整
以下のコードで、日本語文字の表示幅を考慮した列幅の調整を行っています。
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
def __adjust_column_widths(self, worksheet, df): """列幅の調整""" for idx, col in enumerate(worksheet.columns): max_length = get_east_asian_width(df.columns[idx]) column = col[0].column_letter for cell in col: try: width = get_east_asian_width(cell.value) if width > max_length: max_length = width except: pass worksheet.column_dimensions[column].width = (max_length + 2) |
書式設定:セルの配置設定
以下のコードで、数値データの右寄せなど、見やすさを考慮した配置を設定しています。
78 79 80 81 82 83 84 |
def __set_cell_alignments(self, worksheet, df): """セルの配置設定""" for idx, col in enumerate(worksheet.columns): if df.columns[idx] in ['CTR', '掲載順位']: for cell in col: cell.alignment = Alignment(horizontal='right') |
出力される Excel の形式
最終的には、以下のような形式で Excel ファイルが出力されます。
- A列:クエリ(左寄せ)
- B列:URL(左寄せ)
- C列:クリック数(数値、右寄せ)
- D列:表示回数(数値、右寄せ)
- E列:CTR(右寄せ)
- F列:掲載順位(右寄せ)
- G列:リライト推奨(左寄せ)

出力のサンプル
活用方法と分析ポイント
レポートの読み方
生成されたExcelレポートは以下の視点で分析できます。
- リライト候補の確認
ー リライト推奨 列に「*」がついた記事を優先的に確認
ー 上位表示されているのに CTR が低い記事の内容を精査 - クエリ分析のポイント
ー 同一URLに対する複数クエリから傾向をつかむ
ー ユーザーの検索意図とコンテンツのズレ
ー 類似クエリでの検索順位の違い
定期的なチェックの自動化
Linux の場合
以下の run_search_analysis.sh のようなスクリプトを記述し、cron などに登録して定期実行できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/bash # 作業ディレクトリに移動 cd /path/to/GoogleSearchConsole # 仮想環境がある場合はアクティベート source /path/to/venv/bin/activate # Pythonスクリプトの実行 python main.py # ログ出力(オプション) echo "$(date): Search Console Analysis completed" >> /path/to/search_analysis.log |
crontabの設定
# 毎週月曜日の午前9時に実行
0 9 * * 1 /path/to/run_search_analysis.sh
# 毎日午前5時に実行
0 5 * * * /path/to/run_search_analysis.sh
# 毎月1日の午前0時に実行
0 0 1 * * /path/to/run_search_analysis.sh
Windows の場合
以下のようなバッチファイルを作成し、タスクスケジューラーに登録することで定期実行できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@echo off cd /d "%~dp0" :: 仮想環境がある場合はアクティベート(例:venv環境の場合) call venv\Scripts\activate.bat :: Pythonスクリプトの実行 python main.py :: ログ出力(オプション) echo %date% %time%: Search Console Analysis completed >> search_analysis.log :: 実行完了後一時停止(エラー確認用、必要に応じて) pause |
または、以下の記事を参考に、.exe ファイル化して利用することもできます。
こちらもCHECK
-
-
Pythonのプログラムをexe化する方法【pyinstaller】
Pythonの無料パッケージであるpyinstallerを使って、Pythonのプログラム(.py)を Windowsの実行可能ファイル(.exe)に変換する方法を紹介します。PythonがインストールされていないWindows環境でも実行できるようになります。
続きを見る
おわりに
いかがでしたでしょうか。
これまで、専用の有償ツール(月額)を利用して分析していました。
しかし、Google 側の更新により、ツールが使えなくなり、なかなか対応されないため、
自分で作ることにした結果がこのツールです。
リライト推奨を判別する条件が、私の運営するブログに依存している気もするため、
必要に応じて変更、活用いただければ幸いです。
参考