こんにちは、パレイド思想部です。
「この記事、思ったより読まれていないな」「タグが適切かどうかわからない」「内部リンクをもっと張るべきでは?」——こうした確認作業を、勘と手動チェックで回していませんか。
第6回は、Google Analytics 4(GA4)・Search Console のデータを Python で取得し、LLM がブログ記事の品質を自動スコアリングする仕組みと、その結果を Streamlit で可視化するフローを解説します。
感覚でなくデータで改善する:GA4連携とLLMスコアリングの実践
記事品質の改善には2種類のフィードバックが必要です。
| フィードバック種別 | 情報源 | 確認できること |
|---|---|---|
| 読者行動 | GA4 / Search Console | 実際に読まれた回数・検索クエリ・流入元 |
| コンテンツ品質 | LLM スコアリング | タグの妥当性・内部リンク機会・可読性 |
前者は「すでに起きたこと」の計測、後者は「改善できること」の発見です。どちらか一方だけでは手が打てない。ai-editor ではこの2軸を同じダッシュボードで並べて、より効果の高い順に改善を進めることを目指しています。
Google Analytics (GA4) 連携の実装
GA4 データの取得には google-analytics-data、Search Console には google-api-python-client を使います。
代替案として requests で直接 REST API を叩く方法もありますが、OAuth2 トークンリフレッシュの処理を自前で書く必要があり、実装やテストが複雑になります。公式クライアントライブラリであれば認証が容易なため、こちらを採用しました。
# tools/publish/wp_analytics/ga4_client.py
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import (
DateRange, Dimension, Metric, RunReportRequest,
)
class GA4Client:
def __init__(self, property_id: str, credentials_path: str):
import os
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path
self.client = BetaAnalyticsDataClient()
self.property_id = property_id
def fetch_page_metrics(
self, start_date: str, end_date: str, limit: int = 50
) -> list[dict]:
request = RunReportRequest(
property=f"properties/{self.property_id}",
dimensions=[Dimension(name="pagePath")],
metrics=[
Metric(name="screenPageViews"),
Metric(name="averageSessionDuration"),
Metric(name="bounceRate"),
],
date_ranges=[DateRange(start_date=start_date, end_date=end_date)],
limit=limit,
)
response = self.client.run_report(request)
return [
{
"path": row.dimension_values[0].value,
"views": int(row.metric_values[0].value),
"avg_duration": float(row.metric_values[1].value),
"bounce_rate": float(row.metric_values[2].value),
}
for row in response.rows
]
RunReportRequest にディメンション・メトリクス・期間を渡すだけで構造化レスポンスが返ります。ページパスと記事フロントマターの slug をキーにして、GA4 データをローカルの Markdown と突合します。
Search Console 流入クエリの取得
同様に、Search Console のデータも API で取得します。
# tools/publish/wp_analytics/search_console_client.py
from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials
class SearchConsoleClient:
def __init__(self, site_url: str, credentials_path: str):
creds = Credentials.from_service_account_file(
credentials_path,
scopes=["https://www.googleapis.com/auth/webmasters.readonly"],
)
self.service = build("searchconsole", "v1", credentials=creds)
self.site_url = site_url
def fetch_queries(self, page_url: str, days: int = 28) -> list[dict]:
from datetime import date, timedelta
end = date.today()
start = end - timedelta(days=days)
body = {
"startDate": str(start),
"endDate": str(end),
"dimensions": ["query"],
"dimensionFilterGroups": [{
"filters": [{
"dimension": "page",
"expression": page_url,
}]
}],
}
resp = (
self.service.searchanalytics()
.query(siteUrl=self.site_url, body=body)
.execute()
)
return resp.get("rows", [])
LLM 品質スコアリング
GA4 で「読まれていない」記事が見つかったとき、次に知りたいのは「なぜ読まれないのか」です。LLM はここで使います。
スコアリング項目と設計意図
LLM に「記事全体を評価してください」と投げるだけでは、定性的・定量的な評価はできません。 評価軸を構造化して渡すことで、複数記事の比較と評価を可能にしました。
- タグと本文の一致度
- 現在の内部リンク数
- LLM が提案するリンク先
- 見出し構成・段落長
実際には、WordPress API と組み合わせて全記事に一括で同じ処理を適用しています。 {} で囲まれたところはPythonで実際の情報を展開します。 また、指示応答性の高いモデルや function calling 対応モデルを使えば回答をJSONで取得できます。
以下のブログ記事を評価し、JSON で結果を返してください。
{記事本文}
既存タグ: {タグ}
サイト内の関連記事スラッグ一覧: {スラッグ一覧}
評価項目:
- tag_relevance (0-10): タグと本文テーマの一致度
- internal_link_opportunities: 本文中でリンクを張るべき関連記事スラッグのリスト(最大3件)
- readability (0-10): 見出し・段落構成の読みやすさ
- seo_summary: 50字以内の改善提案
JSON のみ返してください。
スコアリングだけならローカルLLMの軽量モデルで十分なケースが多く、ここでは qwen3:8b を使ってみました。
帰ってきたデータは、当サイトは Streamlit を用いたダッシュボードで管理してますが、ここでは割愛します。
LLM の応答を JSON として取得できれば、その後の応用も容易になるでしょう。
まとめ
今回実装したのは3つの仕組みです。
- GA4 Client — ページ単位のビュー数・直帰率を取得し、Markdown のスラッグと突合
- LLM スコアラー — タグ適合度・内部リンク機会・可読性を構造化 JSON で出力
「感覚でいい記事を書く」から「データで改善箇所を特定して書く」へのシフトは、GA4 のインストールより 記事データとの突合ロジック に手間がかかります。slug をキーにした名寄せと、LLM 出力の JSON スキーマ固定が安定運用のポイントでした。
次回最終回(第10回)は、全体のまとめを再掲します。



