【保存版】cronでPython3を定時実行する方法&注意すべき4つのポイント

こんにちは、ライフハックブロガーのたぬ(@tanuhack)です!

「作ったPythonのプログラムが、ある決まった時間に自動で実行されればなあ…」

Macのcron(クーロン)という機能を使えば可能です!!

[aside]補足

cronとは、UNIX系のOS(MacやLinuxなど)に入っているプログラムの1つです。

プログラムを起動する時間を予め設定しておくことで、cronが自動で定期的にプログラムを実行してくれるようになります。

例えば、毎分実行させたり、一日おきにに実行させたり、毎週月曜の8時に実行させたりと、好きな間隔で設定することが出来ます。

[/aside]

プログラムの定時実行は主に以下の3つの用途で使われることが多いです。

  1. スクレイピングやデータ収集、様々な自動操作をするため
  2. プログラムが正しく動作しているか確認するため
  3. ログをとったりバックアップをしたりするため

私は、①の理由でcronをよく使っています\(^o^)/

jupyter notebookやターミナルから手動でプログラムを実行させていた時期があったなんて、今では信じられないくらい定時実行は便利です。抜け出せる自信がありません!!

さあ、あなたもcronを習得して、人間が行うには下らない単純作業を自動化させ、人生のクオリティを上げていきましょう!!

それでは、どうぞ。

この記事のゴール
cronを使いこなし、Pythonプログラムを好きな間隔で定時実行させることが出来るようになる!
注意
注意

cronはMacにしかありません。そのため当記事は、Macユーザー向けの内容となっております。悪しからず。

「どうしても、Windowsでやりたいんだ」という方は、『Python タスクスケジューラ』と検索してみて下さい。

cronの使い方

ターミナルでcrontab -eと実行すると、cronの登録画面(vim)が出てきます。

ターミナル
crontab -e
cron1
cronの登録画面|vim

この黒い画面にcronのコマンドを登録していきます。

vimの操作する

cron1

この黒い画面はvim(ヴィム)と言って、普通のエディタのようなGUI(マウスや指などで操作できる画面のこと)操作ではなく、CUI(キーボードでしか操作できない画面)操作でしか動きません。

最初は戸惑うかもしれませんが、慣れればどうってことありませんよ\(^o^)/

vimには2つのモードがあります。

  1. インサート(挿入)モード:文字が入力できる
  2. コマンドモード:文字が入力できない

vimを起動したら、コマンドモードが起動します。キーボードのIを押すと、コマンドモードからインサートモードに切り替わり、文字を入力することが出来るようになります。

cron2
インサートモード|vim

今回cronを作成する例として、デスクトップ(/Users/Kenta(※あなたのユーザー名)/Desktop/)にあるhello-cron.pyというPythonファイルを毎朝9:00に定時実行させるcronを作ってみましょう。

# hello-cron.py(Pythonのバージョンを出力するプログラム)
import sys
print('Python:' + sys.version)

プログラムの実行タイミングを設定する

cronは、次のような書式で実行する『タイミング』と『コマンド』を設定します。

分 時 日 月 曜日 コマンド
*  *  *  *  *  some_command
0〜59
0〜23
1〜31
1〜12
曜日 0〜7(0,7=日曜、1=月曜、2=火曜、3=水曜、4=木曜、5=金曜、6=土曜)
cronの設定例
12 7 * * *         毎日7:12に実行
0 21 * * *         毎日21:00分に実行
0 18 * * 2         毎週火曜の18:00に実行
0,10 18 * * 1-5       毎平日の18:00と18:10に実行
0,10,20,30,40,50 * * * *   毎日10分毎に実行
*/10 * * * *          毎日10分毎に実行
0 9 1 * *           毎月1日の9:00に実行
* * * * *            毎分実行

ゆえに『デスクトップ(/Users/Kenta(※あなたのユーザー名)/Desktop/)にあるHello-cron.pyというPythonファイルを毎朝9時に定時実行させるcron』は以下のように設定できます。

cron3

毎朝9時にPythonファイルを定時実行させるcron
0 9 * * * python /Users/Kenta/Desktop/hello-cron.py

vimを閉じる

vimをインサートモードからコマンドモードに切り替えるためにはescキーを押します。

そして『:wq』と入力して、returnキーでcronを保存します。

cron4

うまく保存できれば、ターミナルに『installing new crontab』と表示されます。

cron5

出力を確認する

Pythonのprint()メソッドは、ターミナル上に標準出力されません

では、実際にどうやって出力を確認するのかというと、ターミナル上でmailコマンドを使って確認します。

とは言ったものの、mailコマンドで毎回確認するのは不便すぎるので、『標準出力』と『エラーメッセージ』を一緒に『logファイル』として出力する方法を紹介します。

先程のcronを登録する際に、コードを付け足します。

Before|crontab
0 9 * * * python /Users/Kenta/Desktop/hello-cron.py
After|crontab
0 9 * * * python /Users/Kenta/Desktop/hello-cron.py > /Users/Kenta/Desktop/exec-error.log 2>&1

cron7

cron自体は正しく動作しています。

しかし、Python3を実行したはずが、なぜかPython2.7系になっていますね。

実は、cronでPythonプログラムをユーザーが意図した通りに動かすためには、次に紹介する4つのポイントを押さえなくてはいけません。

注意すべき4つのポイント

  1. Python3のPATHを通す
  2. 相対パスではなく絶対パスを記述する
  3. 環境変数は注意が必要
  4. 文字コードを設定する

それぞれ見ていきましょう。

Python3のPATHを通す

cron7

先程、Python3を実行したつもりがPython2.7系で実行されていましたね。おそらく、私以外にも多くの方が2.7系で実行されると思います。

[aside]補足
Macには標準でPython2.7系がインストールされている(※2019年1月現在)[/aside]

そのため、ターミナル上ではPython3(python --versionコマンドでバージョンを確認)が動いていても、cron上ではPython2.7系が動いてしまいます

ターミナル
python --version
Python 3.6.6 :: Anaconda custom (64-bit)

結論から言うと、crontabでPython3が動くようにPATHを通してあげればOKです。

ターミナルでecho $PATHと入力して、そのPATHをcrontabに貼り付けます。

ターミナル
echo $PATH
/anaconda3/bin:/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Applications/VMware Fusion.app/Contents/Public:/opt/X11/bin:/Users/kenta/.nodebrew/current/bin:/Users/Kenta/.nodebrew/current/bin

cron9

cron8

PATHを通してあげると、ちゃんとcronで実行したプログラムでもPython3系を動かすことが出来ました。

相対パスではなく絶対パスを記述する

cronで実行するプログラムでは、相対パスは使えません。すべて絶対パスに書き換えましょう!

環境変数は注意が必要

先程のPATHみたいに、cronの環境変数はターミナルのそれと全然違うことが分かりますね。

一応確認してみると、、、

crontab
* * * * * env >/tmp/cron_env
ターミナル
$ cat /tmp/cron_env
SHELL=/bin/sh
USER=Kenta
PATH=/usr/bin:/bin
PWD=/Users/Kenta
SHLVL=1
HOME=/Users/Kenta
LOGNAME=Kenta
_=/usr/bin/env

うわ、すくなっ…。ご覧の通り、全然用意されていません。

プログラムの中で環境変数を使用している場合は、『プログラムをそのまま変えずにcrontab上で環境変数を定義する』か『(※流出したら危ないけど)プログラムで環境変数を使わず、変数にKEYやPASSを直接代入する』か、で代用してください。

文字コードを設定する

出力に日本語を含む場合、次のようなエラーが発生します。

'ascii' codec can't encode characters in position 0-5: ordinal not in range(128)

cronにLANG=ja_JP.UTF-8と記述しておけばPythonプログラムに日本語が含まれても大丈夫です。

cron10

print('プログラムに日本語が含まれても大丈夫!')

cron11

さいごに

今回は、Pythonを定時実行させるために欠かせないcronの使い方と注意すべき4つのポイントを紹介しました。

cronのおさらい
  • ターミナル:crontab -eでcrontabを起動
  • crontab:Iでインサートモードに変更
  • crontab:『分 時 日 月 曜日 コマンド > /some_directry/exec-error.log 2>&1』でプログラムの実行タイミングを設定し、標準出力とエラーもまとめてログファイルとして出力する
  • crontab:escでコマンドモードに変更、『:wq』で上書き保存する
  • 注意すべき4つのポイント
  • crontab:Python3のPATHを通すPATH=あなたのパス
  • 相対パスを絶対パス(フルパス)に書き換える
  • 環境変数を使う場合は、『プログラムをそのまま変えずにcrontab上で環境変数を定義する』か『プログラムで環境変数を使わず、変数にKEYやPASSを直接代入する』
  • crontab:プログラムに日本語が含まれる場合LANGを宣言するLANG=ja_JP.UTF-8
  • あなたはどんな目的でcronを使いますか?

    1. スクレイピングやデータ収集、様々な自動操作をするため
    2. プログラムが正しく動作しているか確認するため
    3. ログをとったりバックアップをしたりするため

    cronで良い自動化ライフを!