Dari Notebook ke Skrip Terjadwal: Menaikkan Level Analisis Pythonmu
TL;DR
Cara ubah Jupyter Notebook jadi skrip terjadwal ada empat langkah: ekspor notebook jadi file .py, rapikan isinya jadi fungsi dengan satu entry point, pindahin konfigurasi dan rahasia ke file .env, lalu daftarkan ke cron di Linux atau Task Scheduler di Windows. Tambahin logging supaya kamu tau skripnya gagal tanpa harus buka terminal.
Cara ubah Jupyter Notebook jadi skrip terjadwal itu empat langkah: ekspor jadi file .py, rapikan isinya jadi fungsi, pindahin konfigurasi ke file terpisah, lalu daftarkan ke penjadwal sistem.
Notebook itu tempat mikir. Kamu jalanin sel, lihat hasilnya, ganti angka, jalanin lagi.
Masalahnya muncul waktu analisis itu jadi kerjaan rutin. Tiap Senin pagi kamu buka laptop, buka notebook, klik Run All, tungguin, lalu kirim hasilnya ke grup WhatsApp.
Aku ngelakuin itu selama 7 bulan sebelum sadar total waktunya 21 jam setahun cuma buat nungguin dan copy-paste. Skrip yang gantiin kerjaan itu jalannya 11 detik dan aku gak perlu buka laptop.
Kenapa notebook gak cocok buat kerjaan rutin?
Karena notebook nyimpen urutan eksekusi, bukan urutan kode. Sel nomor 12 bisa aja dijalanin sebelum sel nomor 3, dan hasilnya tetap kelihatan bener di layar. Begitu file itu dijalanin dari atas ke bawah oleh mesin, error muncul di tempat yang gak kamu duga.
Ada empat sifat notebook yang jadi masalah pas dijadwalin:
- State tersembunyi. Variabel dari sel yang udah kamu hapus masih nyangkut di memori.
- Gak ada exit code. Sistem penjadwal gak tau skripmu berhasil atau gagal.
- Path relatif ke folder notebook. Pindah folder, semuanya rusak.
- Diff di git berantakan. Output gambar ikut kesimpan dalam bentuk teks base64 yang panjangnya ribuan baris.
Notebook tetap berguna. Pakai buat eksplorasi awal dan buat presentasi hasil. Buat yang jalan tiap hari, pindahin ke file .py.
Gimana cara ubah notebook jadi skrip Python?
Mulai dari ekspor otomatis, lalu rapikan manual. Perintah nbconvert bawaan Jupyter ngubah notebook jadi file .py dalam sekali jalan, tapi hasilnya masih berupa kode berurutan tanpa struktur. Bagian rapikannya yang bikin skrip kamu tahan dijadwalin.
jupyter nbconvert --to script analisis-penjualan.ipynb
Hasilnya analisis-penjualan.py yang isinya semua sel digabung, lengkap dengan komentar nomor sel. Jangan langsung dijadwalin. Ada lima hal yang harus kamu benerin dulu.
- Hapus semua kode eksplorasi. Baris
df.head(),print(df.shape), dan plot yang cuma buat lihat-lihat. - Kumpulin semua import ke atas. Notebook sering punya import yang nyebar di tengah.
- Bungkus tiap tahap jadi fungsi. Satu fungsi satu tugas.
- Pindahin angka dan path ke satu tempat. Jangan ada nilai yang ketik langsung di tengah logika.
- Tambahin entry point. Blok
if __name__ == "__main__":di paling bawah.
Gimana struktur skrip yang enak dijadwalin?
Pola yang aku pakai selalu sama: konfigurasi di atas, fungsi-fungsi kecil di tengah, satu fungsi main() yang ngatur urutan, entry point di bawah. Struktur ini bikin kamu bisa tes satu bagian tanpa jalanin semuanya.
"""Rekap penjualan harian Toko Berkah."""
import logging
import os
from datetime import date, timedelta
from pathlib import Path
import pandas as pd
FOLDER_DATA = Path(os.environ.get("FOLDER_DATA", "./data"))
FOLDER_OUTPUT = Path(os.environ.get("FOLDER_OUTPUT", "./output"))
AMBANG_MINIMAL_BARIS = 50
def ambil_data(tanggal: date) -> pd.DataFrame:
berkas = FOLDER_DATA / f"penjualan-{tanggal:%Y-%m-%d}.csv"
if not berkas.exists():
raise FileNotFoundError(f"File {berkas} gak ketemu")
return pd.read_csv(berkas, parse_dates=["tanggal"])
def bersihin(df: pd.DataFrame) -> pd.DataFrame:
df = df.dropna(subset=["cabang", "nilai"])
df = df[df["nilai"] > 0]
return df
def ringkas(df: pd.DataFrame) -> pd.DataFrame:
hasil = (
df.groupby("cabang", as_index=False)
.agg(transaksi=("nilai", "size"), omzet=("nilai", "sum"))
.sort_values("omzet", ascending=False)
)
return hasil
def simpan(df: pd.DataFrame, tanggal: date) -> Path:
FOLDER_OUTPUT.mkdir(parents=True, exist_ok=True)
tujuan = FOLDER_OUTPUT / f"rekap-{tanggal:%Y-%m-%d}.csv"
df.to_csv(tujuan, index=False)
return tujuan
def main() -> None:
tanggal = date.today() - timedelta(days=1)
logging.info("Mulai rekap untuk %s", tanggal)
df = ambil_data(tanggal)
if len(df) < AMBANG_MINIMAL_BARIS:
raise ValueError(f"Cuma {len(df)} baris, di bawah ambang wajar")
hasil = simpan(ringkas(bersihin(df)), tanggal)
logging.info("Selesai. Output di %s", hasil)
if __name__ == "__main__":
main()
Perhatiin pengecekan AMBANG_MINIMAL_BARIS. Ini penjaga sederhana yang bikin skrip berhenti kalau data sumbernya cuma sebagian. Tanpa itu, skrip bakal tetap sukses dan kirim laporan omzet yang salah.
Cek kewarasan kayak gini bagian dari jaga data quality. Lebih baik gagal berisik daripada berhasil dengan angka ngawur.
Gimana cara pisahin rahasia dan konfigurasi dari kode?
Simpan di file .env dan baca lewat os.environ. Password database, token API, dan path folder beda antara laptop kamu dan server. Kalau nilainya ketik langsung di kode, skripmu cuma jalan di satu tempat dan rahasianya ikut ke-push ke git.
# file .env
DB_HOST=10.20.30.40
DB_USER=analis_readonly
DB_PASSWORD=rahasia-banget
FOLDER_DATA=/srv/data/penjualan
FOLDER_OUTPUT=/srv/output/rekap
from dotenv import load_dotenv
import os
load_dotenv()
DB_HOST = os.environ["DB_HOST"]
DB_PASSWORD = os.environ["DB_PASSWORD"]
Install dulu pakai pip install python-dotenv. Lalu tambahin .env ke file .gitignore kamu, ini langkah yang paling sering kelupaan.
Pakai os.environ["NAMA"] dengan kurung siku buat nilai yang wajib ada. Kalau variabelnya kosong, skrip langsung gagal di awal, bukan setengah jalan.
Gimana cara nambahin logging biar tau kalau gagal?
Ganti semua print() jadi logging. Bedanya, logging otomatis nyatet waktu kejadian, tingkat keparahan, dan bisa nulis ke file sekaligus ke layar. Waktu skrip jalan jam 6 pagi tanpa ada yang nonton, file log itu satu-satunya jejak.
import logging
from logging.handlers import RotatingFileHandler
def siapin_log(path_log: str = "rekap.log") -> None:
berkas = RotatingFileHandler(path_log, maxBytes=1_000_000, backupCount=5)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s | %(levelname)s | %(message)s",
handlers=[berkas, logging.StreamHandler()],
)
RotatingFileHandler bikin file log dipotong tiap 1 MB dan nyimpen 5 file terakhir. Tanpa itu, log kamu bisa jadi 4 GB dalam setahun.
Bungkus main() supaya error kecatat lengkap dan exit code-nya bener:
import sys
if __name__ == "__main__":
siapin_log()
try:
main()
except Exception:
logging.exception("Rekap gagal")
sys.exit(1)
sys.exit(1) itu yang bikin cron dan tool monitoring tau skripnya gagal. Tanpa baris ini, skrip yang error tetap dianggap sukses.
Penjelasan lengkap tiap level dan handler ada di panduan logging resmi Python.
Gimana cara menjadwalkan skripnya?
Pilihannya tergantung tempat skripmu jalan. Di Linux dan macOS pakai cron, di Windows pakai Task Scheduler, di cloud pakai GitHub Actions atau layanan sejenis. Tiga-tiganya butuh satu hal yang sama: path lengkap ke interpreter Python di dalam virtual environment kamu.
| Cara | Cocok buat | Kelebihan | Yang perlu diperhatiin |
|---|---|---|---|
| cron | Server Linux, Mac | Bawaan sistem, ringan | Environment minim, path harus lengkap |
| Task Scheduler | Laptop atau server Windows | Ada antarmuka grafis | Laptop harus nyala |
| GitHub Actions | Skrip yang cuma butuh internet | Gak perlu server sendiri | Jadwalnya pakai zona waktu UTC |
| Airflow | Banyak skrip yang saling nunggu | Retry dan pantauan lengkap | Berat buat 1-2 skrip doang |
Setting cron
Buka daftar jadwal pakai crontab -e, lalu tambahin satu baris:
0 6 * * * cd /srv/rekap && /srv/rekap/venv/bin/python /srv/rekap/rekap_harian.py >> /srv/rekap/cron.log 2>&1
Lima angka di depan artinya menit, jam, tanggal, bulan, hari. Contoh di atas jalan tiap hari jam 6 pagi.
Bagian cd /srv/rekap penting kalau skripmu baca file pakai path relatif. Bagian 2>&1 bikin pesan error ikut kesimpan ke log, bukan hilang.
Setting Task Scheduler di Windows
Lewat command prompt, satu baris ini bikin tugas harian jam 6 pagi:
schtasks /create /tn "Rekap Harian" /tr "C:\rekap\venv\Scripts\python.exe C:\rekap\rekap_harian.py" /sc daily /st 06:00
Setting GitHub Actions
Simpan sebagai .github/workflows/rekap.yml:
name: Rekap Harian
on:
schedule:
- cron: "0 23 * * *" # 23:00 UTC = 06:00 WIB
workflow_dispatch:
jobs:
rekap:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- run: pip install -r requirements.txt
- run: python rekap_harian.py
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
Jadwal di GitHub Actions selalu dihitung pakai UTC. WIB itu UTC plus 7, jadi jam 6 pagi WIB ditulis sebagai 23:00 hari sebelumnya. Ini jebakan yang bikin banyak orang bingung kenapa skripnya jalan sore.
Contoh kasus: rekap harian Toko Berkah
Toko Berkah punya 4 cabang. Tiap pagi, supervisor butuh rekap omzet kemarin sebelum briefing jam 7.
Sebelumnya, satu staf datang jam 6 pagi buat buka notebook, jalanin semua sel, screenshot tabelnya, lalu kirim ke grup. Rata-rata 38 menit termasuk nunggu laptop nyala.
Setelah dijadwalin, skripnya jalan jam 5.30 dan hasilnya masuk grup jam 5.31. Waktu eksekusinya 11 detik buat 4 cabang dengan rata-rata 640 transaksi per hari.
Efek samping yang gak diduga: karena skripnya berhenti waktu data kurang dari 50 baris, mereka nemu masalah sinkronisasi kasir cabang Godean yang selama 3 bulan diam-diam kehilangan transaksi sore. Rata-rata 22 transaksi per hari gak kecatat.
Masalah itu gak pernah kelihatan waktu rekapnya manual, soalnya orang cuma lihat angka totalnya dan ngerasa wajar.
Kesalahan umum waktu pindah ke skrip terjadwal
1. Pakai path relatif
Cron gak jalan dari folder skripmu. Tulis Path(__file__).parent buat dapetin lokasi file, atau pindah direktori dulu di perintah cron-nya.
2. Lupa aktifin virtual environment
Perintah python di cron nunjuk ke Python sistem yang gak punya pandas. Tulis path lengkap ke venv/bin/python.
3. Gak ada notifikasi sama sekali
Skrip yang gagal diam-diam lebih berbahaya dari skrip yang gak pernah dibuat. Minimal kirim satu pesan ke Slack atau email waktu terjadi error.
4. Nulis file output dengan nama yang sama terus
Kalau skrip jalan dua kali, versi lamanya hilang. Sisipin tanggal di nama file, kayak rekap-2025-09-28.csv.
5. Skrip jalan sebelum data sumbernya siap
Kalau proses ETL di hulu selesai jam 5.15, jangan jadwalin skripmu jam 5.00. Kasih jarak, atau bikin skripmu nunggu sampai file sumbernya muncul.
FAQ
Bisa gak jadwalin file .ipynb langsung tanpa diubah ke .py?
Bisa pakai papermill atau nbconvert dengan flag execute, dan itu masuk akal kalau outputnya memang notebook berisi grafik buat dibagikan. Tapi buat kerjaan rutin, file .py lebih gampang di-debug, lebih ringan dijalanin, dan riwayat perubahannya kebaca jelas di git. Notebook nyimpen output sekalian, jadi diff-nya berantakan.
Kenapa skripku jalan di terminal tapi gagal waktu dijadwalin cron?
Hampir selalu karena path. Cron jalan dengan environment yang minim, jadi perintah python biasa belum tentu nunjuk ke virtual environment kamu. Tulis path lengkap ke interpreter di dalam venv dan path lengkap ke file skripnya. Tambahin juga baris pindah direktori kerja kalau skripmu baca file dengan path relatif.
Gimana cara nyimpen password database dengan aman di skrip Python?
Jangan pernah tulis langsung di kode. Simpan di file .env yang gak ikut masuk git, lalu baca pakai os.environ. Di server produksi, isi variabelnya lewat environment variable sistem atau secret manager. Kalau skripnya jalan di GitHub Actions, taruh di menu Secrets repositori kamu.
Berapa sering sebaiknya skrip analisis dijadwalin?
Ikutin ritme keputusannya, bukan ritme datanya. Kalau tim baca laporan tiap pagi sebelum briefing, jadwalin jam 6 pagi sekali sehari. Jadwal tiap jam cuma masuk akal kalau ada orang yang beneran ambil tindakan tiap jam. Frekuensi berlebihan cuma nambah beban server dan bikin notifikasi gagal jadi diabaikan.
Gimana cara tau kalau skrip terjadwalku gagal jalan?
Pasang logging ke file dan kirim notifikasi saat terjadi error. Bungkus proses utama pakai try except, catat pesan lengkapnya pakai logging.exception, lalu kirim ke email atau webhook Slack. Kasih juga notifikasi sukses ringkas biar kamu bisa bedain antara skrip berhasil dan skrip yang gak pernah jalan sama sekali.
Langkah berikutnya
Empat hal yang bikin skrip kamu tahan jalan tanpa dipantengin: struktur fungsi dengan satu entry point, konfigurasi di file .env, logging ke file, dan pengecekan kewarasan data sebelum hasilnya dipakai.
Mulai dari satu notebook yang paling sering kamu jalanin ulang. Ekspor, rapikan, jadwalin. Butuh sekitar dua jam buat yang pertama, dan 20 menit buat yang kedua.
Kalau sumber datamu masih berupa file PDF, gabungin dulu sama teknik di artikel ekstrak tabel PDF dengan Python. Dan buat bikin tahap eksplorasi di notebook lebih cepat, ada 28 trik Jupyter Notebook yang layak dicoba.
Skrip pertama kamu jadwalin buat kerjaan yang mana? Ceritain di komentar.
Mau praktek langsung? Mulai latihan SQL gratis
Latihan interaktif, langsung di browser.
Artikel terkait
Pandas groupby: Agregasi Data ala Pivot di Python (2026)
groupby ngelompokin baris berdasarkan kolom, lalu ngitung ringkasan per grup kayak total atau rata-rata. Ini pivot-nya pandas.
Pandas to_excel: Ekspor DataFrame ke Excel (2026)
to_excel nyimpen DataFrame pandas jadi file Excel .xlsx. Ini cara pakainya, dari satu sheet sampai banyak sheet, plus setelan yang bikin hasilnya rapi.
Pandas read_excel: Baca File Excel di Python
pandas read_excel baca file Excel jadi DataFrame, bisa pilih sheet tertentu dan lompatin baris judul yang berantakan. Ini cara pakai plus parameter penting dengan contoh data toko.