1記事5分の作業が、30記事になったらどうなるか
WordPressで記事を公開する一番素朴な方法は、管理画面を開いて、タイトルを入れて、本文を貼って、公開ボタンを押すことだ。1記事だけなら、これでまったく問題ない。
問題は、それが30記事、50記事になったときに起きる。
タイトルを入れる。本文を貼る。スラッグを直す。フォーカスキーフレーズを入れる。SEOタイトルを書く。メタディスクリプションを書く。抜粋を書く。カテゴリを選ぶ。アイキャッチを設定する。公開して、URLを確認して、関連記事のリンクを差し替える——これを1記事ごとに繰り返す。
人間が手でやる作業は、ミスを生む。コピー漏れ、貼り付け忘れ、カテゴリの選択ミス。それに、単純に時間がかかる。今日もまさにこの問題に直面した。記事が30本近くある状態で、「要目データが何件抜けているか」を1記事ずつ手で開いて確認するのは、現実的ではなかった。
やったこと:API経由で「全記事の状態」を一括取得する
最初にやったのは、個別記事の確認ではなく、全体の状況を一度に可視化することだった。
$allPosts = @()
$page = 1
do {
$batch = Invoke-RestMethod -Uri ".../wp/v2/posts?per_page=100&page=$page" -Headers $headers -Method Get
$allPosts += $batch
$page++
} while ($batch.Count -eq 100)
WordPress REST APIは、1回のリクエストで最大100件までしか返さない。ページネーションを自分でループさせて、全記事を1つの配列に集める。これだけで、サイト全体に何記事あるかが分かる。
次に、その全記事の本文を1件ずつ取得し、「諸元データ」という見出し文言を含むかどうかでフィルタした。
foreach ($post in $allPosts) {
$detail = Invoke-RestMethod -Uri ".../posts/$($post.id)?context=edit" -Headers $headers -Method Get
if ($detail.content.raw -match "諸元データ") {
# この記事は「2部(諸元・戦歴)記事」だと判定
}
}
スラッグの命名規則(-spec-historyで終わる、など)は、記事を作った時期によってバラバラだった。スラッグというメタデータに頼らず、本文の中身そのもので判定する方法に切り替えたことで、命名規則の違いに関係なく、全記事を確実に拾い切れた。
さらに、要目データ(起工日・竣工日・基準排水量など9項目)がそれぞれ何個含まれているかを数値化した。
$keywords = @("起工日","竣工日","基準排水量","全長","全幅","機関","速力","航続距離","乗員")
$foundCount = 0
foreach ($kw in $keywords) {
if ($detail.content.raw -match $kw) { $foundCount++ }
}
結果は、18記事のうち16記事で何らかの要目データが欠けている、というものだった。手作業で1記事ずつ確認していたら、この全体像に気づくまでにもっと時間がかかっていたはずだ。
やったこと:本文の差分だけを安全に書き換える
全体像が見えたら、次は個別の修正だ。ここでもう一つの技術的な発見があった。
WordPressの記事本文には<!-- wp:html -->というブロックコメントが入っている。これはGutenbergエディタが「ここはカスタムHTMLブロックだ」と認識するための目印だ。
新しい本文をまるごとcontentフィールドに代入してPOSTすると、なぜかこのコメントが消えてしまい、結果としてエディタ側でHTMLが正しく解釈されなくなる現象に遭遇した。一方、既存の本文を取得し、その中の一部を-replaceで書き換えてからPOSTする方式だと、コメントは保持された。
$current = Invoke-RestMethod -Uri ".../posts/$id?context=edit" -Headers $headers -Method Get
$oldContent = $current.content.raw
$newContent = $oldContent -replace [regex]::Escape($oldMarker), $newBlock
$body = @{ content = $newContent } | ConvertTo-Json -Depth 5
Invoke-RestMethod -Uri ".../posts/$id" -Headers $headers -Method Post -Body $body -ContentType "application/json; charset=utf-8"
これは単なる小技ではない。「既存のものをベースに、差分だけを当てる」という設計そのものが、安全性を担保していた。全文を新規に作って丸ごと入れ替えるより、変更箇所を最小限に絞る方が、想定外の崩れを防げる。
なぜ「自動化」そのものが目的ではないのか
ここで強調したいのは、これは「人間の作業を全部AIに置き換える」という話ではないということだ。
今日の一連の作業には、必ず人間の確認が入っていた。要目データをどこから補うか(所有している専門書や、兵装辞典から、サイト記事のWikipediaか、専門の艤装データサイトか)の判断、修正後の見た目が他の記事と揃っているかの目視チェック、<!-- wp:html -->が消えていることに最初に気づいたのも人間だった。
「ビジュアルエディタで見栄えが違う」という一言が、実は今回の調査の中で一番重要な指摘だった。フロント側の表示だけを見ていたら、ブロック構造が壊れていることには気づけなかった。APIのレスポンスが「成功」を返していても、それを鵜呑みにせず、実際の見た目で確認する——この往復が、自動化を安全に運用するための要だった。
つまり、自動投稿の本質的な価値は「人間を不要にすること」ではなく、「人間が確認に集中できる状態を作ること」にある。タイピングやコピー&ペーストという、ミスを生みやすく退屈な作業を機械に渡し、人間は「これは正しいか」という判断だけに時間を使う。
必要性:なぜ今、この仕組みが要るのか
戦史記事のサイトを30本、50本と育てていく上で、自動投稿の仕組みが必要になる理由は3つある。
1. スケールするほど、手作業のコストが指数的に増える
1記事の修正に5分かかるとして、それが18記事になれば90分。さらに、修正すべき記事が増えるたびに「どの記事が未修正か」を覚えておく負担も増える。API経由で全記事の状態を一覧化できれば、この管理コストはほぼゼロになる。
2. データベース化の構想と、自動投稿は表裏一体である
前回書いた「記事ではなくデータを資産化する」という設計は、結局のところAPI経由でデータを出し入れする仕組みがなければ机上の空論で終わる。艦艇データを構造化しても、それを記事に変換する手段が「コピー&ペースト」のままなら、データベース化の恩恵は半分しか活かせない。自動投稿の仕組みは、データベース構想を実際に動かすための配管だ。
3. 失敗から学べる速度が上がる
今日、wp:htmlブロックコメントが消える現象に遭遇したとき、二分探索で原因を絞り込むのに使ったのも、結局はAPI経由でのリクエストの繰り出しだった。手作業で「貼って、確認して、戻す」を繰り返していたら、この調査自体に何時間もかかっていたはずだ。仕組み化されていたからこそ、1つの仮説を数十秒で検証し、次の仮説に移れた。
結論:自動化は、丁寧さを諦めるための手段ではない
「自動投稿」という言葉には、雑に量産するイメージがついて回ることがある。しかし今日やっていたことは、その逆だった。
全記事の状態を正確に把握する。変更は最小限の差分にとどめる。保存後は必ず実際の表示を確認する。これらはすべて、手作業でやろうとすると省略されがちな丁寧さだ。自動化された仕組みは、この丁寧さを毎回サボらずに実行してくれる、いわば「手を抜かない助手」だった。
記事を書くサイトから、データを生産する工房へ——その移行を実際に支えているのは、派手なAI生成の部分ではなく、こうした地味なAPI連携の積み重ねだということを、今日改めて実感した。