SEO記事を自動生成するワークフローを構築した
SEO記事を自動生成するワークフローを構築した
はじめに
SEO記事を手動で書くのは時間がかかります。競合分析、キーワード選定、構成作成、本文執筆、校正…すべてを人力でやると1記事に数時間かかることも。
そこで、AIを使って競合分析から記事生成まで自動化するワークフローを構築しました。今回は「派遣の教育訓練は意味ない?」という記事を題材に、実際のフローを紹介します。
システム概要
ワークフロー(全10工程)
工程1: 競合記事取得(web_fetch)
↓
工程2: CSV読み込み・初期化
↓
工程3A: クエリ分析(検索意図)
↓
工程3B: 共起語抽出
↓
工程3C: 競合分析
↓
工程3.5: 人間味・感情生成
↓
工程4: 戦略的アウトライン
↓
工程5: 一次情報追加(厚労省データなど)
↓
工程6: アウトライン強化
↓
工程6.5: 構成案作成
↓
工程7A: 本文生成(初稿)
↓
工程7B: 本文ブラッシュアップ
↓
工程8: ファクトチェック + FAQ生成
↓
工程9: 最終リライト
↓
工程10: 最終出力(Markdown + HTML)
技術スタック
- Python 3.11 – メインロジック
- Claude 3.5 Sonnet – 記事生成AI
- web_fetch – 競合記事取得
- OpenClaw – ワークフロー管理
- Markdown – 中間フォーマット
- WordPress – 最終出力先
実装のポイント
1. 競合記事の自動取得
上位10サイトの本文を自動で取得:
# 工程1: 競合記事取得
import csv
import requests
def fetch_competitor_articles(keyword: str) -> list[dict]:
"""
検索上位10サイトの記事を取得
Returns:
[
{"url": "...", "title": "...", "content": "..."},
...
]
"""
# Google検索で上位10件のURLを取得(手動 or API)
urls = get_top_10_urls(keyword)
articles = []
for url in urls:
try:
# web_fetchで本文を抽出
response = requests.post(
'http://localhost:8765/api/web_fetch',
json={'url': url, 'extractMode': 'markdown'}
)
content = response.json()['content']
articles.append({
'url': url,
'title': extract_title(content),
'content': content
})
except Exception as e:
print(f"Failed to fetch {url}: {e}")
return articles
def save_to_csv(articles: list[dict], output_path: str):
"""CSVに保存"""
with open(output_path, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['url', 'title', 'content'])
writer.writeheader()
writer.writerows(articles)
2. 競合分析
AIで競合記事を分析し、差別化ポイントを抽出:
# 工程3C: 競合分析
import anthropic
async def analyze_competitors(articles: list[dict]) -> dict:
"""
競合記事を分析
Returns:
{
"common_topics": ["topic1", "topic2", ...],
"missing_topics": ["topic1", "topic2", ...],
"unique_angles": ["angle1", "angle2", ...],
"tone": "共感型 / 解決策提示型 / 比較検証型"
}
"""
client = anthropic.Anthropic(api_key=os.getenv('ANTHROPIC_API_KEY'))
# 競合記事の要約を作成
summaries = []
for article in articles:
summary = await summarize_article(client, article['content'])
summaries.append(summary)
# 分析プロンプト
prompt = f"""
以下は「派遣 教育訓練 意味ない」で検索上位10サイトの要約です。
{format_summaries(summaries)}
これらを分析して、以下を抽出してください:
1. 共通トピック(すべての記事で触れられている内容)
2. 欠けているトピック(誰も触れていない視点)
3. ユニークな切り口(差別化できるアプローチ)
4. 全体のトーン(共感型 / 解決策提示型 / 比較検証型)
JSON形式で出力してください。
"""
message = await client.messages.create(
model='claude-3-5-sonnet-20241022',
max_tokens=2000,
messages=[{'role': 'user', 'content': prompt}]
)
return json.loads(message.content[0].text)
3. 人間味・感情の生成
機械的な文章にならないよう、感情や共感を追加:
# 工程3.5: 人間味・感情生成
async def generate_empathy(topic: str, target_audience: str) -> dict:
"""
共感フレーズを生成
Returns:
{
"opening": "あなたは悪くない。派遣の教育訓練が形骸化しているのは...",
"transitions": ["でも、諦めないでほしい。", "実は、..."],
"closing": "一緒に、この状況を変えていきましょう。"
}
"""
prompt = f"""
ターゲット: {target_audience}
トピック: {topic}
このターゲットに向けて、共感と寄り添いのフレーズを作成してください。
- 冒頭: 「あなたは悪くない」と肯定
- 中間: スムーズな遷移フレーズ
- 結び: 希望を持たせる言葉
説教臭くせず、同じ目線で語るトーンで。
"""
# Claude APIで生成
# ...
4. 一次情報の自動追加
厚労省などの公的データを自動で引用:
# 工程5: 一次情報追加
def add_primary_sources(outline: dict) -> dict:
"""
一次情報を追加
例:
- 厚労省の統計データ
- 行政のガイドライン
- 裁判例
"""
sources = {
"派遣教育訓練": [
{
"url": "https://www.mhlw.go.jp/...",
"title": "派遣労働者のキャリアアップ教育訓練(厚労省)",
"data": "受講率: 39.7%(令和4年度調査)"
},
{
"url": "https://jsite.mhlw.go.jp/...",
"title": "キャリアアップ助成金(石川労働局)",
"data": "助成額: 1人当たり最大47,000円"
}
]
}
# アウトラインに挿入
for section in outline['sections']:
if '実態' in section['title']:
section['sources'] = sources['派遣教育訓練']
return outline
5. FAQ自動生成
記事から自動でFAQを作成:
# 工程8: FAQ生成
async def generate_faq(article_content: str) -> list[dict]:
"""
記事からFAQを生成
Returns:
[
{"question": "派遣の教育訓練は本当に意味がない?", "answer": "..."},
{"question": "受講率が低い理由は?", "answer": "..."},
...
]
"""
prompt = f"""
以下の記事から、読者が持ちそうな疑問を5つ抽出し、FAQを作成してください。
【記事】
{article_content}
【出力形式】
Q1: (質問)
A1: (回答)
Q2: (質問)
A2: (回答)
...
"""
# Claude APIで生成
# ...
6. WordPress用HTMLの生成
最終的にWordPress用のHTMLを出力:
# 工程10: WordPress用HTML生成
def convert_to_wordpress_html(markdown: str) -> str:
"""
MarkdownをWordPress用HTMLに変換
- 画像: プレースホルダー挿入
- 脚注: 双方向リンク
- 内部リンク: プレースホルダー
"""
html = markdown_to_html(markdown)
# 画像プレースホルダー
html = re.sub(
r'!\[([^\]]+)\]\([^\)]+\)',
r'#IMAGE_PLACEHOLDER_\1',
html
)
# 脚注
html = add_footnote_links(html)
# 内部リンク
html = re.sub(
r'\[([^\]]+)\]\(#child-article-(\d+)\)',
r'<a href="#CHILD_ARTICLE_\2_URL">\1</a>',
html
)
return html
def add_footnote_links(html: str) -> str:
"""双方向リンクの脚注を追加"""
# [1] → <sup id="ref1"><a href="#cite-1">[1]</a></sup>
html = re.sub(
r'\[(\d+)\]',
r'<sup id="ref\1"><a href="#cite-\1">[\1]</a></sup>',
html
)
return html
実際の成果物
例: 「派遣の教育訓練は意味ない?」記事
- ターゲット文字数: 4,000字
- 実際の文字数: 4,200字
- 生成時間: 約30分(全10工程)
- 一次情報: 厚労省データ3件引用
- FAQ: 5問生成
- 内部リンク: 子記事3本へのリンク
構成
1. 導入(共感)
- あなたは悪くない
- 形骸化している実態
2. 派遣教育訓練の実態
- 受講率10%以下
- なぜ受講しないのか
3. 3つの構造的問題
- 無関係なコンテンツ
- キャリア関心の欠如
- 形式的な研修
4. 解決策
- 実践的コース
- 多言語対応
- 自動リマインダー
5. 事例紹介
- CrossLearning導入事例
6. まとめ
- 一緒に変えていこう
7. FAQ(5問)
8. 参考文献(脚注)
開発で工夫した点
1. トピッククラスター戦略
親記事(ハブ)+ 子記事3本の構成:
- 親記事: 「派遣の教育訓練は意味ない?」(概要)
- 子記事1: 「派遣スタッフが受講しない3つの理由」
- 子記事2: 「教育訓練担当者の本音」
- 子記事3: 「外部委託ソリューション」
内部リンクで相互に結びつけ、SEO効果を高める。
2. 感情スコアリング
機械的な文章を避けるため、感情スコアを測定:
def calculate_emotion_score(text: str) -> float:
"""
感情スコア(0〜1)
- ポジティブワード: 0.1点
- 共感フレーズ: 0.2点
- 一人称: 0.1点
"""
score = 0.0
positive_words = ['やりがい', '成長', '希望', '一緒に']
empathy_phrases = ['あなたは悪くない', '分かります', '気持ちは理解できます']
first_person = ['私たち', 'あなた']
for word in positive_words:
if word in text:
score += 0.1
for phrase in empathy_phrases:
if phrase in text:
score += 0.2
for pronoun in first_person:
if pronoun in text:
score += 0.1
return min(score, 1.0)
3. サブエージェント実行
OpenClawのサブエージェント機能で、バックグラウンド実行:
# OpenClawでサブエージェント起動
def start_subagent_workflow(keyword: str):
"""
サブエージェントでワークフローを実行
メインセッションを占有せず、バックグラウンドで処理
"""
import subprocess
subprocess.run([
'openclaw', 'sessions_spawn',
'--task', f'SEO記事生成ワークフロー: {keyword}',
'--agent', 'main',
'--cleanup', 'keep'
])
改善点・今後の展望
現状の課題
-
❌ ファクトチェックが甘い
→ AIが生成した内容に誤りがある可能性 -
❌ 画像生成は手動
→ プレースホルダーを手動で画像に置換 -
❌ 多言語対応なし
→ 日本語のみ
今後の改善
-
✅ ファクトチェック強化
→ 引用元URLを必須化 -
✅ 画像自動生成
→ DALL-E / Midjourney連携 -
✅ 多言語対応
→ 英語・中国語版を自動生成
まとめ
SEO記事の自動生成で得られた効果:
✅ 作業時間を90%削減
→ 手動5時間 → 自動30分
✅ 一次情報の引用率向上
→ 厚労省データを自動で追加
✅ 内部リンク戦略
→ トピッククラスターで相互リンク
✅ 感情・共感の表現
→ 機械的にならない工夫
開発期間: 約2週間
生成記事数: 10本以上
平均生成時間: 30分/記事
AIを使えばSEO記事の量産が可能です。ただし、ファクトチェックと人間味の追加は必須。完全自動化ではなく、「人間 + AI」の協働が理想です!