はじめに:SEO分析の悩みを一気に解決
「毎月SEOレポートを作成するのに何時間もかかっている…」 「Google Search Consoleのデータを手作業でExcelにまとめるのがもう限界…」 「競合他社のキーワード分析を効率化したい…」
そんな悩みを抱えるWebマーケターやSEO担当者の皆さん、Python + Pandasがあなたの救世主になります!
この記事では、プログラミング初心者でも簡単にSEO分析を自動化できる方法を、実際のコード例とともに徹底解説します。一度マスターすれば、月に20時間以上の作業時間を短縮できるはずです。
なぜPython + PandasがSEO分析に最適なのか?
1. 大量データの高速処理
Google Search Consoleから出力される数万行のデータも、Pandasなら数秒で処理できます。Excelでは重くて開けないファイルも楽々扱えます。
2. 自動化による人的ミスの排除
手作業でのデータ集計は必ずミスが発生します。Pythonスクリプトなら、100%正確な結果を毎回得られます。
3. 視覚化による洞察の発見
数字の羅列では見えなかったトレンドや相関関係が、グラフ化することで一目で理解できるようになります。
事前準備:環境構築から始めよう
必要なライブラリのインストール
まずは必要なPythonライブラリをインストールします。コマンドプロンプトまたはターミナルで以下を実行してください:
pip install pandas matplotlib seaborn requests beautifulsoup4 google-api-python-client
主要ライブラリの役割
- Pandas: データ操作・分析の中核
- Matplotlib/Seaborn: グラフ作成・可視化
- Requests: Webスクレイピング
- BeautifulSoup: HTML解析
- Google API Client: Google Search Console連携
実践編1:Google Search Consoleデータの自動分析
CSVデータの読み込みと基本操作
Google Search ConsoleからエクスポートしたCSVファイルを読み込み、基本的な分析を行います:
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
# CSVファイルの読み込み
df = pd.read_csv('search_console_data.csv')
# データの基本情報を確認
print("データの形状:", df.shape)
print("列名:", df.columns.tolist())
print("\n基本統計:")
print(df.describe())
# 欠損値の確認
print("\n欠損値の数:")
print(df.isnull().sum())
検索クエリの詳細分析
検索クエリごとの詳細分析を実行し、パフォーマンスの良いキーワードを特定します:
# 検索クエリ別の分析
query_analysis = df.groupby('検索クエリ').agg({
'クリック数': 'sum',
'表示回数': 'sum',
'平均掲載順位': 'mean'
}).round(2)
# CTRの計算
query_analysis['CTR'] = (query_analysis['クリック数'] / query_analysis['表示回数'] * 100).round(2)
# 上位20キーワードを表示
top_20_queries = query_analysis.sort_values('クリック数', ascending=False).head(20)
print("上位20検索クエリ:")
print(top_20_queries)
パフォーマンス推移の可視化
時系列データでのパフォーマンス推移をグラフ化します:
# 日付列をdatetime型に変換
df['日付'] = pd.to_datetime(df['日付'])
# 日別集計
daily_performance = df.groupby('日付').agg({
'クリック数': 'sum',
'表示回数': 'sum'
}).reset_index()
# グラフの作成
plt.figure(figsize=(15, 8))
plt.subplot(2, 1, 1)
plt.plot(daily_performance['日付'], daily_performance['クリック数'],
color='blue', linewidth=2)
plt.title('クリック数の推移', fontsize=14, fontweight='bold')
plt.ylabel('クリック数')
plt.grid(True, alpha=0.3)
plt.subplot(2, 1, 2)
plt.plot(daily_performance['日付'], daily_performance['表示回数'],
color='red', linewidth=2)
plt.title('表示回数の推移', fontsize=14, fontweight='bold')
plt.ylabel('表示回数')
plt.xlabel('日付')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
実践編2:競合他社のキーワード分析
Webスクレイピングによる競合調査
競合サイトのメタデータやコンテンツ情報を自動取得します:
import requests
from bs4 import BeautifulSoup
import time
def analyze_competitor_page(url):
"""競合ページの基本SEO情報を取得"""
try:
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers)
soup = BeautifulSoup(response.content, 'html.parser')
# SEO要素の抽出
title = soup.find('title')
title_text = title.text.strip() if title else "タイトルなし"
meta_desc = soup.find('meta', attrs={'name': 'description'})
desc_text = meta_desc['content'] if meta_desc else "ディスクリプションなし"
# H1タグの取得
h1_tags = [h1.text.strip() for h1 in soup.find_all('h1')]
return {
'URL': url,
'タイトル': title_text,
'タイトル文字数': len(title_text),
'メタディスクリプション': desc_text,
'ディスクリプション文字数': len(desc_text),
'H1タグ数': len(h1_tags),
'H1内容': h1_tags
}
except Exception as e:
print(f"エラー: {url} - {e}")
return None
# 競合サイトのURL一覧
competitor_urls = [
'https://example-competitor1.com',
'https://example-competitor2.com',
'https://example-competitor3.com'
]
# 分析実行
competitor_data = []
for url in competitor_urls:
data = analyze_competitor_page(url)
if data:
competitor_data.append(data)
time.sleep(1) # サーバーへの負荷を考慮
# データフレームに変換
competitor_df = pd.DataFrame(competitor_data)
print("競合分析結果:")
print(competitor_df)
キーワード密度分析
取得したコンテンツからキーワード密度を分析します:
import re
from collections import Counter
def calculate_keyword_density(text, target_keywords):
"""キーワード密度を計算"""
# テキストの前処理
text = re.sub(r'[^\w\s]', ' ', text.lower())
words = text.split()
total_words = len(words)
keyword_counts = {}
for keyword in target_keywords:
count = text.count(keyword.lower())
density = (count / total_words * 100) if total_words > 0 else 0
keyword_counts[keyword] = {
'出現回数': count,
'密度(%)': round(density, 2)
}
return keyword_counts
# 分析対象キーワード
target_keywords = ['SEO', 'マーケティング', 'コンテンツ', 'ランキング']
# キーワード密度分析の実行例
sample_text = "SEOマーケティングは重要です。コンテンツSEOでランキング向上を..."
density_result = calculate_keyword_density(sample_text, target_keywords)
print("キーワード密度分析結果:")
for keyword, data in density_result.items():
print(f"{keyword}: {data}")
実践編3:高度な分析とレポート生成
相関分析による洞察発見
各SEO要素間の相関関係を分析し、改善すべき要素を特定します:
# 相関分析用のサンプルデータ作成
seo_data = pd.DataFrame({
'ページ数': [100, 150, 200, 300, 250, 180, 220, 280, 320, 190],
'被リンク数': [50, 80, 120, 200, 150, 90, 130, 180, 240, 110],
'オーガニック流入': [1000, 1500, 2500, 4000, 3000, 1800, 2200, 3500, 4500, 2000],
'平均滞在時間': [120, 150, 180, 220, 200, 160, 170, 210, 240, 165],
'直帰率': [70, 65, 60, 45, 50, 68, 62, 48, 40, 66]
})
# 相関行列の計算
correlation_matrix = seo_data.corr()
# ヒートマップでの可視化
plt.figure(figsize=(10, 8))
sns.heatmap(correlation_matrix,
annot=True,
cmap='coolwarm',
center=0,
square=True,
fmt='.2f')
plt.title('SEO要素間の相関関係', fontsize=16, fontweight='bold')
plt.tight_layout()
plt.show()
# 強い相関関係の発見
print("強い正の相関 (r > 0.7):")
for i in range(len(correlation_matrix.columns)):
for j in range(i+1, len(correlation_matrix.columns)):
corr_value = correlation_matrix.iloc[i, j]
if abs(corr_value) > 0.7:
col1 = correlation_matrix.columns[i]
col2 = correlation_matrix.columns[j]
print(f"{col1} ↔ {col2}: {corr_value:.3f}")
自動レポート生成
分析結果を自動でレポート化する機能を実装します:
def generate_seo_report(df, output_file='seo_report.html'):
"""SEO分析レポートの自動生成"""
# 基本統計の計算
total_clicks = df['クリック数'].sum()
total_impressions = df['表示回数'].sum()
avg_ctr = (total_clicks / total_impressions * 100) if total_impressions > 0 else 0
avg_position = df['平均掲載順位'].mean()
# トップパフォーマンスクエリ
top_queries = df.nlargest(10, 'クリック数')[['検索クエリ', 'クリック数', '表示回数']]
# HTMLレポートの生成
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>SEO分析レポート</title>
<style>
body {{ font-family: Arial, sans-serif; margin: 40px; }}
.header {{ background-color: #2c3e50; color: white; padding: 20px; text-align: center; }}
.metrics {{ display: flex; justify-content: space-around; margin: 30px 0; }}
.metric-box {{ background-color: #f8f9fa; padding: 20px; border-radius: 8px; text-align: center; }}
.metric-value {{ font-size: 2em; font-weight: bold; color: #3498db; }}
table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
th, td {{ padding: 12px; text-align: left; border-bottom: 1px solid #ddd; }}
th {{ background-color: #34495e; color: white; }}
</style>
</head>
<body>
<div class="header">
<h1>SEO パフォーマンス レポート</h1>
<p>Generated on {pd.Timestamp.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<div class="metrics">
<div class="metric-box">
<div class="metric-value">{total_clicks:,}</div>
<div>総クリック数</div>
</div>
<div class="metric-box">
<div class="metric-value">{total_impressions:,}</div>
<div>総表示回数</div>
</div>
<div class="metric-box">
<div class="metric-value">{avg_ctr:.2f}%</div>
<div>平均CTR</div>
</div>
<div class="metric-box">
<div class="metric-value">{avg_position:.1f}</div>
<div>平均掲載順位</div>
</div>
</div>
<h2>トップ10検索クエリ</h2>
<table>
<tr>
<th>検索クエリ</th>
<th>クリック数</th>
<th>表示回数</th>
<th>CTR</th>
</tr>
"""
for _, row in top_queries.iterrows():
ctr = (row['クリック数'] / row['表示回数'] * 100) if row['表示回数'] > 0 else 0
html_content += f"""
<tr>
<td>{row['検索クエリ']}</td>
<td>{row['クリック数']:,}</td>
<td>{row['表示回数']:,}</td>
<td>{ctr:.2f}%</td>
</tr>
"""
html_content += """
</table>
</body>
</html>
"""
# ファイルに保存
with open(output_file, 'w', encoding='utf-8') as f:
f.write(html_content)
print(f"レポートが {output_file} に生成されました!")
# レポート生成の実行
generate_seo_report(df)
実践編4:検索順位追跡の自動化
SERPs APIを使用した順位取得
検索結果の順位を自動取得する仕組みを構築します:
import requests
import json
import time
def track_keyword_rankings(keywords, domain, location='Japan'):
"""キーワード順位の自動追跡"""
rankings_data = []
for keyword in keywords:
try:
# 実際のAPIキーが必要です(例:SerpAPI、DataForSEO等)
# この例は疑似コードです
params = {
'q': keyword,
'location': location,
'gl': 'jp',
'hl': 'ja'
}
# APIレスポンスをシミュレート
# 実際の実装では適切なAPIを使用してください
simulated_rank = random.randint(1, 100) if random.random() > 0.2 else None
ranking_info = {
'キーワード': keyword,
'ドメイン': domain,
'順位': simulated_rank,
'測定日': pd.Timestamp.now().date(),
'URL': f"https://{domain}/example-page"
}
rankings_data.append(ranking_info)
time.sleep(1) # API制限を考慮
except Exception as e:
print(f"エラー: {keyword} - {e}")
return pd.DataFrame(rankings_data)
# キーワードリストと対象ドメイン
target_keywords = [
'Python SEO', 'データ分析 マーケティング',
'Pandas チュートリアル', 'SEO 自動化'
]
target_domain = 'example.com'
# 順位追跡の実行
ranking_df = track_keyword_rankings(target_keywords, target_domain)
print("順位追跡結果:")
print(ranking_df)
順位変動アラートシステム
順位に大幅な変動があった際に自動で通知する機能:
def detect_ranking_changes(current_rankings, previous_rankings, threshold=5):
"""順位変動の検出とアラート"""
alerts = []
# データの結合
comparison = current_rankings.merge(
previous_rankings,
on='キーワード',
suffixes=('_現在', '_前回')
)
for _, row in comparison.iterrows():
if pd.notna(row['順位_現在']) and pd.notna(row['順位_前回']):
rank_change = row['順位_前回'] - row['順位_現在'] # 正の値は順位上昇
if abs(rank_change) >= threshold:
alert_type = "上昇" if rank_change > 0 else "下降"
alerts.append({
'キーワード': row['キーワード'],
'前回順位': row['順位_前回'],
'現在順位': row['順位_現在'],
'変動': rank_change,
'アラート種別': alert_type,
'重要度': 'HIGH' if abs(rank_change) >= 10 else 'MEDIUM'
})
return pd.DataFrame(alerts)
# アラートの実行例
alerts_df = detect_ranking_changes(ranking_df, ranking_df) # 実際は過去データと比較
print("順位変動アラート:")
print(alerts_df)
実践編5:コンテンツ最適化の自動提案
テキスト分析による改善提案
既存コンテンツを分析し、SEO改善提案を自動生成します:
import re
from textstat import flesch_reading_ease
from collections import Counter
def analyze_content_seo(text, target_keywords):
"""コンテンツのSEO分析と改善提案"""
analysis_results = {
'文字数': len(text),
'単語数': len(text.split()),
'段落数': len(text.split('\n\n')),
'読みやすさスコア': flesch_reading_ease(text)
}
# キーワード出現回数
keyword_analysis = {}
for keyword in target_keywords:
count = text.lower().count(keyword.lower())
density = (count / analysis_results['単語数'] * 100) if analysis_results['単語数'] > 0 else 0
keyword_analysis[keyword] = {
'出現回数': count,
'密度(%)': round(density, 2)
}
# 改善提案の生成
suggestions = []
if analysis_results['文字数'] < 1000:
suggestions.append("📝 コンテンツ量が少なすぎます。1,500文字以上を目指しましょう")
if analysis_results['読みやすさスコア'] < 50:
suggestions.append("📖 読みやすさが低いです。短い文章を心がけましょう")
for keyword, data in keyword_analysis.items():
if data['密度(%)'] < 0.5:
suggestions.append(f"🔍 '{keyword}'の密度が低いです。コンテンツ内での使用を増やしましょう")
elif data['密度(%)'] > 3.0:
suggestions.append(f"⚠️ '{keyword}'の密度が高すぎます。自然な文章を心がけましょう")
return {
'基本分析': analysis_results,
'キーワード分析': keyword_analysis,
'改善提案': suggestions
}
# コンテンツ分析の実行例
sample_content = """
PythonとPandasを使ったSEO分析について説明します。
データ分析はSEO改善に欠かせません。
Pandasライブラリを使えば効率的な分析が可能です。
"""
content_analysis = analyze_content_seo(sample_content, ['Python', 'Pandas', 'SEO', 'データ分析'])
print("コンテンツ分析結果:")
print(json.dumps(content_analysis, ensure_ascii=False, indent=2))
競合コンテンツとの差分分析
競合サイトのコンテンツと自社コンテンツを比較分析:
def compare_content_with_competitors(own_content, competitor_contents, keywords):
"""競合との差分分析"""
comparison_results = []
# 自社分析
own_analysis = analyze_content_seo(own_content, keywords)
# 競合分析
for i, competitor_content in enumerate(competitor_contents, 1):
comp_analysis = analyze_content_seo(competitor_content, keywords)
comparison = {
'競合サイト': f'競合{i}',
'文字数比較': comp_analysis['基本分析']['文字数'] - own_analysis['基本分析']['文字数'],
'読みやすさ比較': comp_analysis['基本分析']['読みやすさスコア'] - own_analysis['基本分析']['読みやすさスコア']
}
# キーワード密度の比較
for keyword in keywords:
own_density = own_analysis['キーワード分析'][keyword]['密度(%)']
comp_density = comp_analysis['キーワード分析'][keyword]['密度(%)']
comparison[f'{keyword}密度差'] = comp_density - own_density
comparison_results.append(comparison)
return pd.DataFrame(comparison_results)
# 競合比較の実行例
competitor_samples = [
"Python SEO分析の詳細なガイド。データ分析でSEOを改善...",
"Pandasを活用したマーケティング分析。効果的なSEO戦略..."
]
comparison_df = compare_content_with_competitors(
sample_content,
competitor_samples,
['Python', 'Pandas', 'SEO']
)
print("競合比較結果:")
print(comparison_df)
運用編:分析結果を活用したSEO戦略
データドリブンなキーワード戦略
分析データを基にした戦略的キーワード選定:
def strategic_keyword_analysis(gsc_data, competition_data):
"""戦略的キーワード分析"""
# 機会キーワードの特定(表示回数多、順位低、競合少)
opportunity_keywords = gsc_data[
(gsc_data['表示回数'] > gsc_data['表示回数'].median()) &
(gsc_data['平均掲載順位'] > 10) &
(gsc_data['平均掲載順位'] < 30)
].copy()
# 改善ポテンシャルの計算
opportunity_keywords['改善ポテンシャル'] = (
opportunity_keywords['表示回数'] *
(0.3 - opportunity_keywords['クリック数'] / opportunity_keywords['表示回数'])
).round(0)
# 優先度付け
opportunity_keywords['優先度'] = pd.cut(
opportunity_keywords['改善ポテンシャル'],
bins=[0, 100, 500, float('inf')],
labels=['低', '中', '高']
)
return opportunity_keywords.sort_values('改善ポテンシャル', ascending=False)
# 戦略分析の実行
strategic_keywords = strategic_keyword_analysis(df, competitor_df)
print("機会キーワード分析:")
print(strategic_keywords.head(10))
ROI計算とリソース配分
SEO施策のROIを計算し、効率的なリソース配分を提案:
def calculate_seo_roi(keyword_data, avg_conversion_rate=0.02, avg_order_value=5000):
"""SEO ROIの計算"""
roi_data = keyword_data.copy()
# 予想収益の計算
roi_data['月間予想訪問者'] = roi_data['表示回数'] * (roi_data['クリック数'] / roi_data['表示回数'])
roi_data['月間予想CV'] = roi_data['月間予想訪問者'] * avg_conversion_rate
roi_data['月間予想収益'] = roi_data['月間予想CV'] * avg_order_value
# 改善後の予想(順位を5位改善した場合)
improved_ctr = roi_data.apply(
lambda row: min(0.3, (row['クリック数'] / row['表示回数']) * 1.5),
axis=1
)
roi_data['改善後月間収益'] = roi_data['表示回数'] * improved_ctr * avg_conversion_rate * avg_order_value
roi_data['収益改善ポテンシャル'] = roi_data['改善後月間収益'] - roi_data['月間予想収益']
return roi_data.sort_values('収益改善ポテンシャル', ascending=False)
# ROI分析の実行
roi_analysis = calculate_seo_roi(strategic_keywords)
print("ROI分析結果:")
print(roi_analysis[['検索クエリ', '月間予想収益', '収益改善ポテンシャル']].head(10))
トラブルシューティング:よくある問題と解決策
パフォーマンスの最適化
大量データの処理を高速化するテクニック:
# メモリ効率の改善
def optimize_dataframe_memory(df):
"""データフレームのメモリ使用量を最適化"""
start_memory = df.memory_usage(deep=True).sum() / 1024**2
# 数値型の最適化
for col in df.select_dtypes(include=['int64']).columns:
df[col] = pd.to_numeric(df[col], downcast='integer')
for col in df.select_dtypes(include=['float64']).columns:
df[col] = pd.to_numeric(df[col], downcast='float')
# 文字列型の最適化
for col in df.select_dtypes(include=['object']).columns:
num_unique_values = len(df[col].unique())
num_total_values = len(df[col])
if num_unique_values / num_total_values < 0.5:
df[col] = df[col].astype('category')
end_memory = df.memory_usage(deep=True).sum() / 1024**2
print(f'メモリ使用量: {start_memory:.2f} MB → {end_memory:.2f} MB')
print(f'削減率: {100 * (start_memory - end_memory) / start_memory:.1f}%')
return df
# チャンクサイズを指定した大量データ処理
def process_large_csv(file_path, chunk_size=10000):
"""大量CSVファイルの効率的処理"""
results = []
for chunk in pd.read_csv(file_path, chunksize=chunk_size):
# チャンクごとの処理
processed_chunk = chunk.groupby('検索クエリ').agg({
'クリック数': 'sum',
'表示回数': 'sum'
}).reset_index()
results.append(processed_chunk)
# 結果を統合
final_result = pd.concat(results, ignore_index=True)
final_result = final_result.groupby('検索クエリ').agg({
'クリック数': 'sum',
'表示回数': 'sum'
}).reset_index()
return final_result
エラーハンドリングとログ機能
堅牢なSEO分析システムの構築:
import logging
from functools import wraps
# ログ設定
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('seo_analysis.log'),
logging.StreamHandler()
]
)
def error_handler(func):
"""エラーハンドリングデコレータ"""
@wraps(func)
def wrapper(*args, **kwargs):
try:
logging.info(f'{func.__name__} 開始')
result = func(*args, **kwargs)
logging.info(f'{func.__name__} 正常終了')
return result
except Exception as e:
logging.error(f'{func.__name__} でエラー: {str(e)}')
return None
return wrapper
@error_handler
def safe_data_processing(df):
"""安全なデータ処理"""
# データの前処理と検証
if df.empty:
raise ValueError("データフレームが空です")
required_columns = ['検索クエリ', 'クリック数', '表示回数']
missing_columns = [col for col in required_columns if col not in df.columns]
if missing_columns:
raise ValueError(f"必要な列が見つかりません: {missing_columns}")
# 異常値の検出と処理
df = df[df['クリック数'] >= 0] # 負の値を除去
df = df[df['表示回数'] >= df['クリック数']] # 論理的不整合を除去
logging.info(f"処理完了: {len(df)} 行のデータ")
return df
# 使用例
processed_data = safe_data_processing(df)
API制限への対応
外部APIを使用する際の制限対応:
import time
from datetime import datetime, timedelta
class APIRateLimiter:
"""API制限を管理するクラス"""
def __init__(self, max_requests=100, time_window=3600):
self.max_requests = max_requests
self.time_window = time_window # 秒
self.requests = []
def can_make_request(self):
"""リクエスト可能かチェック"""
now = datetime.now()
cutoff = now - timedelta(seconds=self.time_window)
# 古いリクエスト履歴を削除
self.requests = [req_time for req_time in self.requests if req_time > cutoff]
return len(self.requests) < self.max_requests
def make_request(self, func, *args, **kwargs):
"""制限付きでリクエストを実行"""
if not self.can_make_request():
wait_time = self.time_window - (datetime.now() - min(self.requests)).total_seconds()
print(f"API制限に達しました。{wait_time:.0f}秒待機します...")
time.sleep(wait_time + 1)
self.requests.append(datetime.now())
return func(*args, **kwargs)
# 使用例
rate_limiter = APIRateLimiter(max_requests=50, time_window=3600)
def api_call_with_limit(url):
"""制限付きAPI呼び出し"""
return rate_limiter.make_request(requests.get, url)
応用編:機械学習を活用したSEO予測
順位予測モデルの構築
過去のデータを使用して将来の順位を予測:
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
def build_ranking_prediction_model(historical_data):
"""順位予測モデルの構築"""
# 特徴量の作成
features_df = historical_data.copy()
# 移動平均の計算
features_df['クリック数_7日平均'] = features_df.groupby('検索クエリ')['クリック数'].rolling(7).mean().reset_index(0, drop=True)
features_df['表示回数_7日平均'] = features_df.groupby('検索クエリ')['表示回数'].rolling(7).mean().reset_index(0, drop=True)
# 前日差分
features_df['クリック数_前日差'] = features_df.groupby('検索クエリ')['クリック数'].diff()
features_df['表示回数_前日差'] = features_df.groupby('検索クエリ')['表示回数'].diff()
# CTRの計算
features_df['CTR'] = features_df['クリック数'] / features_df['表示回数']
features_df['CTR'] = features_df['CTR'].fillna(0)
# 欠損値処理
features_df = features_df.fillna(features_df.mean())
# 特徴量とターゲットの分離
feature_columns = ['クリック数_7日平均', '表示回数_7日平均', 'クリック数_前日差', '表示回数_前日差', 'CTR']
X = features_df[feature_columns]
y = features_df['平均掲載順位']
# 訓練・テストデータの分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# モデルの訓練
model = RandomForestRegressor(n_estimators=100, random_state=42)
model.fit(X_train, y_train)
# 予測と評価
y_pred = model.predict(X_test)
mae = mean_absolute_error(y_test, y_pred)
print(f"順位予測モデルの精度 (MAE): {mae:.2f}")
# 特徴量重要度の表示
feature_importance = pd.DataFrame({
'特徴量': feature_columns,
'重要度': model.feature_importances_
}).sort_values('重要度', ascending=False)
print("特徴量重要度:")
print(feature_importance)
return model
# サンプルデータでモデル構築(実際のデータに置き換えてください)
sample_historical_data = pd.DataFrame({
'検索クエリ': ['Python SEO'] * 30,
'クリック数': range(50, 80),
'表示回数': range(1000, 1300, 10),
'平均掲載順位': [15 + i*0.1 for i in range(30)]
})
ranking_model = build_ranking_prediction_model(sample_historical_data)
トレンド分析と季節性検出
検索トレンドの季節性を分析し、戦略立案に活用:
from scipy import stats
import numpy as np
def analyze_seasonal_trends(data, date_column='日付', metric_column='クリック数'):
"""季節性トレンド分析"""
# 日付データの準備
data[date_column] = pd.to_datetime(data[date_column])
data['月'] = data[date_column].dt.month
data['曜日'] = data[date_column].dt.dayofweek
data['四半期'] = data[date_column].dt.quarter
# 月別トレンド分析
monthly_trend = data.groupby('月')[metric_column].agg(['mean', 'std']).round(2)
monthly_trend['変動係数'] = (monthly_trend['std'] / monthly_trend['mean']).round(3)
# 曜日別トレンド分析
weekday_names = ['月', '火', '水', '木', '金', '土', '日']
weekly_trend = data.groupby('曜日')[metric_column].mean()
weekly_trend.index = weekday_names
# 季節性の統計的検定
seasonal_groups = [data[data['四半期'] == q][metric_column].values for q in range(1, 5)]
seasonal_test = stats.kruskal(*seasonal_groups)
results = {
'月別トレンド': monthly_trend,
'曜日別トレンド': weekly_trend.round(2),
'季節性検定': {
'統計量': seasonal_test.statistic,
'p値': seasonal_test.pvalue,
'季節性あり': seasonal_test.pvalue < 0.05
}
}
# 可視化
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
# 月別トレンド
axes[0, 0].bar(monthly_trend.index, monthly_trend['mean'])
axes[0, 0].set_title('月別平均パフォーマンス')
axes[0, 0].set_xlabel('月')
axes[0, 0].set_ylabel(metric_column)
# 曜日別トレンド
axes[0, 1].bar(range(7), weekly_trend.values)
axes[0, 1].set_title('曜日別平均パフォーマンス')
axes[0, 1].set_xlabel('曜日')
axes[0, 1].set_xticks(range(7))
axes[0, 1].set_xticklabels(weekday_names)
# 時系列トレンド
daily_data = data.groupby(date_column)[metric_column].sum()
axes[1, 0].plot(daily_data.index, daily_data.values)
axes[1, 0].set_title('時系列トレンド')
axes[1, 0].tick_params(axis='x', rotation=45)
# 四半期別ボックスプロット
quarterly_data = [data[data['四半期'] == q][metric_column].values for q in range(1, 5)]
axes[1, 1].boxplot(quarterly_data, labels=['Q1', 'Q2', 'Q3', 'Q4'])
axes[1, 1].set_title('四半期別分布')
plt.tight_layout()
plt.show()
return results
# 季節性分析の実行
seasonal_analysis = analyze_seasonal_trends(df)
print("季節性分析結果:")
print(f"季節性の存在: {seasonal_analysis['季節性検定']['季節性あり']}")
自動化とスケジューリング
タスクスケジューラーの実装
定期的なSEO分析を自動実行:
import schedule
import threading
from datetime import datetime
class SEOAutomationScheduler:
"""SEO分析の自動スケジューラー"""
def __init__(self):
self.is_running = False
self.thread = None
def daily_analysis(self):
"""日次分析タスク"""
try:
print(f"[{datetime.now()}] 日次SEO分析を開始...")
# GSCデータの取得・分析
# data = fetch_gsc_data() # 実装が必要
# analysis = analyze_seo_performance(data)
# レポート生成
# generate_daily_report(analysis)
print("日次分析完了")
except Exception as e:
print(f"日次分析エラー: {e}")
def weekly_analysis(self):
"""週次分析タスク"""
try:
print(f"[{datetime.now()}] 週次SEO分析を開始...")
# 週次レポート生成
# generate_weekly_report()
# 順位変動アラート
# check_ranking_changes()
print("週次分析完了")
except Exception as e:
print(f"週次分析エラー: {e}")
def monthly_analysis(self):
"""月次分析タスク"""
try:
print(f"[{datetime.now()}] 月次SEO分析を開始...")
# 月次総合レポート
# generate_monthly_report()
# 戦略見直し提案
# generate_strategy_recommendations()
print("月次分析完了")
except Exception as e:
print(f"月次分析エラー: {e}")
def setup_schedule(self):
"""スケジュール設定"""
# 毎日午前9時に実行
schedule.every().day.at("09:00").do(self.daily_analysis)
# 毎週月曜日午前10時に実行
schedule.every().monday.at("10:00").do(self.weekly_analysis)
# 毎月1日午前11時に実行
schedule.every().day.at("11:00").do(
lambda: self.monthly_analysis() if datetime.now().day == 1 else None
)
def run_scheduler(self):
"""スケジューラーの実行"""
self.setup_schedule()
while self.is_running:
schedule.run_pending()
time.sleep(60) # 1分間隔でチェック
def start(self):
"""スケジューラー開始"""
if not self.is_running:
self.is_running = True
self.thread = threading.Thread(target=self.run_scheduler)
self.thread.daemon = True
self.thread.start()
print("SEO自動分析スケジューラーを開始しました")
def stop(self):
"""スケジューラー停止"""
self.is_running = False
if self.thread:
self.thread.join()
print("SEO自動分析スケジューラーを停止しました")
# スケジューラーの使用例
scheduler = SEOAutomationScheduler()
scheduler.start()
# 手動でのタスク実行例
scheduler.daily_analysis()
Slack/メール通知システム
分析結果を自動でチームに共有:
import smtplib
import requests
import json
from email.mime.text import MimeText
from email.mime.multipart import MimeMultipart
class NotificationSystem:
"""通知システム"""
def __init__(self, slack_webhook_url=None, email_config=None):
self.slack_webhook_url = slack_webhook_url
self.email_config = email_config
def send_slack_notification(self, message, title="SEO Alert"):
"""Slack通知送信"""
if not self.slack_webhook_url:
return False
try:
payload = {
"text": title,
"attachments": [
{
"color": "warning" if "Alert" in title else "good",
"fields": [
{
"title": title,
"value": message,
"short": False
}
]
}
]
}
response = requests.post(
self.slack_webhook_url,
data=json.dumps(payload),
headers={'Content-Type': 'application/json'}
)
return response.status_code == 200
except Exception as e:
print(f"Slack通知エラー: {e}")
return False
def send_email_notification(self, subject, body, recipients):
"""メール通知送信"""
if not self.email_config:
return False
try:
msg = MimeMultipart()
msg['From'] = self.email_config['from_email']
msg['Subject'] = subject
msg.attach(MimeText(body, 'html'))
server = smtplib.SMTP(self.email_config['smtp_server'], self.email_config['port'])
server.starttls()
server.login(self.email_config['username'], self.email_config['password'])
for recipient in recipients:
msg['To'] = recipient
server.send_message(msg)
del msg['To']
server.quit()
return True
except Exception as e:
print(f"メール送信エラー: {e}")
return False
def send_ranking_alert(self, alerts_df):
"""順位変動アラートの送信"""
if alerts_df.empty:
return
# Slack通知
slack_message = "🚨 **順位変動アラート**\n"
for _, alert in alerts_df.head(5).iterrows():
status = "📈" if alert['変動'] > 0 else "📉"
slack_message += f"{status} {alert['キーワード']}: {alert['前回順位']}位 → {alert['現在順位']}位\n"
self.send_slack_notification(slack_message, "SEO順位変動アラート")
# メール通知
email_body = f"""
<h2>SEO順位変動レポート</h2>
<p>以下のキーワードで大幅な順位変動が検出されました:</p>
<table border="1" style="border-collapse: collapse;">
<tr>
<th>キーワード</th>
<th>前回順位</th>
<th>現在順位</th>
<th>変動</th>
<th>重要度</th>
</tr>
"""
for _, alert in alerts_df.iterrows():
email_body += f"""
<tr>
<td>{alert['キーワード']}</td>
<td>{alert['前回順位']}</td>
<td>{alert['現在順位']}</td>
<td>{alert['変動']:+d}</td>
<td>{alert['重要度']}</td>
</tr>
"""
email_body += "</table>"
self.send_email_notification(
"SEO順位変動アラート",
email_body,
["seo-team@company.com"]
)
# 通知システムの設定例
notification_system = NotificationSystem(
slack_webhook_url="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK",
email_config={
'smtp_server': 'smtp.gmail.com',
'port': 587,
'username': 'your-email@gmail.com',
'password': 'your-password',
'from_email': 'seo-alerts@company.com'
}
)
まとめ:SEO分析の未来は自動化にあり
導入によって実現できること
- 作業時間の大幅短縮: 月20時間以上の時間節約
- 分析精度の向上: 人的ミスの排除と深い洞察の発見
- リアルタイム監視: 24時間365日の自動監視体制
- データドリブン意思決定: 感覚に頼らない戦略立案
- スケーラブルな運用: チーム規模に関係なく高品質な分析
次のステップ
このチュートリアルを実践した後は、以下の発展的な取り組みにチャレンジしてみてください:
- Google Analytics APIとの連携で総合的な分析システム構築
- 機械学習モデルの精度向上と予測範囲の拡張
- A/Bテスト自動化によるコンテンツ最適化
- 競合分析の高度化(価格監視、コンテンツ更新追跡等)
- 音声検索対応の分析項目追加
最後に:継続的改善の重要性
SEO分析の自動化は一度構築して終わりではありません。検索エンジンのアルゴリズム変更、市場の変化、ビジネス目標の変更に応じて、継続的に分析手法を改善していくことが重要です。
Python + Pandasの組み合わせは、この変化に柔軟に対応できる強力なツールセットです。今日から始めて、あなたのSEO業務を次のレベルに押し上げましょう!
この記事があなたのSEO分析業務の効率化に少しでも役立てば幸いです。質問やフィードバックがございましたら、ぜひコメント欄でお聞かせください!
コメント