前回の記事 (XServer × SWELL でブログを立ち上げた初日ログ) では、Contact Form 7 を WP管理画面の UI 操作で 20分かけて設置しました。1 サイトならそれで十分です。
ただ、AI × ソロ起業の前提だと、最終的に 3〜4個の WP サイトを並行運用する ことが確定しています。同じ UI クリックを 3回も 4回もやるのは時間がもったいない。そこで、ここでは Python + WP-CLI で全工程を自動化したスクリプトを公開します。
実行は 1 コマンド、所要 5分以内です。
目次
- なぜ Contact Form 7 を自動化するのか
- スクリプトが何をするか (4 ステップ)
- ハマりポイント 2 つ
- スクリプト全文
- 実行ログと所要時間
- 次のサイトに使い回すときのカスタマイズ
1. なぜ Contact Form 7 を自動化するのか
ブログ立ち上げ作業のうち、Contact Form 7 設置は地味に時間が食う部分です。前回 UI 操作で行ったときの所要は 20分でした。プラグイン検索・インストール・有効化、フォーム本体の編集 (メール送信先・本文テンプレ)、お問い合わせ固定ページ作成、メール送信テストの 4 工程。
このうち、メール送信テストを除けば すべて WP-CLI で実行可能 です。3〜4 サイトに展開する前提なら、このスクリプトを 1 個書いておけば、残りのサイトは 5分で同じ状態に揃います。
2. スクリプトが何をするか (4 ステップ)
- プラグイン install + activate (
wp plugin install contact-form-7 --activate) - CF7 フォーム本体を作成 (
wp post create --post_type=wpcf7_contact_form ...) - メタ値を設定 (
_form,_mail,_mail_2,_messages,_localeをwp post meta updateで投入) - お問い合わせ固定ページを作成 (本文にショートコード
を埋める)エラー: コンタクトフォームが見つかりません。
CF7 のフォーム定義はすべて wp_postmeta テーブルに格納されています。具体的には:
| meta_key | 型 | 中身 |
|---|---|---|
_form |
文字列 | フォーム本体テンプレ (label + shortcode mix) |
_mail |
PHP serialized array | 主送信先・件名・本文・追加ヘッダー |
_mail_2 |
PHP serialized array | 自動返信メール (デフォルトは無効) |
_messages |
PHP serialized array | バリデーションメッセージ 22 種 |
_locale |
文字列 | ja |
_additional_settings |
文字列 | 追加設定 (空でよい) |
PHP serialized array は WP-CLI に --format=json を付ければ JSON から自動変換されます。手で serialize() 形式を組み立てる必要はありません。
3. ハマりポイント 2 つ
3-1. wp post meta update <id> <key> - の stdin マーカーは --format=json と併用できない
長い JSON 値を渡すとき、最初は WP-CLI の stdin マーカー - で楽をしようとしました:
echo '{"active":true,...}' | wp post meta update 30 _mail - --format=json
これは失敗します。エラーは Error: Invalid JSON: - 。--format=json を付けると、WP-CLI は値を JSON-decode しようとしますが、その前に - を「stdin から読む」ではなく「リテラル - 」として解釈してしまいます。
正解: 通常の位置引数として直接渡します。shlex.quote を通せばシェルエスケープも問題ありません。
wp("post", "meta", "update", str(form_id), "_mail",
json.dumps(mail, ensure_ascii=False), "--format=json")
json.dumps の出力は単一行で、シングルクォート含まないので shlex.quote で囲めば bash → SSH → WP-CLI まで素直に通ります。
3-2. _form の改行を保つには JSON を経由しないこと
_form は文字列ですが、改行を含みます:
<label> 氏名
[text* your-name autocomplete:name] </label>
これを そのまま wp post meta update <id> _form "<multi-line string>" で渡せば、改行は単一引用符の中で保持されたまま PHP 側に届きます (Python の subprocess は引数として渡すので、bash で評価されない)。--format=json を付けて JSON 文字列で渡すと、\n のリテラル化で逆に問題が出るので、ここは plain text で渡すのが正解です。
つまり、
| meta | フォーマット |
|---|---|
_form |
plain text (生の改行を保つ) |
_mail / _mail_2 / _messages |
--format=json で JSON 渡し |
_locale / _additional_settings |
plain text |
の使い分けが要点です。
4. スクリプト全文
scripts/setup_contact_form_7.py (一部省略、リポジトリでフル公開):
"""Set up Contact Form 7 from scratch via WP-CLI over SSH."""
import argparse
import json
import shlex
import subprocess
import sys
SSH_HOST = "xserver-aishacho"
WP_PATH = "~/ai-shacho.com/public_html"
def ssh_run(cmd: str, timeout: float = 60.0) -> str:
full = f"cd {WP_PATH} && {cmd}"
r = subprocess.run(
["ssh", SSH_HOST, full],
capture_output=True, text=True, encoding="utf-8", timeout=timeout,
)
if r.returncode != 0:
sys.exit(f"[ssh fail] {cmd}\n stderr={r.stderr}")
return r.stdout.strip()
def wp(*args: str) -> str:
cmd = "wp " + " ".join(shlex.quote(a) for a in args)
return ssh_run(cmd)
def build_form_template() -> str:
return (
"<label> 氏名\n"
" [text* your-name autocomplete:name] </label>\n\n"
"<label> メールアドレス\n"
" [email* your-email autocomplete:email] </label>\n\n"
"<label> 題名\n"
" [text* your-subject] </label>\n\n"
"<label> メッセージ本文 (任意)\n"
" [textarea your-message] </label>\n\n"
"[submit \"送信\"]"
)
def build_mail(recipient: str) -> dict:
return {
"active": True,
"subject": "[_site_title] \"[your-subject]\"",
"sender": f"[_site_title] <{recipient}>",
"recipient": recipient,
"body": (
"差出人: [your-name] [your-email]\n"
"題名: [your-subject]\n\n"
"メッセージ本文:\n[your-message]\n\n"
"-- \n本メールはあなたのウェブサイト ([_site_title] [_site_url]) "
"のコンタクトフォームに送信があったことをお知らせするものです。"
),
"additional_headers": "Reply-To: [your-email]",
"attachments": "",
"use_html": True,
"exclude_blank": True,
}
def main() -> int:
ap = argparse.ArgumentParser()
ap.add_argument("--form-title", default="お問い合わせ")
ap.add_argument("--page-title", default="お問い合わせ")
ap.add_argument("--recipient", default="contact@ai-shacho.com")
args = ap.parse_args()
# 1. plugin install + activate (idempotent)
wp("plugin", "install", "contact-form-7", "--activate")
# 2. create CF7 form post
form_id = int(wp(
"post", "create",
"--post_type=wpcf7_contact_form",
f"--post_title={args.form_title}",
"--post_status=publish",
"--porcelain",
))
# 3. set meta values
wp("post", "meta", "update", str(form_id), "_form", build_form_template())
wp("post", "meta", "update", str(form_id), "_mail",
json.dumps(build_mail(args.recipient), ensure_ascii=False),
"--format=json")
# _mail_2 と _messages も同様 (省略・リポジトリ参照)
wp("post", "meta", "update", str(form_id), "_locale", "ja")
# 4. create page with shortcode
body = (
"ご質問・お仕事のご依頼はこちらからお願いします。\n\n"
f'エラー: コンタクトフォームが見つかりません。
\n'
)
page_id = int(wp(
"post", "create",
"--post_type=page",
f"--post_title={args.page_title}",
f"--post_content={body}",
"--post_status=publish",
"--porcelain",
))
site_url = wp("option", "get", "home")
print(f"[OK] form_id={form_id} page_id={page_id}")
print(f" URL: {site_url}/?page_id={page_id}")
return 0
if __name__ == "__main__":
sys.exit(main())
_mail_2 (自動返信メール) と _messages (バリデーションメッセージ 22種) は紙面の都合で省略しました。フル版はリポジトリの scripts/setup_contact_form_7.py を参照してください。
5. 実行ログと所要時間
実行コマンド:
python scripts/setup_contact_form_7.py \
--form-title "お問い合わせ" \
--page-title "お問い合わせ" \
--recipient contact@ai-shacho.com
実行ログ:
[1/4] install + activate contact-form-7 (idempotent)
[2/4] create CF7 form: お問い合わせ
[3/4] set meta on form_id=32
[4/4] create or update page: お問い合わせ
[OK] form_id=32 page_id=33
URL: https://ai-shacho.com/?page_id=33
各ステップの所要時間:
| ステップ | 所要 |
|---|---|
| プラグイン install + activate | 約 30秒 |
| フォーム post + メタ 6 件投入 | 約 1分 |
| お問い合わせ固定ページ作成 | 約 10秒 |
| メール送信テスト (UI で 1 回) | 約 2分 |
合計 約 4分。残り 1分はメール届確認の待ち時間です。前回の UI 手作業 20分から 80% 短縮できました。
レンダリング確認:
Invoke-WebRequest "https://ai-shacho.com/?page_id=33" `
| Select-Object -ExpandProperty Content `
| Select-String -Pattern 'wpcf7-form|your-name|your-email'
wpcf7-form クラス・your-name・your-email フィールドがすべてレンダリング側に出ていれば成功です。
6. 次のサイトに使い回すときのカスタマイズ
本記事のスクリプトは XServer (エックスサーバー
) 上の WordPress 前提で書いています。SSH 接続が標準で使えてWP-CLI が /usr/bin/wp に入っているため、追加セットアップなしでこのスクリプトが動きます。
スクリプトのトップに 2 つの定数があります:
SSH_HOST = "xserver-aishacho"
WP_PATH = "~/ai-shacho.com/public_html"
新サイトでは ~/.ssh/config に新サイト用 Host を追加し、SSH_HOST と WP_PATH を書き換えれば動きます。実引数 --recipient でメール送信先を切り替えれば、フォームの _mail.recipient が新ドメインの問い合わせ用アドレスになります。
CF7 のフォーム定義 (build_form_template) や送信本文 (build_mail) はサイトごとに変えたい場合があるかもしれません。その場合は YAML 設定ファイルから読み込む形に拡張すると、サイト名 + recipient のペアを並べておくだけで全サイトに同じ仕様を投入できます。
まとめ
WP-CLI と Python の組み合わせは、Contact Form 7 のような「UI で 20分かかるが裏側は単純なメタ値更新」系の作業と相性が良いです。
ハマりポイントは「- stdin マーカーが --format=json と併用不可」と「_form は plain text で渡す」の 2 点だけ把握しておけば、CF7 以外の WordPress プラグインも同じパターンで自動化できます。
次に読むおすすめ
- XServer × SWELL でブログを立ち上げた初日ログ (2026年版)
- SWELL カスタマイズの WP-CLI Python 自動化 (準備中)
CTA
X 会社アカウントをフォロー: @ai_shacho_jp — 日々の自動化スクリプト・実装ログを投稿しています。
