Level 7 実践: トレンドからブログネタを自動収集する
このチュートリアルについて
Section titled “このチュートリアルについて”概念・仕組みを先に理解したい方は Level 7 の概念ガイド を参照してください。
Puppeteer MCP(Model Context Protocol サーバー)を Claude Code に追加し、ブラウザ操作でトレンド記事を収集してブログ記事を自動生成します。ネタ探しにかかる時間をゼロに近づけられます。
対象読者: Level 6 まで完了しており、/blog スラッシュコマンドが動作している方。Node.js の基本操作に慣れていると理解しやすいです。
学習時間の目安: 読了 10分 + 実践 40分
前のレベルからの引き継ぎ
Section titled “前のレベルからの引き継ぎ”Level 6 で PR 自動レビューが完成した。ブログ記事は /blog コマンドで量産できるが、ネタ探しに時間がかかっており、自動化が課題になっている。
Step 1: Puppeteer MCP を設定する
Section titled “Step 1: Puppeteer MCP を設定する”.claude/settings.json に Puppeteer MCP サーバーを追加します。既存の GitHub MCP と並べた完全な設定を示します。
{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}"
}
},
"puppeteer": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-puppeteer"],
"env": {
"PUPPETEER_LAUNCH_OPTIONS": "{\"headless\": true, \"args\": [\"--no-sandbox\"]}"
}
}
},
"permissions": {
"allow": [
"mcp:github:*",
"mcp:puppeteer:*"
]
}
}@modelcontextprotocol/server-puppeteer は npx 実行時に自動でダウンロードされます。事前インストールは不要です。
Claude Code を再起動して MCP サーバーが読み込まれたことを確認します。
# 一度終了して再起動
exit
claude起動時のログに puppeteer MCP server started のような表示が出れば成功です。
Step 2: ブラウザ操作でトレンドを収集する
Section titled “Step 2: ブラウザ操作でトレンドを収集する”Claude Code の対話モードで以下を順番に入力します。
Step 2-1: トレンド記事の収集
> dev.to/t/typescript のページを開いて今週のトレンド記事タイトルを10件取得してください。Claude は Puppeteer MCP を使ってブラウザを起動し、ページのテキストコンテンツを解析します。出力例:
dev.to/t/typescript を開きました。
今週のトレンド記事(10件):
1. "TypeScript 5.5's Inferred Type Predicates Explained"
2. "Stop Using `any`: A Practical Guide to Unknown"
3. "Zod v4 Released: What's New for Runtime Validation"
4. "Branded Types in TypeScript for Domain Modeling"
5. "TypeScript Decorators Are Finally Stable"
6. "React 19 and TypeScript: New Hooks Typing Patterns"
7. "Building Type-Safe APIs with tRPC and Next.js 14"
8. "Why I Switched from Prisma to Drizzle ORM"
9. "TypeScript Performance Tips for Large Codebases"
10. "Satisfies Operator: When to Use It Over As"Step 2-2: my-portfolio 向けの優先度付け
> my-portfolio のブログ(Next.js・TypeScript 中心)でカバーすべき上位3件を選んでください。Claude の出力例:
my-portfolio のブログ方針(Next.js・TypeScript 中心)に基づいて選定しました。
## 優先度高(上位3件)
1. **"TypeScript 5.5's Inferred Type Predicates Explained"**
理由: TypeScript の新機能で、Next.js プロジェクトにすぐ適用できる。
想定タイトル案: 「TypeScript 5.5 の型述語推論で if 文が変わる」
2. **"React 19 and TypeScript: New Hooks Typing Patterns"**
理由: React と TypeScript の組み合わせは my-portfolio の中心トピック。
想定タイトル案: 「React 19 の新フックを TypeScript で型安全に使う」
3. **"Building Type-Safe APIs with tRPC and Next.js 14"**
理由: Next.js 14 + TypeScript という my-portfolio の技術スタックに直結。
想定タイトル案: 「tRPC + Next.js 14 で型安全フルスタック開発」
## 優先度低(今回はスキップ)
- "Zod v4 Released": 有用だが my-portfolio の現在の記事と重複する可能性がある
- "Why I Switched from Prisma to Drizzle ORM": データベース系で現在のブログ方針と外れるStep 3: 自動収集・記事生成スクリプトの作成
Section titled “Step 3: 自動収集・記事生成スクリプトの作成”収集からブログ記事生成までを一括実行する scripts/collect_and_blog.sh を作成します。
#!/usr/bin/env bash
# scripts/collect_and_blog.sh
# dev.to のトレンドを収集して /blog コマンドで記事を自動生成する
set -euo pipefail
BLOG_DIR="src/content/posts"
LOG_FILE="logs/collect_and_blog_$(date +%Y%m%d_%H%M%S).log"
mkdir -p logs
echo "=== ブログネタ自動収集・生成 ===" | tee "${LOG_FILE}"
echo "開始時刻: $(date)" | tee -a "${LOG_FILE}"
echo "" | tee -a "${LOG_FILE}"
# Step 1: Puppeteer MCP でトレンドを収集して JSON で出力
echo "Step 1: dev.to のトレンドを収集しています..." | tee -a "${LOG_FILE}"
TREND_PROMPT="dev.to/t/typescript を開いて今週のトレンド記事を10件取得し、
my-portfolio(Next.js・TypeScript 中心のブログ)向けに優先度の高い上位3件を選んでください。
出力は以下の JSON 形式のみで返してください(説明文は不要):
{
\"articles\": [
{\"rank\": 1, \"title\": \"元記事タイトル\", \"blog_title\": \"日本語の記事タイトル案\", \"reason\": \"選定理由\"},
{\"rank\": 2, \"title\": \"元記事タイトル\", \"blog_title\": \"日本語の記事タイトル案\", \"reason\": \"選定理由\"},
{\"rank\": 3, \"title\": \"元記事タイトル\", \"blog_title\": \"日本語の記事タイトル案\", \"reason\": \"選定理由\"}
]
}"
TREND_JSON=$(echo "${TREND_PROMPT}" | claude --print --dangerously-skip-permissions)
echo "収集結果:" | tee -a "${LOG_FILE}"
echo "${TREND_JSON}" | tee -a "${LOG_FILE}"
echo "" | tee -a "${LOG_FILE}"
# Step 2: JSON をパースして各記事を生成
echo "Step 2: ブログ記事を生成しています..." | tee -a "${LOG_FILE}"
# jq で記事タイトルを抽出してループ
TITLES=$(echo "${TREND_JSON}" | jq -r '.articles[].blog_title' 2>/dev/null || echo "")
if [ -z "${TITLES}" ]; then
echo "ERROR: JSON のパースに失敗しました。ログを確認してください。" | tee -a "${LOG_FILE}"
exit 1
fi
COUNT=0
while IFS= read -r TITLE; do
COUNT=$((COUNT + 1))
echo " 記事 ${COUNT}: ${TITLE}" | tee -a "${LOG_FILE}"
BLOG_PROMPT="/blog ${TITLE}"
echo "${BLOG_PROMPT}" | claude --print --dangerously-skip-permissions >> "${LOG_FILE}" 2>&1
echo " 完了" | tee -a "${LOG_FILE}"
echo "" | tee -a "${LOG_FILE}"
# API レート制限を避けるため少し待つ
sleep 3
done <<< "${TITLES}"
echo "=== 完了 ===" | tee -a "${LOG_FILE}"
echo "生成された記事数: ${COUNT}" | tee -a "${LOG_FILE}"
echo "ログ: ${LOG_FILE}" | tee -a "${LOG_FILE}"実行権限を付与して実行します。
chmod +x scripts/collect_and_blog.sh
./scripts/collect_and_blog.sh実行完了後、src/content/posts/ に新しい記事ファイルが生成されていることを確認します。
ls -lt src/content/posts/ | head -5Step 4: 生成結果の確認
Section titled “Step 4: 生成結果の確認”# 最新の記事を確認
LATEST=$(ls -t src/content/posts/*.md 2>/dev/null | head -1)
if [ -n "${LATEST}" ]; then
head -20 "${LATEST}"
fiローカルで確認します。
npm run dev
# http://localhost:4321/blog でブログ一覧を確認スクレイピングを行う前に以下を確認してください。
| 確認項目 | 内容 |
|---|---|
| robots.txt | dev.to/robots.txt でクローリングが許可されているパスを確認する |
| 利用規約 | サービスの利用規約でスクレイピングの可否を確認する |
| アクセス頻度 | 短時間に大量のリクエストを送らない(スクリプト内の sleep 3 は最低限の配慮) |
| 著作権 | 収集した記事タイトルはアイデアの参考にとどめ、内容はオリジナルで書く |
Puppeteer MCP はブラウザを直接操作するため、robots.txt での制限を技術的にはバイパスできますが、サービスの利用規約を遵守することが求められます。
この時点の my-portfolio
Section titled “この時点の my-portfolio”.claude/settings.jsonに Puppeteer MCP サーバーが追加されたscripts/collect_and_blog.shが追加され、トレンド収集から記事生成までを一括実行できるlogs/ディレクトリに実行ログが保存されるsrc/content/posts/にトレンドベースのブログ記事が自動生成された
my-portfolio/
├── .claude/
│ └── settings.json # ← Puppeteer MCP を追加
├── scripts/
│ ├── review_pr.sh
│ └── collect_and_blog.sh # ← 新規追加
├── logs/ # ← 新規追加
│ └── collect_and_blog_*.log
├── src/
│ └── content/
│ └── posts/
│ ├── typescript-type-inference.md # 既存
│ ├── nextjs-app-router.md # 既存
│ ├── react-useeffect.md # 既存
│ └── typescript-55-inferred-*.md # ← 新規生成
└── ...