カテゴリー: 実装ガイド

  • Claude CodeでWordPressに記事を自動投稿する仕組み|複数サイトで運用している構成・コード・失敗の記録

    項目 内容
    検証環境 複数のWordPressサイトをAIで運用している個人運営者の実環境(サイト名・ジャンルは非公開)
    検証日 2026年11月4日時点の運用に基づく記述
    データ元 運営者自身の運用経験(匿名化済み・数値は丸めて表記)
    再現コード あり(サニタイズ済みの汎用形。example.com 表記)

    この記事で分かること/分からないこと

    この記事は、AIコーディングエージェント(Claude Code)を使ってWordPressに記事を「下書きとして」自動投入し、公開は人間が決める、という半自動の仕組みの全体設計をまとめたものです。複数のWordPressサイトをこの方式で運用している運営者が、実際に組んだ構成と、実際にやらかした失敗を書いています。

    分かること:

    • 記事の企画から下書き投入までの流れと、各段階で何を自動化し、何を人間に残したか
    • WordPress REST APIで記事を下書き投稿する最小のコードと、認証情報の安全な扱い方
    • 公開前に機械で落とせる検査の種類と、検査を書くときの落とし穴
    • 運用で実際にハマった6つのつまずき

    分からないこと:

    • 「この仕組みで収益が出るか」。収益や順位の話は、この記事では約束しません。結果は運用するサイトの状態や分野で変わります。
    • Claude Code自体の使い方の基礎。インストールや基本操作は公式ドキュメントを参照してください。
    • WordPress REST APIの細かな仕様。それは別記事(準備中)で言語別に扱います。

    なお、本サイトはAnthropic社・WordPress関連団体・各サーバー会社とは無関係の非公式サイトです。各名称は各社の商標で、ここでは普通名詞的に、ツール名として言及するだけです。

    また、ここに載せるコードは、自分の環境で動かす前に必ずバックアップを取り、テスト用のサイトか下書き状態で試してください。動作の結果は、お使いの環境で変わります。

    全体像|「選ぶ→作る→検査→照合→下書き投入→人間が公開」の6段階

    先に全体像です。私の運用では、記事は次の6段階を通ります。

    1. キーワード選定: 狙う検索意図と、すでに書いた記事との重複を決める
    2. 記事製造: AIが下書きを作る。必要な一次情報は人間が材料として渡す
    3. 機械検査: スクリプトが、禁止表現・リンク・構文など機械的に判定できる項目を落とす
    4. 原典照合: 記事中の事実(制度、仕様、数値)を、公式資料などの一次情報に当てて確かめる
    5. REST APIで下書き投入: 検査を通った記事を、status=draftでWordPressに入れる
    6. 人間が公開を判断: 管理画面で下書きを読み、公開ボタンを押すのは人間

    ポイントは、自動化の範囲が「5」で止まっていることです。6の「公開」は、意図的に人間に残しています。そして、1から5の途中で人間が介在する場所もあります。たとえば2の一次情報(実際に測った数値、実際に起きた失敗)は、AIには作れないので人間が渡します。

    この6段階に「Claude Codeでなければならない理由」は、実はありません。同じ設計はほかのAIツールでも組めます。Claude Codeを使う利点は、ローカルのファイルを読み書きし、スクリプトを自分で書いて実行し、検査の結果を見て直すところまでを、一つの作業場でやってくれる点にあります。つまり、段階2〜5を「同じ作業ディレクトリの中で」つなげやすいのです。

    なぜ自動「公開」ではなく自動「下書き」なのか

    REST APIはstatus=publishと書けば、そのまま公開できます。技術的には、全自動にするほうが簡単です。それでも下書き止まりにしているのには、理由が2つあります。

    理由1: スパム判定のリスクを避けるため。 Googleのスパムポリシーには「Scaled Content Abuse(大規模なコンテンツの不正生成)」という項目があります。ポイントは、AIで作ったかどうかではなく、検索順位を操作する目的で、価値の低いページを大量に作ることが問題とされている点です。GoogleはAI生成そのものを禁じているわけではない、という立場を公式に示しています(詳細は公式ドキュメントで確認してください)。ただ、人間が一度も読まないまま、自動で量産し続ける運用は、この「大規模な生成」に該当する側へ寄っていきます。公開前に人間が読み、判断する工程があることは、運用の実態としても、説明責任としても重要だと考えています。

    理由2: 品質事故を、公開前に止めるため。 AIの下書きには、もっともらしい誤りが混じります。制度の名称、数字、仕様の説明などです。機械検査である程度は落とせますが、「この主張は本当に成り立つか」「この記事は読者の役に立つか」は、まだ人間が見たほうが安全です。公開してしまえば、直すまで間違いが検索結果に出続けます。下書きなら、間違いは管理画面の中で止まります。

    正直に書いておくと、この方式では「人間の検品時間」が最後まで残ります。公開判断の自動化はしていませんし、するつもりもありません。逆に言えば、この仕組みで減らせているのは「書く作業」と「入稿の作業」であって、「判断する作業」ではありません。

    準備|WordPress側(認証・権限・HTTPS)

    アプリケーションパスワードを使う

    WordPress 5.6以降には、REST API専用のパスワードを発行する「アプリケーションパスワード」が標準で備わっています。ユーザーのプロフィール画面から発行できます。

    重要なのは、これがユーザーのログインパスワードとは別物だという点です。REST APIの認証にしか使えず、管理画面にはログインできません。漏れたときは、そのパスワードだけを失効させればよく、ログインパスワードを変える必要がありません。投稿の自動化に、ログインパスワードそのものを使うのは避けてください。

    なお、アプリケーションパスワードは、既定ではHTTPSのサイトでのみ使えます。HTTPのままでは動きません。投稿の通信は認証情報を含むので、常時HTTPSにしておくのが前提です。

    権限は、最小限から考える

    投稿に使うユーザーは、本来は必要最小限の権限にしたいところです。ただ、現実には「何を自動化するか」で必要な権限が変わります。

    • 下書きを作るだけなら、Contributor(寄稿者)やAuthor(投稿者)でも足ります
    • カテゴリの新規作成には、Editor(編集者)以上が必要です
    • 固定ページの作成や、画像のアップロードでも、権限が足りない場面があります

    立ち上げ期は強い権限が要り、軌道に乗ったら権限を絞る、という使い分けもあり得ます。自分の自動化の範囲を決めて、それに必要な最低限の権限を割り当てる、という考え方がよいと思います。

    公開前にやっておくこと

    準備の段階で、次の2つは記事を1本目投入する前にやってください。

    • パーマリンク設定: 一度記事を公開した後に変えると、URLがすべて変わります。最初に決めます(私は投稿名ベースにしています)
    • 著者の表示名: 新しく作ったユーザーは、ログインIDがそのまま表示名になっていることがあります。テーマによっては、そのIDが著者表示から見えてしまいます。匿名で運営する場合は、必ず表示名を別の名前に変更します

    準備|Claude Code側(ディレクトリ・CLAUDE.md・秘密情報)

    ディレクトリ構成

    作業ディレクトリは、段階ごとに置き場所を分けています。汎用化した例です。

    site-a/
    ├── CLAUDE.md            # このサイトの運用ルール(AIが毎回読む)
    ├── kw/                  # キーワードと検索意図のメモ
    ├── drafts/              # 記事の下書き(Markdown)
    ├── sources/             # 原典照合に使った一次資料のメモ
    ├── scripts/
    │   ├── check.py         # 機械検査
    │   ├── md2html.py       # Markdown→HTML変換
    │   └── post_draft.sh    # REST API投入
    └── logs/                # 投入ログ(記事ID・日時・結果)
    

    サイトごとにディレクトリを分けると、設定や禁止表現の取り違えが減ります。複数サイトを一つのディレクトリに混ぜるのは、避けたほうがよいです。

    CLAUDE.mdに書くこと

    Claude Codeは、作業ディレクトリのCLAUDE.mdを読んでから作業します。ここに、そのサイト固有のルールを書いておくと、毎回説明し直さずに済みます。私が書いている種類の項目は次のとおりです。

    • 文体・禁止表現(成果の断定、根拠のない比較など)
    • 「公開は人間が行う。statusは常にdraft」というルール
    • 事実を書くときは一次情報を確認し、確認できないことは「未確認」と書くこと
    • 検査スクリプトを通ってから投入すること

    特に3番目は重要です。AIは、知らないことでも、それらしく書いてしまいます。「確認できないなら、書かない」を明文化しておくと、事故が減ります。

    秘密情報は、スクリプトに書かない

    アプリケーションパスワードは、スクリプトに平文で書きません。macOSなら、キーチェーンに保存して、実行時に取り出します。

    # 初回だけ:キーチェーンに保存(-wを最後に付けると、パスワードを対話入力できる)
    security add-generic-password -s "example-wp" -a "api-user" -w
    
    # 使うとき:取り出す
    WP_APP_PW="$(security find-generic-password -s "example-wp" -a "api-user" -w)"
    

    -wの後ろに直接パスワードを書くと、シェルの履歴やプロセス一覧に残るので、対話入力のほうが安全です。また、スクリプトやCLAUDE.mdに、ホスト名・ユーザー名・パスワードを書かないでください。AIに作業させる場合も、秘密情報そのものをチャットに貼らず、「キーチェーンから取り出す」処理だけを書いてもらうのが安全です。

    実装|Markdownを変換してREST APIで下書き投入する

    最小の下書き投入

    投稿は、POST /wp-json/wp/v2/postsにJSONを送るだけです。固定ページは/pages、カテゴリの作成は/categoriesに、同じ要領で送れます。

    #!/usr/bin/env bash
    # post_draft.sh:HTMLを下書きとして投入する(汎用サンプル)
    set -euo pipefail
    
    SITE="https://example.com"
    WP_USER="api-user"
    WP_APP_PW="$(security find-generic-password -s "example-wp" -a "api-user" -w)"
    
    TITLE="$1"; SLUG="$2"; CONTENT_FILE="$3"; CATEGORY_ID="$4"
    
    # JSONはpythonで組み立てる(引用符や改行のエスケープ事故を避ける)
    BODY="$(python3 - "$TITLE" "$SLUG" "$CONTENT_FILE" "$CATEGORY_ID" <<'PY'
    import json, sys
    title, slug, path, cat = sys.argv[1:5]
    print(json.dumps({
        "title": title,
        "slug": slug,
        "content": open(path, encoding="utf-8").read(),
        "status": "draft",           # 公開は人間が行う
        "categories": [int(cat)],
    }, ensure_ascii=False))
    PY
    )"
    
    # 認証情報はコマンド引数ではなく、標準入力の設定として渡す(ps対策)
    printf 'user = "%s:%s"\n' "$WP_USER" "$WP_APP_PW" | \
      curl -sS -K - -X POST "$SITE/wp-json/wp/v2/posts" \
        -H "Content-Type: application/json; charset=utf-8" \
        -d "$BODY" | python3 -c 'import json,sys; r=json.load(sys.stdin); print(r.get("id"), r.get("status"), r.get("code",""))'
    

    最後の行で、返ってきたJSONから記事ID・ステータス・エラーコードだけを表示しています。成功すれば、statusはdraftになっているはずです。公開されてしまうことは、この呼び出しからは起きません。

    カテゴリは、IDの数字で指定します。事前に/categoriesでカテゴリを作るか、一覧を取得してIDを確認します。

    Markdownからの変換と「改行」の罠

    AIが書いた原稿はMarkdownです。それをHTMLに変換してcontentに入れます。ここで最初にハマりやすいのが、WordPressのwpautop(自動整形)です。本文中の改行を<br>や<p>に変換するため、JavaScriptやレイアウト用のHTMLが壊れることがあります。

    対策は、投入するHTMLから改行を潰すことです。変換スクリプトの最後に入れます。

    import re
    
    def squash_newlines(html: str) -> str:
        # <pre>内は改行が意味を持つので、一時的に退避する
        pres = []
        def keep(m):
            pres.append(m.group(0))
            return f"@@PRE{len(pres)-1}@@"
        html = re.sub(r"<pre\b.*?</pre>", keep, html, flags=re.S | re.I)
        html = re.sub(r"\s*\n\s*", "", html)     # 改行と前後の空白を除去
        for i, p in enumerate(pres):
            html = html.replace(f"@@PRE{i}@@", p)
        return html
    

    ただし、改行を潰すと、別の副作用があります。たとえば、JavaScriptの//コメントは、改行がなくなると、その後ろのコードまで巻き込んでコメントにしてしまいます。本文にJavaScriptを埋める場合は、/* */形式のコメントにするか、コメントを書かないでください。また、JS文字列の中に<div>などのブロックタグを書くと、wpautopが介入して壊れることがあります。

    <pre>の中まで改行を潰すと、コード例が1行に潰れてしまうので、上のように退避しています。

    実装|公開前の機械検査

    機械検査は、「人間が見る前に、機械で判定できるものを落とす」ためのものです。私の検査項目は、次の5種類です。

    検査 内容 補足
    禁止表現スキャン 成果を約束する表現・断定表現、運営方針で使わない語のリスト照合 リストは運営者が管理。実例は公開しません
    外部リンクの全数確認 本文中の外部リンクにHTTPリクエストを送り、ステータスを確認 200以外は人間が目視。ボット弾きで403になる場合あり
    内部リンクの到達性 記事内の内部リンク先が存在するか 未公開記事へのリンクは公開順に注意
    JSON-LD(構造化データ)の構文 application/ld+jsonの中身がJSONとして読めるか 描画後のHTMLで確認する
    文字数・H2の数 記事タイプごとの下限・上限、目次との整合 薄い記事の公開を止める

    検査のgrepは、緩いパターンで書く

    検査スクリプトを書くとき、最も事故が多いのが、パターンが厳しすぎて検出漏れすることです。たとえば、リンクを検出するのに<a href="https://という文字列で探すと、次のものは検出できません。

    • 属性の順番が違うもの(<a class="x" href=...>)
    • 引用符がシングルクォートのもの、または引用符がないもの
    • HREFのように大文字で書かれたもの

    緩いパターンの例です。

    import re
    
    LINK = re.compile(
        r"""<a\b[^>]*?\bhref\s*=\s*(["']?)(https?://[^\s"'>]+)""",
        re.I | re.S,
    )
    
    def external_links(html: str) -> list[str]:
        return [m.group(2) for m in LINK.finditer(html)]
    

    属性の順番、引用符の種類、大文字・小文字のいずれにも依存しません。「検出漏れがあるとき、検査は通ったように見える」ので、この種のバグは気づきにくいのが厄介です。検査を書いたら、わざと違反を仕込んだテスト用の入力で、実際に検出できるかを試すことをおすすめします。

    外部リンクのステータス確認

    # 例:1件のURLを確認(リダイレクト追従・タイムアウト付き)
    curl -s -o /dev/null -L --max-time 15 -w "%{http_code}\n" "https://example.com/some-page"
    

    全リンクに対して回して、200以外を一覧にします。ただし、200以外がすべて「リンク切れ」とは限りません。ボット対策で、スクリプトからのアクセスだけ403を返すサイトもあります。そのため、200以外は「人間が目視で確認する対象」として扱い、自動で削除はしていません。

    機械検査では、「PR表記が入っているか」のように、運営ルールに沿った項目もチェックしています。広告・アフィリエイトの表示は、景品表示法のステルスマーケティング規制(2023年10月施行)の対象です。表記が必要な記事に、表記が漏れていないかは、機械で検出できる数少ない法務項目です。

    原典照合|AIの事実を、一次情報に当てる

    検査と投入の間に、「原典照合」を入れています。これは機械検査と違い、人間(またはAIに一次資料を読ませて人間が確認)が行います。

    やっていることは単純で、記事中の事実を一つずつ取り出し、公式資料にあたって一致を確認します。仕様の説明ならWordPressの公式ドキュメント、検索の挙動ならGoogleの公式ドキュメント、ツールの機能なら各ツールの公式資料です。確認できなかったものは、記事から削るか、「未確認」と書きます。

    AIは、公式資料に書かれていないことを、書かれているかのように説明することがあります。ここは、機械検査では落とせません。原典照合で、一次情報に紐づけられない主張は、記事に残さない、というのが私のルールです。

    運用|時間・コスト・自動化率について

    運営の実感値を、丸めて書きます。これは私の環境での感触であり、再現を約束する数字ではありません。

    • この半自動の体制で、1サイトあたり週4〜6本の更新を、人間1人の検品時間だけで維持できています。ここでの「検品時間」には、公開前の読み込み、一次情報の提供、検査結果の確認を含みます
    • 新規サイトの立ち上げ(ドメイン設定→WordPressの設置→固定ページ→初期記事の投入)は、1日で完了する運用になっています。REST APIでの投入と、固定ページの雛形があるためです

    一方で、記事1本あたりの所要時間やコストの内訳は、この記事では書きません。モデルの料金、契約プラン、使い方で大きく変わり、一般化しにくいためです。実測値を整理できたら、別の記事(コストの実数公開)で扱う予定です。

    自動化率という観点では、「書く」と「入稿」が自動化され、「判断」が残ります。この残る部分を、効率化のために無理に減らさないことが、この設計の前提です。

    失敗集|実際にやらかした6つ

    運用で実際にハマったものです。

    失敗1: 本文の改行が<br>になり、JSとレイアウトが壊れた

    前に触れたwpautopの問題です。ローカルのプレビューでは正常でも、WordPressに投入すると、本文内の改行が<br>に変換されて、JavaScriptやHTMLのレイアウトが壊れました。改行を潰して投入する、JS文字列の中にブロックタグを書かない、という二つのルールで解消しました。

    失敗2: 抜粋にHTMLを入れて、構造化データが壊れた

    記事の抜粋(excerpt)にHTMLタグを入れて投入したところ、テーマがその抜粋をJSON-LD(構造化データ)に流し込む仕様で、JSONが壊れました。抜粋は素のテキストだけにします。投入時に、タグを除去する処理を入れています。ページを見ただけでは気づきにくく、検査で構文を見て初めて分かりました。

    失敗3: パーマリンクを、公開後に変えそうになった

    初期設定のままでも記事は投入できてしまうため、パーマリンクの設定を後回しにしがちです。公開してから変えると、URLがすべて変わります。記事の公開前に、必ず設定するのが鉄則です。

    失敗4: ログインIDが、著者表示から見えていた

    ユーザーを作ったままにすると、ログインIDが著者の表示に出ることがあります。匿名で運営している場合、これは致命的です。表示名を必ず変更し、公開後に、著者アーカイブやREST APIのユーザー一覧(/wp-json/wp/v2/users)から情報が見えていないかも確認しています。

    失敗5: 検査のgrepが、検出漏れしていた

    先に書いた緩いパターンの話です。最初は厳密なパターンで書いていたため、属性の順番や引用符が違うリンクを、検査が見逃していました。検査を通ったのに問題が残っていた、という最も気づきにくいパターンです。緩いパターンに変え、違反入りのテスト入力で検出できることを確認する運用にしました。

    失敗6: 「更新が反映されていない」と誤認した

    ページキャッシュが効いていて、更新したはずのページが古いまま表示され、「投入に失敗した」と誤認したことがあります。確認のときは、URLの末尾にキャッシュバスターを付けて、キャッシュを避けます。

    curl -s "https://example.com/sample-page/?nocache=$(date +%s)" | head -c 500
    

    投入が本当に成功したかは、APIのレスポンス(記事ID・ステータス)と、管理画面の下書き一覧で確認できます。見た目だけで判断しないのがコツです。

    よくある質問

    Q. SSH(WP-CLI)とREST APIのどちらがよいですか。A. 私はREST APIを使っています。サーバーにSSHで入らなくても、HTTPSで投稿でき、権限をWordPressのユーザー単位で絞れるからです。SSHが使える環境なら、WP-CLIも選択肢になります。両者の違いは別記事(準備中)で扱います。

    Q. Claude Codeは、サブスクリプションとAPI従量課金のどちらで使うべきですか。A. 利用形態によって、料金も利用規約上の扱いも異なります。運用を始める前に、最新の公式の利用規約・料金ページで確認してください。ここでは、どちらが良いとは書きません。

    Q. 複数のサイトを運用するとき、気をつけることは。A. 一つのディレクトリにまとめず、サイトごとに作業ディレクトリ・認証情報・CLAUDE.mdを分けることです。取り違えて別のサイトに投入する事故を避けられます。また、複数のサイトを運営している事実は、運営者情報で開示することを私は大事にしています。

    Q. AI Overview(検索結果のAI要約)に引用されるために、特別な対応は必要ですか。A. Googleは、AI機能に表示されるために特別な技術要件はないとしています。llms.txtのような専用ファイルも、必須ではないという説明です(公式ドキュメントで最新の内容を確認してください)。また、ある調査では、引用元の多くが通常検索の上位10位以内のページだったと報告されています。この数字は調査によって異なるので鵜呑みにはせず、方向性としては、通常のSEOで上位を狙うことが、そのままAI検索への対応にもなる、と私は理解しています。

    規約・リスク

    自動化の運用では、次の点を確認してください。

    • 利用規約: 使うAIサービスの利用規約・利用ポリシーは更新されます。自動化やコンテンツの大量生成に関する条項を、運用前に原文で確認し、更新のたびに見直してください。この記事の内容は、規約の解釈を示すものではありません
    • PR表記: アフィリエイトなど報酬が発生する導線には、読者が分かる形で広告表示をします。機械検査に、表記漏れの検出を入れておくと安全です
    • スパム判定対策の考え方: 本数を増やすことを目的にしない。1本ごとに、読者に独自の価値(一次情報や、実測のデータ)があるかを基準にする。自動化は、その価値を届ける手間を減らすために使います
    • 成果の扱い: 他のサイトで動いた方法が、自分のサイトで同じ結果になるとは限りません。この記事の数字は、運営者の環境での記述です

    次に読む記事

    • WordPress REST APIの仕様と最小コード(準備中)
    • Python実装の詳細とエラー処理(準備中)
    • Markdownをブロックエディタ向けに変換する方法(準備中)
    • 公開前の人間チェックのフロー(準備中)
    • AI記事の機械検査スクリプト集(準備中)
    • REST APIで401・403が出たときの対処(準備中)

    なお、完成版のスクリプトとプロンプト集を、有料note/テンプレートとしてまとめることを検討しています。準備ができた時点で、この記事に案内を追記します(現時点では販売していません)。