PythonでWordPressのREST APIを操作する|記事の一括更新と、403で止まらないための書き方

Fal.aiで生成したノートPCで作業する丸メガネ美少女のイラスト

PR

AIで記事作成を効率化したいなら → 高品質SEO記事生成AIツール【Value AI Writer byGMO】

WordPressの管理画面から、100件の記事のリンクを1つずつ直す——考えただけで気が遠くなりますよね。WordPressにはREST APIが用意されていて、Pythonから記事の取得・更新・下書き化まで自動で操作できます。この記事では、実際に使っているコードをもとに、つまずきやすいポイント込みで手順をまとめます。

この記事でできるようになること

  • アプリケーションパスワードで認証して、PythonからWordPressを操作する
  • 記事の一覧を取得し、特定の記事だけを更新する
  • 複数記事をまとめて処理する(連続で叩かない書き方)
  • 403エラーで止まらないための、実務的な対処

WordPressのバージョンやプラグイン構成によって挙動は変わります。手順を試すときは、まず1件だけで実行して結果を確認するところから始めてください。

準備1:アプリケーションパスワードを発行する

REST APIで書き込みを行うには認証が必要です。WordPress 5.6以降にはアプリケーションパスワードという仕組みがあり、管理画面から発行できます。

  1. 管理画面の「ユーザー」→「プロフィール」を開く
  2. ページ下部の「アプリケーションパスワード」欄に、名前(例:python-script)を入力して追加
  3. 表示された24文字のパスワードを控える(この画面を閉じると二度と表示されません)

ログインパスワードを直接スクリプトに書くのは絶対に避けてください。アプリケーションパスワードは用途ごとに発行して、不要になったらその1つだけ削除できるのが利点です。

準備2:認証ヘッダを作る

アプリケーションパスワードは、「ユーザー名:パスワード」をBase64でエンコードしてBasic認証のヘッダに載せるのが基本形です。

import base64
import requests

BASE = "https://example.com/wp-json/wp/v2"
USER = "your-username"
APP_PASSWORD = "xxxx xxxx xxxx xxxx xxxx xxxx"  # 発行したアプリケーションパスワード

token = base64.b64encode(f"{USER}:{APP_PASSWORD}".encode()).decode()
headers = {
    "Authorization": "Basic " + token,
    "Content-Type": "application/json",
}

パスワードは環境変数や .env ファイルから読む形にしておくと、スクリプトを共有しても中身が漏れません。

ステップ1:記事の一覧を取得する

r = requests.get(
    f"{BASE}/posts",
    headers=headers,
    params={"per_page": 5, "_fields": "id,title,link,status"},
    timeout=30,
)
r.raise_for_status()

for p in r.json():
    print(p["id"], p["status"], p["link"])

_fields で取得する項目を絞ると、レスポンスが軽くなって処理が速くなります。全記事を舐めるときは、レスポンスヘッダの X-WP-Total(総件数)と X-WP-TotalPages(総ページ数)を見てページを回すのが確実です。

ステップ2:1件だけ更新する(必ずここから)

更新は対象のIDに対してPOSTします。いきなり100件流すのは絶対にやめてください。まず1件で成功を確認します。

post_id = 123

payload = {
    "title": "新しいタイトル",
    "content": "

本文をここに書きます。

", "status": "draft", # ← 必ず明示する } r = requests.post(f"{BASE}/posts/{post_id}", headers=headers, json=payload, timeout=30) r.raise_for_status() print(r.json()["link"])

status は必ず指定してください。省略すると、意図せず公開記事が下書きに戻ったり、その逆が起きたりします。実際にこれを忘れて、公開状態が予期せず変わったことがありました。

ステップ3:変更前にバックアップを取る

機械的に一括更新すると、戻すのが大変です。変更前の内容をファイルに保存してから書き換えるのが鉄則です。編集用の内容を取るには context=edit を付けます(認証が必要)。

import json

before = requests.get(
    f"{BASE}/posts/{post_id}",
    headers=headers,
    params={"context": "edit"},
    timeout=30,
).json()

with open(f"backup-post-{post_id}.json", "w", encoding="utf-8") as f:
    json.dump(before, f, ensure_ascii=False, indent=2)

ステップ4:複数記事をまとめて処理する

ここからが本題です。ただし、連続でリクエストを投げるとサーバー側の防御機構に引っかかります。対策は2つで、①失敗したら待って再試行する、②成功しても少し間を空ける、です。

import time

def post_with_retry(path, payload, retries=4):
    """403/429/503 のときは待って再試行する"""
    for i in range(retries + 1):
        r = requests.post(f"{BASE}/{path}", headers=headers, json=payload, timeout=30)
        if r.status_code == 200:
            return r.json()
        if r.status_code in (403, 429, 503) and i < retries:
            wait = 15 * (i + 1)
            print(f"{r.status_code}: {wait}秒待って再試行します ({i + 1}/{retries})")
            time.sleep(wait)
            continue
        r.raise_for_status()

for target in targets:  # targets = [{"id": ..., "payload": {...}}, ...]
    result = post_with_retry(f"posts/{target['id']}", target["payload"])
    print("OK", result["link"])
    time.sleep(1.5)  # 連続で叩きすぎない

つまずいたポイントと対処

実際に運用していて詰まったのは、次の4つです。コードを書く前に知っておくと時間を節約できます。

症状原因対処
同じ内容でも、403になったり通ったりするWAF系のセキュリティプラグインが、大きなPOSTリクエストを断続的にブロックしている20秒〜数分待って再試行する。本文のサイズ依存ではないこともあるので、内容を疑って作り直す必要はない
DELETEが必ず403になるセキュリティ設定でDELETEメソッドが拒否されている削除ではなく statustrash にしてゴミ箱へ移動する(実質は同じで、復元もできる)
公開状態が意図せず変わるPOST時に status を省略した更新時は必ず status を明示する
絞り込んだつもりが全件返るパラメータ名の間違い、またはページングの未処理per_pagepage_fields の動きを1件ずつ確認する

安全に運用するための5つのルール

  • まず1件で試す:成功も失敗も、1件で確認してから件数を増やします
  • 変更前にバックアップを取る:JSONで保存しておけば、失敗しても元に戻せます
  • ドライランを作る:実際に書き換えず、対象と変更内容をログに出すだけのモードを先に動かします
  • 必ず待ち時間を入れる:連続アクセスはサーバーにも負担です。1件ごとに1〜2秒空けます
  • statusを明示する:下書きで作るのか公開するのか、必ず自分で指定します

まとめ

WordPressのREST APIは、「取得 → 1件テスト → バックアップ → 一括処理(待ち時間つき)」という順番さえ守れば、それほど難しくありません。逆に、いきなり全件に対して書き込みをかけるのが一番危険です。

つまずきの多くは、コードの書き方ではなくサーバー側の防御(WAF・セキュリティプラグイン)が原因です。エラーが出たら「自分のコードが間違っている」と決めつけずに、レスポンスのステータスコードを確認するところから始めると、解決が早くなります。同じ考え方は、WordPress以外のAPIを扱うときにもそのまま使えます。

関連記事