Pythonプログラムを一定の間隔や定時のスケジュールで実行するには、大きく分けて以下の2つの方法があります。
- 方法1:Pythonプログラムの中に定期実行機能を実装する
- 方法2:ツールでPythonプログラムを定期実行する
方法1は、with True:
の無限ループの中で時間や時刻を毎回チェックして、定期的に処理を実行する方法です。時間や時刻のチェックは「scheduleモジュール」を利用すると簡単に実装できます。
方法2の代表的なツールは、Windowsの「タスクスケジューラ」とmacOS/Linuxの「cron」です。これらのOS標準のツールで定期的にコマンド(例えばpython3 test.py
)を実行します。
どちらを使うかは迷うところですが継続して定期実行する場合は方法2が適しています。
方法1は、Pythonプログラムを実行したままにする必要があるので、何らかの原因でPythonプログラムが止まってしまうとそこで定期実行は中断されます。
方法2は、Pythonプログラムとは関係なくシステムが毎回Pythonプログラムを実行してくれるので、定期実行自体が中断されることはありません。
ただ、方法1はプログラムで簡単に実装できるのに、方法2は「タスクスケジューラ」や「cron」の導入や設定がわかりづらいと感じることも多いです。そこで、今回はシンプルな事例を用いて、導入や設定の方法をわかりやすく説明してみました。
今回は実際に継続して運用することを想定して「VPS(仮想専用サーバー)」で「Linux OS(今回はUbuntu)」を稼働させて、「cron」で定期実行する方法を説明します。
本記事の目次
定期実行用サーバーの準備
安定した状態で定期実行を継続するには、電源や回線の維持管理を考えるとリモートのサーバーが必要になってきます。
そこで、今回は実際の運用を想定してリモートのサーバーを最初から使います。以下の記事の方法で「さくらのVPS」に構築した「Ubuntu 22.04」のサーバーを利用します。
なお、自分のパソコンで試すことも可能です。「Ubuntu」はWindows標準の「WSL2(Windows Subsystem for Linux 2)」により簡単にWindows10/11にインストールできます。また、「cron」はmacOSに標準で組み込まれていますので、いきなりVPSでは敷居が高いと感じる場合は、自分のWindowsやmacOSでまず試してみてください。
テスト用Pythonプログラムの作成
今回は自分のGmailアドレスにメールを送信するプログラムをサーバーで定時実行します。
以下のコードのmy_addr
とmy_pass
を自分の情報に書き換えて使用します。「Gmailのアプリパスワード」は、2段階認証を有効にすると取得できます(取得方法はこちらを参考にしてください)。
cron_test_mail.py
# cron_test_mail.py
import datetime
import smtplib
from email.mime.text import MIMEText
my_addr = "自分のGmailアドレス"
my_pass = "自分のGmailのアプリパスワード"
# メッセージの作成
def create_message(from_addr, to_addr, subject, body_txt):
msg = MIMEText(body_txt)
msg["Subject"] = subject
msg["From"] = from_addr
msg["To"] = to_addr
return msg
# メールの送信
def send_mail(msg):
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.ehlo()
server.starttls()
server.ehlo()
server.login(my_addr, my_pass)
server.send_message(msg)
def main():
now = datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
body = "サーバーの時刻は {} です。".format(now)
msg = create_message(my_addr, my_addr, "cron_test_mail", body)
send_mail(msg)
if __name__ == '__main__':
main()
まずは自分のパソコンで実行してみます。以下のように自分あてにメールが届き、本文に「実行時の時刻」が書き込まれているのを確認します。
サーバーにプログラムをアップロード
「scp
コマンド」や「ファイル転送ソフト」を用いて、サーバーに上記のcron_test_mail.py
をアップロードします。今回はファイルが1つだけなので、以下のscp
コマンドを使います(コマンドの使い方はこちらの記事を参考にしてください)。
scp cron_test_mail.py ユーザー名@サーバーのIPアドレス:~/
サーバーでプログラムを実行してみる
サーバーでもプログラムが問題なく実行できるか確認します。SSHでサーバーにログインして、以下のコマンドを実行します。
python3 cron_test_mail.py
以下のようにサーバーで実行しても、自分のGmailアドレスにメールが届いているのを確認します。
定期実行の管理(crontabコマンド)
定期実行のスケジュールは「crontab(cron table)」と呼ばれるスケジュール表のファイルで指定します。cronは、このファイルを見つけると中に書き込まれているスケジュールを実行します。
crontabファイルを編集や表示するには、以下のようにcrontab
コマンドを用います。
crontabコマンド一覧
crontabコマンド | 操作内容 |
---|---|
crontab -e |
crontabファイルを作成・編集する |
crontab -l |
crontabファイルの内容を表示する |
crontab -r |
crontabファイルを削除する |
定期実行の開始方法
定期実行は、crontabファイルがあれば自動でcronが読み込んで行ってくれるので、まずcrontabファイルを作成します。もしも既にある場合は、編集するかcrontab -r
で一度削除してから新規に作成します。
crontabファイルを作成するには、crontab -e
を実行します。はじめての場合は、以下のようにエディタ(editor)を選択するメッセージが表示されるので、Enterを押してデフォルトのnano(1)を選択します。
すると「空のcrontabファイル」が編集用に作成されてスケジュールを入力できるようになります。今回はcron_test_mail.py
を2分毎に実行したいので、以下のスケジュールを書き込みます(書き方は後ほど説明します)。
*/2 * * * * python3 cron_test_mail.py
以下のようにnanoの編集画面で最終行に入力したら Ctrl + X を押します。「変更されたバッファを保存しますか?」と表示されるので、Yを入力して保存します。
これでcrontabファイルが保存されてすぐに定期実行が開始されます。
以下のように2分毎に自分あてにメールが送信されます。正確に2分毎に定期実行されているのを、メールの本文に書き込んだ時刻から確認できます。
スケジュールの設定方法(crontabファイルの書き方)
crontabファイルには、以下のフォーマットでスケジュールを書き込みます。
分 時 日 月 曜日 実行するコマンド
日時を表す「分、時、日、月、曜日」の5つのフィールドは、すべて「整数」で入力します。曜日は0
から6
で指定します(0
が日曜日です)。
また、以下のように記号を用いて範囲やステップを指定することもできます。
crontabファイルで用いられる記号
記号 | 説明 | |
---|---|---|
アスタリスク | * |
有効な値をすべて指定 |
ハイフン | - |
整数の範囲(例:1-3 は1 、2 、3 を意味する) |
コンマ | , |
整数のリスト(例:1,3,5 は、1 、3 、5 の3つの整数を示す) |
スラッシュ | / |
間隔(ステップ)で指定(書き方は「範囲/step 」例:1-5/2 は、1,2,3,4,5 から2つ間隔=1 、3 、5 、*/2 はすべての値から2つ間隔=2 、4 、6 、8 ..) |
ハッシュマーク | # |
コメントアウトを意味する(コメントアウトされた部分は無視される) |
設定例
cronの使い方で、最もわかりづらいのがスケジュールの設定方法です。そこで、以下に様々な例を挙げてみましたので、参考にしながら設定してみてください。
毎時・毎日実行
1〜5つ目のフィールドを*
にすると「毎分」、2〜5つ目のフィールドを*
にすると「毎時」、3〜5つ目のフィールドを*
にすると「毎日」になります。
設定例 | 説明 |
---|---|
* * * * * コマンド |
1分毎に(毎分)実行 |
*/2 * * * * コマンド |
2分毎に実行 |
*/5 * * * * コマンド |
5分毎に実行 |
30 * * * * コマンド |
毎時30分(例:10:30, 11:30, 12:30,..)に実行 |
30 9 * * * コマンド |
毎日9時30分に実行 |
日時を指定
1〜4つ目のフィールドで、月日と時刻を指定します。曜日を表す5つ目のフィールドは*
にしておきます。
設定例 | 説明 |
---|---|
0 9 15 * * コマンド |
毎月15日の9時に実行 |
30 19 10 3 * コマンド |
3月10日19時30分に実行 |
30 0 1 4,10 * コマンド |
4月1日と10月1日の0時30分に実行 |
0 9 15 */2 * コマンド |
奇数月の15日の9時に実行 |
0 9 15 2-12/2 * コマンド |
偶数月の15日の9時に実行 |
曜日を指定
5つ目のフィールドで曜日を指定します。「毎週何曜日」と指定するには、月日を表す3つ目と4つ目のフィールドを*
にします。
設定例 | 説明 |
---|---|
30 17 * * 3 コマンド |
毎週水曜日の17時30分に実行 |
0 8 * * 0,6 コマンド |
土日の8時に実行 |
0 9 * * 1-5 コマンド |
月曜日から金曜日の9時に実行 |
定期実行の停止方法
スケジュールを破棄する場合(crontabファイルを削除する)
crontab -r
を実行するとcrontabファイルが削除されるので、定期実行も停止します。
再度定期実行を行うには、crontab -e
でcrontabファイルをあらたに作成する必要があります。
一時的に停止する場合
あとで再開する予定がある場合は、以下のように一時的に行の先頭に#
を挿入することでコメントアウトして上書き保存します(Ctrl + X の後にY)。
再開するときは、行の先頭の#
を削除してコメントアウトを解除し上書き保存すれば、すぐにまた定期実行が開始されます。
crontabのTips
print()とエラーの出力をログファイルに記録する方法
以下のように末尾に>> cron_test_mail.log 2>&1
を付け加えるとcron_test_mail.log
にprint()とエラーの出力を記録できます。
*/2 * * * * python3 cron_test_mail.py >> cron_test_mail.log 2>&1
ここで、>>
は「リダイレクト」と呼ばれ[左側のコマンドの出力]を[右側のファイル]に保存します。>>
の「追記モード」と>
の「上書きモード」の2種類があります。
それぞれ、ファイルが存在しない場合は新規に作成されますが、ファイルが存在する場合はそれぞれ「下に追記」または「上書き」で保存します。今回は、定期実行を継続するので「追記モード」にしています。「上書きモード」にすると直近のエラーしかログに残りません。
また、末尾に2>&1
を付けることで、print()とエラーによる出力の両方を1つのファイルに保存できます。どちらかだけや別々に保存したい場合は、以下のように入力します。
# print()による出力だけ保存する
*/2 * * * * python3 cron_test_mail.py >> cron_test_mail.log
# エラーの出力だけ保存する
*/2 * * * * python3 cron_test_mail.py 2>> cron_test_mail.log
# print()とエラーの出力を別々に保存する(print()はtest_std.log、エラーはtest_err.log)
*/2 * * * * python3 cron_test_mail.py >> test_std.log 2>> test_err.log
crontabファイルの内容をテキストファイルから読み込む方法
テキストファイルにcrontabファイルの内容を書き込んでおき、以下のようにcrontabコマンドの引数にファイル名を指定して読み込むこともできます。
crontab cronfile.txt
cronfile.txt
*/2 * * * * python3 cron_test_mail.py
問題なく読み込まれたかはcrontab -l
で表示して確認できます。
nanoの画面で編集しなくて済み、テキストファイルで管理できるので、スケジュールの差し替えなども便利にできます。おすすめの方法です。
cronのステータスを確認する
通常cronは最初から起動していますが、どうしてもうまく定期実行が開始されないような場合には、以下のコマンドでcronの稼働状況(ステータス)を確認してみてください。
sudo systemctl status cron --no-pager
以下のように「Active: active (running)」と表示されればcronは起動しています。
もしも「Active: inactive (dead)」と表示される場合は、停止してしまっているので、以下のコマンドで「起動」します。
sudo systemctl start cron
また、以下のコマンドで「再起動」「停止」も可能です。
sudo systemctl restart cron
sudo systemctl stop cron
最後に
今回はcronを利用したPythonプログラムの定期実行の方法を説明しました。プログラムの中にwith True:
で実装する方法も簡単で便利ですが、今回の方法ならばプログラムを改良しないでそのまま実行できます。
さらに、プログラムとは別のツールから実行されるので、プログラムと一緒に定期実行も停止してしまう心配がありません。定期実行を安定して継続するのに適した方法です。
Pythonプログラムの定期実行ができるようになると以下の記事にもあるように自動化できることの幅がグッとひろがります。ぜひチャレンジしてみてください!