CHAPTER 8 ・ 仕事で使える形にする / Lesson 32
定期実行と通知の考え方
毎朝・毎月、自動で動かす。タスクスケジューラの使い方と、完了や失敗の知らせ方、秘密情報の扱いです。
このレッスンが終わったら
「自動で動かすには、スケジューラに登録する。失敗に気づける仕組みと、パスワードをコードに直接書かない工夫が必要」と言える。
こんな場面、ありませんか?
毎月1日の朝に、前の月の売上レポートを作る仕事があります。Lesson 31 で、python report.py --month 2025-09 と打てば動く形になりました。それでも、毎月こうなりがちです。
- 1日が休み明けで、うっかり忘れる
- 担当者が休みの日に、誰も動かさない
- 動かしたつもりが、途中でエラーになっていたことに、数日後に気づく
「決まった日時に、人の手を借りずに動かす」ことが、自動化の入り口です。ただし、自動で動くということは、誰も見ていない場所で動くということでもあります。そのため、次の3つを一緒に整えます。
- いつ動かすかスケジューラに登録する
- 動いたか分かるかログと、終了コード、通知
- 秘密を守れるかパスワードをコードに書かない
手で動かせる形を、先に作る
自動化の前に、手で動かして、毎回同じ結果になることが必要です。スケジューラは、--month を毎月打ってはくれません。そこで、--month を省いたときは、前の月を自動で選ぶようにします。「今の日付」を使う部分は、日付を引数にして、動かして確かめられる形にしておきます。
▶ 実行を押すと、ここに結果が出ます
10月1日の前の月は 2025-09、1月1日の前の月は、年をまたいで 2025-12 になります。これを、report.py の初期値に使います。(date.today() は、実行のたびに値が変わるため、このページでは動かしません)
parser.add_argument("--month", default=last_month(date.today()), help="省略すると、前の月")次に、スケジューラが動かしやすいように、コマンドをまとめた バッチファイル(Windows で、コマンドを順に実行するファイル)を作ります。メモ帳で、次の内容を run_report.bat という名前で保存します。
@echo off
cd /d C:\Users\tanaka\python-work\sales-report
.venv\Scripts\python.exe report.py >> logs\run.log 2>&1
echo exit code: %errorlevel% >> logs\run.log作業フォルダの中に、logs フォルダも作っておきます。保存した run_report.bat を、ダブルクリックして、手で動かしてみてください。logs\run.log に、実行の記録が残れば成功です。ここまでできてから、自動にします。
仕組みを読む
@echo off実行中の命令を、画面にいちいち表示しないcd /d C:\Users\tanaka\...\sales-report作業フォルダへ移動する(/d は、ドライブも切り替える印).venv\Scripts\python.exe report.py仮想環境のPythonで、report.py を動かす(有効化の操作は不要)>> logs\run.log 2>&1画面に出る文字(エラーも)を、ログファイルの末尾に書き足すecho exit code: %errorlevel%直前のコマンドの終了コードを、ログに書く日本語にすると
ここで大事なのが、終了コードです。プログラムは終わるとき、OSに数字を1つ伝えます。0 は成功、0以外は失敗という決まりです。スケジューラやバッチファイルは、この数字で、成功か失敗かを知ります。Python では、main() が数を返し、最後に sys.exit() へ渡します。
if __name__ == "__main__":
sys.exit(main())ブラウザでは sys.exit() を使わず、main() が返す数字を見てみます。
▶ 実行を押すと、ここに結果が出ます
Mac や Linux では、cron(決まった時刻にコマンドを動かす仕組み)が、同じ役目をします。1行が、1つの予定です。
0分(0分)8時(8時)1日(1日)*月(毎月)*曜日(どれでも)cd ~/sales-report && ...動かすコマンドターミナルで crontab -e と打って開く画面に、この1行を書き足して保存します。Mac では、cron が許可されていない設定もあるので、社内の案内に従ってください。
やってみる:登録と、失敗の知らせ方
タスクスケジューラは、Windows に入っている、自動実行の係です。次の手順で、run_report.bat を毎月1日の朝に動かします。画面の表記は、Windows のバージョンで少し違います。
- スタートメニューで「タスク スケジューラ」と入力して、開く。
- 右側の「基本タスクの作成」を押し、名前を「月次売上レポート」にする。
- トリガー(いつ動かすか)は「毎月」。開始の日時、月は「すべて」、日は「1」、時刻は朝8時にする。
- 操作は「プログラムの開始」。「プログラム/スクリプト」に
run_report.batのフルパス(C:\から始まる場所)、「開始(オプション)」に作業フォルダを入れる。 - 完了を押す。一覧に出たタスクを右クリックして「実行する」を選び、試しに動かす。
logs\run.logが増えたか確かめる。 - タスクの「プロパティ」で、次を確認する。「全般」…「ユーザーがログオンしているかどうかにかかわらず実行する」にするとき、パソコンのパスワード入力が要ります(会社の方針を確認)。「条件」…スリープ中でも動かすなら、「スリープ解除」にチェック。「設定」…「タスクがすでに実行中の場合」の規則を、「新しいインスタンスを開始しない」にする(二重実行の防止)。
Mac / Linux では、前のSTEPの cron の1行を登録するだけです。
② 失敗に気づく仕組みを作る誰も見ていない場所で失敗したら、誰かに知らせる必要があります。段階は3つあります。
- ログ:
run.logに、何が起きたかを残す(Lesson 12)。 - 終了コード:失敗したら 0 以外を返す。タスクスケジューラの「前回の実行結果」でも確認できる。
- 通知:完了や失敗を、メールなどで知らせる。
メール送信は、SMTPという決まりごとで行い、Python では smtplib を使います。次のコードは、このページでは動かしません。実際に送るには、社内のメールサーバーの情報が必要です。
import logging
import os
import smtplib
from email.message import EmailMessage
def send_mail(subject, body):
msg = EmailMessage()
msg["Subject"] = subject
msg["From"] = "report@example.co.jp"
msg["To"] = os.environ["REPORT_MAIL_TO"]
msg.set_content(body)
with smtplib.SMTP("smtp.example.co.jp", 587) as server:
server.starttls()
server.login(os.environ["REPORT_MAIL_USER"], os.environ["REPORT_MAIL_PASSWORD"])
server.send_message(msg)
def main():
try:
run_report()
send_mail("月次レポート:完了", "正常に終わりました。")
return 0
except Exception:
logging.exception("月次レポートで失敗しました")
send_mail("月次レポート:失敗", "ログ(logs\\run.log)を確認してください。")
return 1ふだんは except Exception を避けますが、ここは最上位の1か所だけです。ログに全文を残し、通知し、終了コード 1 で終わるので、握りつぶしてはいません。
上のコードの os.environ["REPORT_MAIL_PASSWORD"] は、パスワードを環境変数から読んでいます。コードに直接書くと、コードを人に見せたり、共有フォルダに置いたりしたときに、パスワードも一緒に漏れます。
悪い例:コードに書く
server.login("report@example.co.jp", "Sakura2025!")良い例:環境変数から読む
server.login(os.environ["REPORT_MAIL_USER"], os.environ["REPORT_MAIL_PASSWORD"])
環境変数は、Windows では、スタートメニューで「環境変数」と入力し、「アカウントのユーザー環境変数を編集」から、名前と値を登録します。登録したあとは、開いていたコマンドプロンプトを一度閉じて、開き直すと読めます。ブラウザでは、設定されていない場合の動きだけ確かめられます。
▶ 実行を押すと、ここに結果が出ます
os.environ["名前"] は、なければ KeyError で止まります。os.environ.get("名前") は、なければ None(または指定した初期値)を返します。「設定漏れに気づいて止めたい」ときは前者、「なくても動く値」には後者を使います。
環境変数のほかに、.env(イーエヌブイ)という設定ファイルに書き、python-dotenv という道具で読む方法もあります。この .env ファイルは、他の人に渡さない・共有フォルダに置かない・バージョン管理(Git)の対象から外す(.gitignore に .env と書く)のが約束です。
つまずきやすい点を確かめる
自動実行は、手で動かしているときには起きない失敗をします。先回りして確認しましょう。
| 落とし穴 | 何が起きるか | 対策 |
|---|---|---|
| パソコンが寝ている・電源が切れている | 予定の時刻に動かない | スリープ解除、取りこぼしを後で実行する設定。重要なものは、共有のパソコンで動かす |
| ネットワークドライブ(Z: など) | 自動実行では、ドライブの割り当てが見えない | \\server\share\... の形(UNCパス)で書く |
| 作業フォルダが違う | ファイルが見つからない | cd /d と、タスクの「開始(オプション)」で指定する |
| パスワードの変更 | 「ログオンしているかにかかわらず」のタスクが、動かなくなる | 変更したら、タスクの資格情報も更新する |
| 二重実行 | 前の実行が終わらないうちに、次が始まり、同じファイルを壊す | 「新しいインスタンスを開始しない」、または下のロックファイル |
| 月初が休日 | 元データが、まだそろっていない | 前日の入力が終わる時刻の後にする。営業日の判定は Lesson 03 |
二重実行は、コードの側でも防げます。実行の最初に report.lock というファイルを新しく作り(mode="x" は、すでにあればエラーになる、Lesson 29 の書き方)、作れなければ「実行中」と判断して、やめます。
▶ 実行を押すと、ここに結果が出ます
2回目の起動は、はじかれました。finally があるので、集計が途中で失敗しても、ロックファイルは削除されます(消し忘れると、次回から動かなくなるため)。
最後に、登録前の確認リストです。自分の仕事で、順に確かめてください。
- □ 手で動かして、毎回同じ結果になる(
--monthを省いても動く) - □ 試しに動かして、
run.logに記録が残る - □ 失敗したとき、終了コードが 0 以外になり、通知かログで気づける
- □ パスワードやAPIキーは、コードに書かず、環境変数から読んでいる
- □ ネットワークドライブは、UNCパスで書いてある
- □ 会社のルールで、自動実行・メール送信を許可されている
仕事ならどう使う?
理解チェック自動で動かしたプログラムが失敗したとき、スケジューラやバッチファイルが失敗に気づくための仕組みは?
理解チェックメール送信用のパスワードの扱いとして、適切なのは?