技術ブログの執筆と投稿は、継続的な情報発信において重要な活動である。しかし、記事作成から投稿までの手作業は時間がかかり、効率化の余地がある。
本記事では、Markdown で記事を作成し、Google Blogger API を通じて自動投稿するスクリプトの作成方法を詳しく解説する。OAuth2 認証と環境変数管理に焦点を当てた実践的な実装例を紹介する。
システム要件
前提条件
- Node.js 16 以上
- Google アカウント
- Blogger ブログ
目標とする機能
- Markdown による記事作成
- OAuth2 による安全な認証
- 環境変数による設定管理
- Google Blogger への自動投稿
アーキテクチャ設計
ディレクトリ構成
├── articles/
│ ├── drafts/ # 作成中の記事
│ ├── review/ # レビュー中の記事
│ ├── published/ # 投稿済みの記事
│ └── templates/ # 記事テンプレート
├── scripts/
│ ├── auth.js # OAuth2 認証
│ ├── new-article.js # 記事作成
│ ├── build.js # HTML ビルド
│ ├── publish-draft.js # Blogger 投稿
│ └── update-article.js # 記事更新
└── blogger-config.json # API 設定ファイル
技術スタック
- 言語: JavaScript (Node.js)
- 認証: Google OAuth2
- ビルド: Markdown-it + MathJax + Prism.js
- 設定管理: 環境変数 + JSON
Google Cloud Console の設定
1. プロジェクトの作成と API 有効化
# Google Cloud Console での手順:
# 1. 新しいプロジェクトを作成
# 2. Blogger API を有効化
# 3. OAuth 2.0 認証情報を作成(デスクトップアプリケーション)
2. OAuth2 認証情報の取得
重要な設定項目:
- アプリケーションの種類: デスクトップアプリケーション
- リダイレクトURI:
urn:ietf:wg:oauth:2.0:oob - スコープ:
https://www.googleapis.com/auth/blogger
認証システムの実装
OAuth2 認証スクリプト
// scripts/auth.js の主要部分
const { google } = require('googleapis');
async function authenticateUser(config) {
const { clientId, clientSecret, redirectUri } = config.blogger;
const oAuth2Client = new google.auth.OAuth2(
clientId,
clientSecret,
redirectUri
);
// 認証URLを生成
const authUrl = oAuth2Client.generateAuthUrl({
access_type: 'offline',
scope: [
'https://www.googleapis.com/auth/blogger',
'https://www.googleapis.com/auth/blogger.readonly'
]
});
console.log('認証URL:', authUrl);
// 認証コードを取得(ユーザー入力)
const authCode = await getUserInput();
// トークンを取得・保存
const { tokens } = await oAuth2Client.getToken(authCode);
await fs.writeJson('blogger-token.json', tokens);
return oAuth2Client;
}
環境変数管理
// blogger-config.json
{
"blogger": {
"blogId": "${BLOGGER_BLOG_ID}",
"clientId": "${BLOGGER_CLIENT_ID}",
"clientSecret": "${BLOGGER_CLIENT_SECRET}",
"redirectUri": "urn:ietf:wg:oauth:2.0:oob"
}
}
// 環境変数展開機能
function expandEnvironmentVariables(obj) {
if (typeof obj === 'string') {
return obj.replace(/\$\{([^}]+)\}/g, (match, envVar) => {
return process.env[envVar] || match;
});
}
// 再帰的に処理...
}
記事管理システム
記事作成スクリプト
// scripts/new-article.js の核心部分
function generateFileName(title, date) {
const dateStr = moment(date).format('YYYYMMDD');
const titleSlug = title
.toLowerCase()
.replace(/[^a-z0-9ひらがなカタカナ漢字]/g, '_')
.replace(/_+/g, '_')
.replace(/^_|_$/g, '');
return `${dateStr}_${titleSlug}.md`;
}
function generateArticleContent(options) {
return `---
title: "${options.title}"
date: "${options.date}"
tags: [${options.tags.map(tag => `"${tag}"`).join(', ')}]
description: "${options.description}"
---
# ${options.title}
## 概要
${options.description}
## 内容
### セクション1
内容を書く
`;
}
ビルドシステム
// scripts/build.js - Markdown から HTML への変換
const MarkdownIt = require('markdown-it');
const markdownItKatex = require('markdown-it-katex');
const Prism = require('prismjs');
const md = new MarkdownIt({
html: true,
breaks: true,
linkify: true,
highlight: function (str, lang) {
if (lang && Prism.languages[lang]) {
return Prism.highlight(str, Prism.languages[lang], lang);
}
return '';
}
});
md.use(markdownItKatex); // 数式対応
Blogger API 連携
記事投稿機能
// scripts/publish-draft.js の主要部分
async function publishToBlogger(auth, blogId, title, content, labels = []) {
const blogger = google.blogger({ version: 'v3', auth });
const post = {
kind: 'blogger#post',
title: title,
content: content,
labels: labels
};
const response = await blogger.posts.insert({
blogId: blogId,
resource: post,
isDraft: true // 安全のため下書きとして投稿
});
return response.data;
}
エラーハンドリング
// 認証エラーの詳細処理
catch (error) {
if (error.message.includes('access_denied')) {
console.log('OAuth 同意画面でテストユーザーを追加してください');
} else if (error.message.includes('invalid_client')) {
console.log('クライアントIDとシークレットを確認してください');
}
}
セキュリティ考慮事項
機密情報の管理
# .gitignore に追加すべき項目
blogger-token.json
*.local.json
.env
.env.local
環境変数の設定
# 本番環境での設定例
export BLOGGER_CLIENT_ID="your-client-id"
export BLOGGER_CLIENT_SECRET="your-client-secret"
export BLOGGER_BLOG_ID="your-blog-id"
使用方法
基本的な使い方
# 1. 認証設定(初回のみ)
node scripts/auth.js
# 2. 記事をMarkdownで作成(手動)
# articles/drafts/your-article.md
# 3. HTML にビルド
node scripts/build.js your-article.md
# 4. Blogger に下書きとして投稿
node scripts/publish-draft.js your-article.md
# 5. 既存記事の更新
node scripts/update-article.js your-article.md
便利なコマンド
# 記事一覧の表示
node scripts/publish-draft.js --list
# ヘルプ表示
node scripts/publish-draft.js --help
トラブルシューティング
OAuth 認証エラー
問題: "アクセスをブロック: 認証エラーである"
解決: Google Cloud Console でテストユーザーを追加
性能と拡張性
最適化のポイント
- キャッシュ活用: 認証トークンの適切な管理
- 並列処理: 複数記事の一括処理対応
- エラー回復: リトライ機能の実装
- ログ管理: 詳細なログ出力
今後の拡張案
- 画像の自動アップロード機能
- SEO メタデータの自動生成
- SNS への自動投稿連携
- 記事のスケジュール投稿機能
まとめ
本記事では、Google Blogger API を活用した技術記事の自動投稿方法を解説した。
実現できたこと
- 効率的な記事管理: Markdown による記事作成
- 安全な認証: OAuth2 による API 連携
- シンプルな構成: 複雑な依存関係のない軽量システム
- 自動投稿: Google Blogger への直接投稿
学んだポイント
- OAuth2 認証の実装: Google API との安全な連携方法
- 環境変数管理: セキュリティを考慮した設定管理
- Node.js スクリプト: 実用的な CLI ツールの作成
- システム設計: 複雑性を排除したシンプルなアーキテクチャ
このスクリプトにより、技術記事の執筆から投稿までの作業を大幅に効率化できた。Google Blogger API は思った以上に使いやすく、個人ブログの自動化には最適である。
参考文献
- Google Blogger API v3 Documentation
- Google OAuth2 Documentation
- Node.js googleapis Library
- Claude Code Documentation
執筆について: この記事は Claude Code (claude.ai/code) との協働により執筆され、実際に構築した自動投稿スクリプトを使用して Blogger に投稿されている。
0 件のコメント:
コメントを投稿