WordPressにPythonから投稿できた瞬間、思ったより感動しました。
ただし、その前に6回失敗しています。認証で2回、URLで1回、ステータス指定で1回、JSONで1回、最後はただのスラッグ重複でした。地方の夜は静かですが、エラー画面だけは妙ににぎやかです。
この記事では、遠回りして残った「まずこれで動かす」コードだけを書きます。
何をしたかったのか
やりたかったのは単純です。
Markdownで書いた記事を、WordPress REST API経由で下書き投稿する。管理画面に毎回貼り付けるのではなく、Pythonから送る。まずは1記事だけで十分です。
最初からカテゴリ自動作成、画像アップロード、予約投稿まで入れようとすると転びます。私は転びました。
事前に必要なもの
必要なのは3つです。
1つ目はWordPressのURL。2つ目はユーザー名。3つ目はアプリケーションパスワードです。
パスワードをコードに直書きしないため、環境変数に入れます。
$env:WP_BASE_URL="https://example.com"
$env:WP_USER="your-user"
$env:WP_APP_PASSWORD="xxxx xxxx xxxx xxxx xxxx xxxx"
実際の値は自分の環境に合わせてください。ここで1文字でも違うと、かなり冷たい401が返ってきます。
動く最小コード
私は最終的に、このくらいまで削りました。
import os
import requests
from requests.auth import HTTPBasicAuth
def create_post(title: str, content: str, status: str = "draft") -> dict:
base_url = os.environ["WP_BASE_URL"].rstrip("/")
user = os.environ["WP_USER"]
app_password = os.environ["WP_APP_PASSWORD"]
endpoint = f"{base_url}/wp-json/wp/v2/posts"
payload = {
"title": title,
"content": content,
"status": status,
}
response = requests.post(
endpoint,
json=payload,
auth=HTTPBasicAuth(user, app_password),
timeout=20,
)
# ハマりポイント: 失敗時に本文を出さないと原因が見えない
if response.status_code >= 400:
raise RuntimeError(f"{response.status_code}: {response.text}")
return response.json()
if __name__ == "__main__":
post = create_post(
"API投稿テスト",
"<p>Pythonから投稿した下書きです。</p>",
)
print(post["id"], post["link"])
最初の成功まで、ここから始めればよかったです。余計な機能を足すのは、1件投稿できてからで十分でした。
私が踏んだ6つの穴
1つ目は、通常のログインパスワードを使ったことです。REST APIではアプリケーションパスワードを使います。ここで15分消えました。
2つ目は、URLの末尾スラッシュ問題です。rstrip("/") を入れておくと、//wp-json のような地味な事故を防げます。
3つ目は、status を publish にしてしまったことです。テスト投稿がそのまま公開されるのは心臓に悪いです。初回は必ず draft にします。
4つ目は、HTMLとMarkdownの扱いを混ぜたことです。WordPressに送る本文は、まずHTMLとして送るほうが分かりやすいです。
5つ目は、エラー本文を捨てたことです。response.raise_for_status() だけだと、なぜ落ちたのか見えにくい場面があります。
6つ目は、同じタイトルで何度も投稿して管理画面が散らかったことです。テスト記事は3本までと決めたほうがいいです。
MarkdownをHTMLにする
Markdownから投稿したい場合は、変換を1段挟みます。
import markdown
def markdown_to_html(path: str) -> str:
text = open(path, encoding="utf-8").read()
# ハマりポイント: 拡張を固定しておくとコードブロックが崩れにくい
return markdown.markdown(text, extensions=["fenced_code", "tables"])
ただし、装飾やブロックの見え方はテーマに左右されます。最初から完璧な見た目を狙うより、下書きに送って管理画面で確認するほうが速いです。
予約投稿やカテゴリは後でいい
REST APIはできることが多いです。
カテゴリ、タグ、アイキャッチ、予約投稿、更新、削除。調べるほど全部やりたくなります。でも、初日に全部入れると、どこで失敗したか分からなくなります。
私の場合、最初の目標を「下書き1本」に絞ったら、ようやく進みました。成功体験が1つあると、次の機能追加が落ち着いてできます。
投稿前に必ず見る3項目
自動投稿で一番怖いのは、意図しない公開です。
私は初回から publish を使いかけて、送信直前に手が止まりました。公開サイトに「API投稿テスト」が出る未来が見えて、正直かなり怖かったです。
そこで、投稿スクリプトには確認用の出力を入れるようにしました。
def print_preview(payload: dict) -> None:
print("title:", payload.get("title"))
print("status:", payload.get("status"))
print("content_chars:", len(payload.get("content", "")))
# ハマりポイント: draft以外なら人間が一度止まれるようにする
if payload.get("status") != "draft":
raise ValueError("first run must use draft status")
見るのは、タイトル、ステータス、本文の文字数です。この3つだけで、空本文、公開指定、タイトル間違いにかなり気づけます。
自動化は、成功時の速さだけでなく、失敗直前に止まれる仕組みまで含めて作るべきでした。
まとめ
WordPress REST API自動投稿は、動くとかなり便利です。記事作成フローをファイルベースにできるので、下書き、レビュー、投稿の流れが整理されます。
でも初日に必要なのは、立派な投稿システムではありません。必要なのは、20行前後で下書きを1本作れる最小コードです。
自動投稿の第一歩は、公開ボタンを押すことではなく、安心して失敗できる下書きを作ることです。

コメント