ブログを長くやっていると、いつの間にか思考の順番が逆立ちしてくる。
本来は「作ったから書く」のはずだった。それがある時期から「書くために、何か作れることはないか」を探すようになっていた。ネタ帳を眺めて、PVが取れそうなキーワードから逆算して、その器に合う題材をひねり出す。記事が主で、行動が従。完全に主従が入れ替わっていた。
この記事は、その順番を元に戻した最初の一本だ。題材は大層なものではない。「自分が書いた記事のMarkdownを、コマンド一発でWordPressに下書き投入する道具」を作って動かした、それだけの話である。ただ、作る順番を正したら、副産物がそのまま記事になった。今あなたが読んでいるこの初稿も、その道具で投入されたものだ。
記事を「商品」だと思うのをやめた日
きっかけは、検索流入が静かに痩せていく体感だった。
数字の話は他所に詳しい人がいくらでもいるので深追いしないが、ざっくり言えば「『〜とは』『〜のやり方』で検索した人が、もう記事まで来ない」時代になりつつある。AIが検索結果の上で要約を返してしまうからだ。情報系の記事は、AIが無料で代わりに喋ってくれる。書き手としては「では私が書く意味は」と立ち止まらざるを得ない。
そこで考え方を一段ずらした。記事をPVで稼ぐ商品として扱うのをやめて、自分が実際に手を動かして作って動かしたものの記録に寄せる。AIが要約で溶かせるのは借り物の解説だけで、「私がこれを作って、こうハマって、こう直して、今も動いている」という痕跡は溶かせない。なら、そっちを書けばいい。
問題は、その路線だと「書くために作った感」が出た瞬間に嘘くさくなることだった。だから逆にした。作りたいものを作る。記録はそのおまけ。 今回はその実験第一号である。
思いつき:記事のMarkdownを、コマンド一発で流せないか
普段、文章はエディタでMarkdownで書いている。一方、公開先のWordPressは管理画面に貼り付けて整える。この「Markdownで書く」と「管理画面で貼る」の間に、地味に毎回手作業の谷がある。コピペして、ブロックがおかしくなって、直して、という例のやつだ。
ここをまたぎたかった。理想はこうだ。
手元のMarkdownファイルを指定して、コマンドを一回叩くと、WordPressに下書きとして記事が立つ。
公開はしない。あくまで下書きまで。最後の公開ボタンは自分の指で押したい。この線引きは後で効いてくるので覚えておいてほしい。
調べると、WordPressには標準でREST APIという出入口がある。外部からプログラムで記事を読み書きできる仕組みだ。これを使えば管理画面を経由せずに記事を投入できる。理屈の上では、の話だが。
まず疎通だけ確かめる:REST APIは本当に生きているか
新しい仕組みを相手にするとき、いきなり本番の機能を書き始めるのは悪手だと経験上知っている。まず「向こうが息をしているか」だけを確かめる。
WordPressのREST APIは、バージョン4.7以降は標準で有効になっている(※一次情報:WordPress REST API Handbook)。生きているかの確認はあっけないほど簡単で、ブラウザで 自分のドメイン/wp-json/wp/v2/posts を開いてJSONがズラッと返ってくれば、もう動いている。
次に認証だ。記事を読むだけなら認証は要らないが、書き込むには「お前は誰だ」を通す必要がある。ここで使うのが Application Password という仕組みだった。WordPress 5.6からコアに標準同梱されている機能で、プラグインを足さなくていい(※一次情報:Authentication — REST API Handbook)。管理画面の ユーザー → プロフィール編集 の下の方に、専用のパスワードを発行する欄がひっそり用意されている。
発行すると、スペース区切りの呪文みたいな文字列が一度だけ表示される。これを控えて、HTTPS経由でBasic認証として渡す。普段のログインパスワードとは別物なので、漏れても本体は無事だし、要らなくなったら個別に失効できる。よくできている。
ここまでで、まず「読む」だけの最小スクリプトを書いた。下書き一覧を取得するだけ。私の環境では、これがあっさり通った。向こうは確かに息をしていた。第一関門突破である。
つまずきメモ:認証・権限・SANGOブロックの三つの壁
……と、ここまで読むとスルスル進んだように見えるが、当然そんなわけはない。三回まあまあ派手に転んだ。供養も兼ねて書いておく。
壁その一:認証で401が返り続ける。 最初、Application Passwordをコピーするときに、表示されたスペースをご丁寧に全部詰めて貼っていた。あの呪文、スペースもセットで意味があるのか・ないのか確信が持てず、結果として認証が通らない。「公式に標準機能だと書いてあるのに通らない、ということは私の理解が間違っている」と腹をくくって素直に確認したら、渡し方の問題だった。新しい仕組みを疑う前に自分を疑え、という毎度の教訓。
壁その二:誰の権限で書いているのか問題。 読むのは通っても、書く権限があるかは別の話だ。投稿を作成・更新できるのは、その操作に足る権限を持ったユーザーに限られる。私の環境では管理者ユーザーで叩いていたので投稿・更新とも通ったが、これは「公式仕様」ではなく「私の環境ではこうだった」という実測である。同じことをやる人は、自分のユーザーの権限を一度確認した方がいい。
壁その三:SANGOブロックの呪い。 これが地味に一番効いた。set-ten.comは長らくSANGOというテーマを使っていて、過去記事の本文はSANGO独自のブロックで埋め尽くされている。最初、その作法に寄せて投入しようとして崩れた。考えてみれば当たり前で、テーマ固有のブロックはテーマを替えた瞬間に全部ただのゴミになる。今回は脱SANGOも見据えていたので、ここで方針を固めた——投入する本文は、テーマに依存しない標準的な形式に限る。 特定テーマに魂を売らない。これで、将来テーマを替えても本文は生き残る。怪我の功名だった。
3モード1スクリプトに落ち着いた理由
機能を考えていくと、欲しい動作は三つに整理できた。
- read:今ある下書きの一覧を読む(疎通確認とデバッグ用)
- post-test:ダミーの下書きを一件だけ投入してみる(書き込み権限の確認用)
- from-md:指定したMarkdownファイルを下書きとして投入する(本番)
これを最初、別々のスクリプトに分けるか迷った。だが分けると、共通する認証処理や接続設定が三箇所に散らばってメンテが面倒になる。結局、一本のスクリプトに第一引数でモードを切り替える形に落ち着いた。read post-test from-md を引数で渡し分けるだけ。共通部分は一箇所、差分はモードごとの関数に閉じる。地味だが、後で自分が読み返したとき一番事故りにくい構成だと思っている。
派手なフレームワークは使っていない。Pythonの世界でHTTPを叩く定番ライブラリと、Markdownを変換する定番ライブラリの二つだけ。理由は次に書く。
安全装置:なぜ全部draft既定で、公開は人間がやるのか
このスクリプト、全モードで投入時のステータスを下書き(draft)に固定している。 公開(publish)は一切やらない。
REST APIの仕様上は、ステータスに publish(公開)も draft(下書き)も指定できる(※一次情報:Posts — REST API Handbook のステータス一覧)。つまり「コマンド一発で公開」も技術的には可能だ。だが、あえてやらない。
理由は単純で、自動化していい工程と、人間が最後に見るべき工程は違うからだ。Markdownを変換して投入する、という機械的な作業は自動化していい。だが「これを世に出すか」の最終判断は、コマンドの勢いで通していいものではない。誤字、事実誤認、出す前に気が変わる——どれも、公開ボタンを自分の指で押す一拍があれば防げる。
自動化の設計で一番大事なのは「どこまでを機械に渡し、どこからを人間が握るか」の線引きだと思っている。全部自動が偉いわけではない。今回はその線を「投入=機械/公開=人間」に引いた。
鍵をリポジトリに置かない:認証情報を環境変数に逃がす
もう一つの設計判断。認証情報(ユーザー名とApplication Password)を、スクリプトにもリポジトリにも一切書いていない。 環境変数から読む形にしている。
これは半ば常識だが、常識ほど事故が起きる。コードに直接パスワードを書くと、それをうっかりGitにコミットした瞬間に、履歴に永久に刻まれる。後から消しても履歴には残る。set-ten.comのリポジトリはプライベートだが、「プライベートだから大丈夫」で鍵を平文で置くのは、鍵を玄関マットの下に隠すのと同じ発想だ。
なので、スクリプトは起動時に環境変数を読みに行くだけ。鍵そのものは自分の手元の環境にしか存在しない。リポジトリには「環境変数から読みます」という事実しか書かれていない。これなら万一リポジトリが流出しても、鍵は流出しない。
実際に動かす:この記事の初稿を、その道具で下書き投入した
さて、ここが今回の一番の見どころだ。
このスクリプトの最初の本番投入対象は、ダミー記事でも過去記事でもなく、今あなたが読んでいる、この記事そのものだった。手元でこの原稿をMarkdownで書き終え、from-md モードにファイルを渡して、下書きとして投入した。
自分の作った道具で、その道具について書いた記事を、自分のブログに立てる。
ちょっとした再帰というか、蛇が自分の尻尾を咥えているような構図で、書いていて少し笑った。デモのためのデモではなく、実際の制作物が実際の仕事をこなした瞬間だ。設計書に書いた「作った副産物が勝手にネタになる」が、文字通り目の前で起きた。
正直に補足しておく。この道具がやったのは「初稿の投入」までだ。 投入後、この文章を管理画面で読み返し、手を入れて整えてから公開している。つまり今の完成形そのものをコマンドが吐いたわけではない。from-mdモードは投入専用で、投入後の更新は別の操作になる(更新自体はREST API上は POST /wp/v2/posts/<id> で可能だが、今回の道具にはまだ積んでいない)。「全自動でこの記事が出来上がった」と書いた方が話は派手だが、それは事実と違う。動く証拠で語ると決めた以上、ここは正確に書く。
一次情報メモ(出典)
この記事で技術仕様に触れた箇所の裏取り先を残しておく。執筆時点での確認である。
- WordPress REST API のエンドポイント・投稿ステータスの一覧:Posts – REST API Handbook
- Application Password とBasic認証での渡し方:Authentication – REST API Handbook
一方で、「管理者ユーザーで投稿・更新が通った」「セキュリティ系プラグインにブロックされなかった」「タイムゾーンが想定通りだった」といった点は、公式仕様ではなく私の環境での実測だ。サーバーの設定やプラグイン構成で変わりうるので、同じことを試す人は自分の環境で確かめてほしい。
次に作るもの、と方針の答え合わせ
冒頭で「作る順番を正したら副産物が記事になった」と書いた。実際にやってみての答え合わせをすると、想像以上に手応えがあった。
ネタを探して書いた記事は、書いている間ずっと「これ需要あるかな」という不安がつきまとう。だが今回は違った。まず自分が欲しい道具を作って、現に動いて、その過程をそのまま書いただけだ。需要を当てにいっていないから、外しても痛くない。そして借り物ではないから、AIに要約で溶かされない。少なくとも、書いていて嘘がなかった。
次は、この道具自身を育てる方向と、別の道具を作る方向、両方が見えている。投入だけでなく更新も叩けるようにするのか、画像の扱いをどうするのか——課題はまだある。だがそれも、作って動かして詰まったら、また記録すればいい。
作る。動かす。詰まる。直す。書く。この順番でしばらく回してみる。
🖖 Live long and learn.
