PythonスクリプトをCronでスケジュールする方法:初心者のための完全ガイド
原題: How to Schedule Python Scripts with Cron: A Beginner's Complete Guide
分析結果
- カテゴリ
- AI
- 重要度
- 59
- トレンドスコア
- 21
- 要約
- このガイドでは、PythonスクリプトをCronを使ってスケジュールする方法を初心者向けに詳しく説明します。CronはLinuxやUnix系のシステムでタスクを自動的に実行するためのツールです。記事では、Cronの基本的な使い方、スケジュールの設定方法、Pythonスクリプトの実行方法について解説し、実際の例を交えて説明します。これにより、定期的なタスクの自動化が可能になります。
- キーワード
How to Schedule Python Scripts with Cron: A Beginner's Complete Guide The best automation script is one that runs itself. Cron is the Unix scheduler built into every macOS and Linux machine. No SaaS, no cloud subscriptions, no third-party scheduler — just a text file that tells your system when to run your code. Here's everything you need to use it correctly. 🎁 Free: AI Publishing Checklist — 7 steps in Python · Full pipeline: germy5.gumroad.com/l/xhxkzz (pay what you want, min $9.99) The crontab syntax ┌─ minute ( 0 - 59 ) │ ┌─ hour ( 0 - 23 ) │ │ ┌─ day ( 1 - 31 ) │ │ │ ┌─ month ( 1 - 12 ) │ │ │ │ ┌─ weekday ( 0 = Sun , 1 = Mon ... 6 = Sat ) │ │ │ │ │ * * * * * command Common patterns: # Every day at 10am 0 10 * * * python3 /path/to/script.py # Every hour 0 * * * * python3 /path/to/script.py # Every 15 minutes * /15 * * * * python3 /path/to/script.py # Every Monday at 9am 0 9 * * 1 python3 /path/to/script.py # First of every month at midnight 0 0 1 * * python3 /path/to/script.py Use crontab.guru to verify any expression before running it. Step 1: Open your crontab crontab -e # opens in your default editor crontab -l # list current jobs crontab -r # remove all jobs (careful!) If this is your first time, you'll be asked to choose an editor. Choose nano for simplicity. Step 2: Write your script correctly Before scheduling, your script needs two things: 1. Absolute paths — not relative: # Wrong — cron runs from a different directory with open ( " state.json " ) as f : # FileNotFoundError at runtime data = json . load ( f ) # Right — always resolve relative to the script itself import os BASE_DIR = os . path . dirname ( os . path . abspath ( __file__ )) STATE_FILE = os . path . join ( BASE_DIR , " state.json " ) with open ( STATE_FILE ) as f : data = json . load ( f ) 2. A shebang line so cron knows which Python to use: #!/usr/bin/env python3 """ publish_queue.py — publishes one article per day. Cron: 0 10 * * * /usr/bin/python3 /Users/me/scripts/publish_queue.py """ import os , json , requests # ... Step 3: Log everything Cron jobs run silently. Without logging, you won't know if your script ran, crashed, or succeeded. #!/usr/bin/env python3 import logging import os from datetime import datetime # Log next to the script file BASE_DIR = os . path . dirname ( os . path . abspath ( __file__ )) LOG_FILE = os . path . join ( BASE_DIR , " publish.log " ) logging . basicConfig ( filename = LOG_FILE , level = logging . INFO , format = " %(asctime)s %(levelname)s %(message)s " , datefmt = " %Y-%m-%d %H:%M:%S " , ) log = logging . getLogger ( __name__ ) def main (): log . info ( " Script started " ) try : # ... your actual work ... log . info ( " Published: My Article Title " ) except Exception as e : log . error ( f " Failed: { e } " , exc_info = True ) raise if __name__ == " __main__ " : main () Your crontab entry should also capture stdout/stderr: 0 10 * * * /usr/bin/python3 /Users/me/scripts/publish_queue.py >> /Users/me/scripts/publish.log 2>&1 The >> file 2>&1 part appends both stdout and stderr to your log file. Step 4: Environment variables Cron runs with a minimal environment — none of your shell aliases, PATH entries, or export ed variables are available. The problem: import os token = os . environ [ " DEVTO_TOKEN " ] # KeyError — cron doesn't have this Solution 1: Use a .env file # Load environment from a file at the top of your script def load_env ( path : str ) -> None : """ Load KEY=value pairs from a file into os.environ. """ with open ( path ) as f : for line in f : line = line . strip () if line and not line . startswith ( " # " ) and " = " in line : key , _ , value = line . partition ( " = " ) os . environ [ key . strip ()] = value . strip (). strip ( '"' ). strip ( "'" ) BASE_DIR = os . path . dirname ( os . path . abspath ( __file__ )) load_env ( os . path . join ( BASE_DIR , " .env " )) token = os . environ [ " DEVTO_TOKEN " ] # works Your .env file: DEVTO_TOKEN = abc123yourtoken GUMROAD_TOKEN = xyz789yourtoken Solution 2: Set variables directly in crontab # At the top of your crontab file DEVTO_TOKEN = abc123yourtoken GUMROAD_TOKEN = xyz789yourtoken 0 10 * * * python3 /path/to/publish_queue.py >> /path/to/publish.log 2>&1 Step 5: Fix the PATH problem Cron's PATH is usually just /usr/bin:/bin . Commands like python3 may not be found if you installed Python via Homebrew or pyenv. The fix: use the full path to python3. # Find your python3 path in terminal: which python3 # /usr/local/bin/python3 (Homebrew) # /opt/homebrew/bin/python3 (Apple Silicon Homebrew) # /usr/bin/python3 (system Python) # Then use that full path in crontab: 0 10 * * * /opt/homebrew/bin/python3 /Users/me/scripts/publish.py >> /Users/me/scripts/pub.log 2>&1 Or set PATH explicitly at the top of your crontab: PATH = /opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin 0 10 * * * python3 /Users/me/scripts/publish.py >> /Users/me/scripts/pub.log 2>&1 Step 6: Virtual environments If your script uses third-party packages (requests, pillow, etc.), you need to point cron at your venv's Python: # Create and activate venv (one time) python3 -m venv /Users/me/scripts/.venv source /Users/me/scripts/.venv/bin/activate pip install requests pillow # In crontab — use the venv python directly 0 10 * * * /Users/me/scripts/.venv/bin/python /Users/me/scripts/publish.py >> /Users/me/scripts/pub.log 2>&1 No need to activate the venv in crontab — using the venv's python directly is equivalent. Complete working example The two cron jobs that power the publishing pipeline: #!/usr/bin/env python3 """ auto_publish_queue.py Reads publish_queue.json and publishes the next pending article to Dev.to. Cron: 0 10 * * * /path/to/.venv/bin/python /path/to/auto_publish_queue.py """ import os , json , re , logging , requests from datetime import date BASE_DIR = os . path . dirname ( os . path . abspath ( __file__ )) QUEUE_FILE = os . path . join ( BASE_DIR , " publish_queue.json " ) LOG_FILE = os . path . join ( BASE_DIR , " queue.log " ) logging . basicConfig ( filename = LOG_FILE , level = logging . INFO , format = " %(asctime)s %(message)s " , ) log = logging . getLogger ( __name__ ) TOKEN = os . environ . get ( " DEVTO_TOKEN " , "" ) def load_queue (): with open ( QUEUE_FILE ) as f : return json . load ( f ) def save_queue ( q ): with open ( QUEUE_FILE , " w " ) as f : json . dump ( q , f , indent = 2 ) def publish ( filepath ): with open ( os . path . join ( BASE_DIR , filepath )) as f : content = f . read () match = re . match ( r ' ^---\n(.*?)\n---\n ' , content , re . DOTALL ) fm = {} for line in match . group ( 1 ). splitlines (): if line . startswith ( ' tags: ' ): fm [ ' tags ' ] = [ t . strip () for t in line [ 6 :]. split ( ' , ' )] elif ' : ' in line : k , _ , v = line . partition ( ' : ' ) fm [ k . strip ()] = v . strip (). strip ( '"' ) headers = { " api-key " : TOKEN , " Content-Type " : " application/json " } resp = requests . post ( " https://dev.to/api/articles " , headers = headers , json = { " article " : { " title " : fm [ " title " ], " body_markdown " : content , " published " : True , " tags " : fm . get ( " tags " , []), " description " : fm . get ( " description " , "" ), } }) resp . raise_for_status () return resp . json () def main (): q = load_queue () if not q [ " pending " ]: log . info ( " Queue empty — nothing to publish " ) return item = q [ " pending " ][ 0 ] log . info ( f " Publishing: { item [ ' title ' ] } " ) result = publish ( item [ " filename " ]) url = f " https://dev.to { result [ ' path ' ] } " log . info ( f " Published: { url } " ) q [ " pending " ]. pop ( 0 ) q [ " published " ]. append ({ " filename " : item [ " filename " ], " title " : item [ " title " ], " date " : str ( date . today ()), " url " : url , " id " : result [ " id " ], }) save_queue ( q ) if __name__ == " __main__ " : main () Crontab setup: # Edit with: crontab -e DEVTO_TOKEN = your_token_here # 9am: ping RSS aggregators 0 9 * * * /path/to/.venv/bin/python /path/to/daily_ping.py >> /path/to/ping.log 2>&1 # 10am: publish next article from queue 0 10 * * * /path/to/.venv/bin/python /path/to/auto_publish_queue.py >> /path/to/queue.log 2>&1 Debugging checklist When a cron job doesn't run or fails silently: # 1. Check if cron is running (macOS) sudo launchctl list | grep cron # 2. Check system cron log (macOS) log show --predicate 'process == "cron"' --last 1h # 3. Check your log file tail -f /path/to/your/script.log # 4. Test the exact command cron will run /path/to/python3 /path/to/script.py >> /tmp/test.log 2>&1 cat /tmp/test.log # 5. Check permissions ls -la /path/to/script.py # must be readable chmod +x /path/to/script.py Common errors: Error Cause Fix Script never runs Cron daemon not running sudo launchctl load /System/Library/LaunchDaemons/com.vix.cron.plist python3: not found PATH too minimal Use full path: /usr/bin/python3 or /opt/homebrew/bin/python3 ModuleNotFoundError Wrong Python (no venv) Use venv's Python: /path/.venv/bin/python FileNotFoundError Relative path in script Use os.path.abspath(__file__) to build absolute paths KeyError on env var Shell env not loaded Set vars at top of crontab or load from .env file My two cron jobs publish one article per day and ping RSS aggregators — same pattern, different scripts: germy5.gumroad.com/l/xhxkzz — pay what you want, min $9.99. Further Reading Your First Automated Python Script That Validates and Runs Itself I'm Using Python to Automate My Own Marketing — Here's Every Script Python JSON: Read, Write, Validate, and Pretty-Print How to Schedule Python Scripts with Cron: A Beginner's Complete Guide The best automation script is one that runs itself. Cron is the Unix scheduler built into every macOS and Linux machine. No SaaS, no cloud subscriptions, no third-party scheduler — just a text file that tells your system when to run your code. Here's everything you need to use it correctly. 🎁 Free: AI Publishing Checklist — 7 steps in Python · Full pipeline: germy5.gumroad.com/l/xhxkzz (pay what you want, min