毎朝、担当者が数十ページを順番に開いて、その日の状況を1枚の表にまとめている。1ページあたり数十秒でも、対象が数百件あれば午前中がそれだけで終わります。この種の作業は、夜のうちにサーバー側で回してしまえば消えます。ただし「回るようにする」のと「回り続ける」のは別の話で、実際にトラブルになるのは後者のほうです。失敗しても誰も気づかない仕組みを作ってしまうと、止まってから発覚するまでの数日分のデータが丸ごと消えます。ここでは、実際に作った夜間バッチの中身と、止まったことに気づくための作り方を書きます。
実際に作ったもの:800件超のページを巡回して、CSVにする
多数の店舗ページを扱う情報サイトを運営する事業者から、「その日どのスタッフが出ているかを、まとめて把握したい」という相談を受けました。条件は2つで、サイト上に表示する必要はないこと、社内で確認できればよいことです。表示要件がないため、デザインの調整が一切発生しない、珍しく輪郭のはっきりした案件でした。
作ったものは単純です。各ページのカスタムフィールドに、確認先のURLを1件ずつ持たせる。夜間に file_get_contents でそのURLを順に取得し、HTMLの中の特定の文字列があるかどうかで在籍と出勤を判定する。結果は独自のテーブルに1日分として書き込む。管理画面に画面を1つ足して、その日の結果をCSVでダウンロードできるようにする。これで50,000円の案件です。
実測値をひとつ残しておきます。約850件のページを巡回して、6分弱でした。件数から所要時間を見積もるときの基準として、いまも使っています。1件あたり0.4秒ほどで、この中には相手側のサーバーの応答待ちが含まれます。
設計で1つだけ判断したことがあります。収集する処理と、CSVを書き出す処理を、同じファイルにまとめずに分けたことです。片方はcronから呼ばれ、もう片方は管理画面から呼ばれる。呼ばれ方も、動く頻度も、直したくなる理由も違います。実際、納品後に「CSVに列を足してほしい」という要望が3回来ましたが、いずれも出力側のファイルだけを直せば済み、当日中に反映できました。収集ロジックに一切触らずに済んだのは、この分割のおかげです。
細かい点をひとつ。日本語を含むCSVは、最初からShift_JISで出力してください。担当者はExcelで開きます。UTF-8で出すと文字化けの問い合わせが必ず来ます。文字コードまわりの扱いはCSVの取り込みで文字化けを起こさないための処理と、確認の手順のほうに整理しています。
レンタルサーバーのcronで、最初に踏む段差
共用のレンタルサーバーでcronを組む場合、最初に決めるのは「URLを叩くのか、コマンドで実行するのか」です。curl でPHPファイルのURLを叩く形は設定が簡単ですが、Webサーバー経由になるため実行時間の上限に引っかかりやすく、しかもそのURLを外部から誰でも叩けてしまいます。処理が数分に及ぶなら、サーバー上のPHPをコマンドラインから実行する形にしてください。実行時間の制約が緩く、URLも露出しません。
WordPressの中の処理を動かす場合、標準の擬似cronはサイトへのアクセスがあったときにしか走りません。誰も来ない深夜に確実に動かしたいなら、wp-config.php で DISABLE_WP_CRON を有効にしたうえで、サーバー側のcronから wp-cron.php を定期的に叩く構成に切り替えます。
そしてもう一つ、実際に踏んで痛かった段差を書いておきます。コントロールパネルからcronを登録する形式のサーバーで、実行時刻とコメント欄の内容が同一の登録が「同じもの」として扱われ、後から登録した設定が先の設定を置き換えてしまう、という挙動に当たったことがあります。同じサーバーアカウントで2つのサイトを運用していて、2つ目のサイトに同じ内容のcronを同じ時刻で登録した結果、稼働していた1つ目のサイトの収集が止まりました。しかもエラーは出ません。データが増えなくなるだけです。数日後に「表示されない」と連絡が来て発覚しました。
対策は簡単で、実行時刻をずらす(たとえば5時30分と5時40分)か、コメント欄にサイト名を入れて一意にするかのどちらかです。複数サイトを同じサーバーで運用している場合、テーマやプラグインは完全に独立していても、cron・データベース・PHPのバージョン・WAFの設定といったサーバーパネル側の設定は共有されています。1つのサイトへの追加作業が別のサイトを壊す構造が、そこに潜んでいます。横展開の作業では、新しく足した側だけでなく、既存側の動作確認まで完了条件に含めてください。
症状から原因へ、最短で降りる
cronで動く仕組みは、UIの不具合に見える形で報告が上がってきます。実際に受けた報告と、そのときの真因を並べます。
| 報告された症状 | 真因 | 先に見る場所 |
|---|---|---|
| ボタンを押しても何も出てこない | 収集が一度も走っておらず、データが存在しない | テーブルの中身。コードより先にデータ |
| 同じ行が2件ずつ表示される | バッチの二重実行による二重登録 | 実行ログ。表示側は見ない |
| 導入した当日だけ空になる | 当日分のみを出力する仕様。初日は必ず空 | 仕様どおり。案内文の不足 |
| ある日から件数が減った | 参照先のHTML構造の変更で判定が外れた | 取得したHTMLの実物 |
共通しているのは、表示側を疑っても何も出てこないという点です。cron方式の機能では、まずデータそのものを見てください。テーブルに行があるのか、何時の行なのか、件数はいくつか。これで大半が終わります。
成功したことも通知する
失敗したときだけ通知する設計は、一見合理的ですが穴があります。cronの登録そのものが消えたり、サーバー側の都合で実行されなくなったりした場合、失敗すら発生しないので何も届きません。静かに壊れて、誰も気づかない状態です。
毎日1通、結果を送ってください。「昨夜の収集:837件、取得失敗3件、所要5分52秒」。この1行が届いていれば動いていますし、届かなければ止まっています。担当者は中身を読まなくてかまいません。届くかどうかだけが情報です。あわせて、処理の開始時刻・終了時刻・件数・エラー件数を1行のログとしてテーブルに残しておくと、後から「いつから止まっていたのか」を数分で確定できます。
二重起動の防止も入れておきます。前回の処理が終わらないうちに次が始まると、同じデータが2回入ります。ロックファイルを作って flock で押さえるのが手軽で、処理の冒頭でロックが取れなければ何もせず終了する、という数行で済みます。
公開したあとに壊れる場所
外部のページから文字列一致で情報を取り出す仕組みは、相手側のHTMLが変われば無言で壊れます。エラーにならず、判定だけが常に外れるようになる。これは構造上どうしても残るリスクなので、納品時に必ず伝え、そのうえで「取得件数が0の日が続いたら通知する」といった監視を入れておきます。取得そのものの設計と、やってよい範囲の線引きについてはWebスクレイピングを業務に使うときの設計と法的な線引きにまとめました。
件数の増加も効いてきます。850件で6分弱ということは、数千件になればPHPの実行時間の上限や、共用サーバーの負荷制限に触れます。件数が増える見込みがあるなら、最初から分割実行できる形にしておくか、1回あたりの上限件数を設定値として外に出しておいてください。
もう一つ、ディレクトリ構成の変更で止まる例があります。サイトのSSL化でドキュメントルートが変わった環境で、サブディレクトリに置かれたアプリケーションを別の場所へコピーしようとしたことがあります。作業前にディレクトリの中を確認したところ、実行用のシェルスクリプトが置かれていることに気づいてコピーを中止しました。実行パスが固定されたスクリプトがある状態でディレクトリを二重化すると、同じ処理が2か所から走り、データが壊れます。定期実行が動いている環境をコピー・移動する前には、cronの登録一覧と常駐スクリプトの有無を必ず確認してください。
運用開始の初日についても書いておきます。「当日分を集めて出す」という仕組みは、稼働した日は必ず空になります。夜間に1度回って初めてデータが入るからです。ここを案内し忘れると、公開したその日に「動いていない」という連絡が来ます。納品の案内には「翌朝以降にご確認ください」を必ず入れてください。管理画面に追加した画面の場所も、直接のURLと合わせて書き添えます。メニューのどこに増えたかは、作った側が思うほど見つけてもらえません。
よくある質問
共用のレンタルサーバーでも組めますか
組めます。コントロールパネルにcronの設定画面があるプランなら、そこから登録するだけです。無い場合や、実行できるコマンドが制限されている場合は、外部の監視サービスから定期的にURLを叩く形で代替します。ただしその場合はURLが外部から叩ける状態になるので、トークンでの認証を必ず付けてください。
実行する時刻はどう決めればよいですか
参照先のサイトが更新されたあとで、かつ担当者が出社する前です。取得先が夜間に更新される性質なら早朝、日中に更新されるなら夕方以降になります。時刻は後から変えたくなるので、設定値として外に出しておき、変更できる旨を引き渡し時に伝えておくと安心してもらえます。
Zapierのようなサービスでは足りませんか
件数が少なく、処理が単純なら足ります。足りなくなるのは、実行回数が多くて課金が跳ねるとき、取得したHTMLを解析するような処理が必要なとき、そして失敗した分を自前で再実行したいときです。切り替えの判断基準はZapierで足りなくなったとき、自作APIブリッジに切り替える判断基準に書いています。
集めたデータをサイトに表示することもできますか
できます。ただし表示する場合は、取得と表示のあいだにもう1段の検討が要ります。取得に失敗した日に古いデータを出し続けてよいのか、それとも何も出さないほうがよいのか。この判断は業務によって変わります。毎日データを取り込んでサイトに表示する仕組みを、無理なく作るのほうで整理しています。
毎日の手作業を夜間に回すこと自体は、それほど大きな開発ではありません。分かれ目は、止まったときに気づける形になっているかどうかです。いまの作業内容とサーバー環境を伺えば、自動化できる範囲と手順をお出しできます。
