Ekstraksi Tabel dari PDF Laporan Keuangan dengan Python
Blog/Tips & Trik/Ekstraksi Tabel dari PDF Laporan Keuangan dengan Python

Ekstraksi Tabel dari PDF Laporan Keuangan dengan Python

BimaBima
·27 September 2025·11 menit baca

Penulis

Bima

Bima

Founder & Data Professional

Bagikan

TL;DR

Cara ekstrak tabel dari PDF pakai Python paling cepat adalah pakai pdfplumber: buka file, panggil extract_tables() per halaman, lalu ubah hasilnya jadi DataFrame pandas. Kalau tabelnya punya garis kotak yang jelas, Camelot dengan flavor lattice biasanya lebih akurat. PDF hasil scan harus lewat OCR dulu sebelum bisa diekstrak.

Cara ekstrak tabel dari PDF pakai Python paling cepat adalah pdfplumber: buka file, panggil extract_tables() per halaman, lalu ubah hasilnya jadi DataFrame pandas.

Masalahnya klasik. Bagian keuangan kirim laporan dalam bentuk PDF, kamu butuh angkanya buat rekap, dan satu-satunya opsi yang kepikiran adalah ketik ulang.

Aku pernah ngetik ulang neraca 3 halaman selama 40 menit. Skrip yang aku tulis buat ganti kerjaan itu jalannya 6 detik.

Di bawah ini alur lengkapnya, dari install sampai angka rupiah siap dijumlahin.

Apa cara paling gampang ekstrak tabel dari PDF pakai Python?

Pakai pdfplumber. Library ini baca layer teks di dalam PDF, deteksi posisi garis dan jarak antar kata, lalu susun ulang jadi baris dan kolom. Satu perintah pip install pdfplumber, gak butuh Java atau software tambahan. Hasilnya list of list Python yang tinggal kamu kasih ke pandas.

import pdfplumber
import pandas as pd

path = "laporan-keuangan-2024.pdf"
semua_tabel = []

with pdfplumber.open(path) as pdf:
    for nomor, halaman in enumerate(pdf.pages, start=1):
        for tabel in halaman.extract_tables():
            df = pd.DataFrame(tabel[1:], columns=tabel[0])
            df["halaman"] = nomor
            semua_tabel.append(df)

print(len(semua_tabel), "tabel ketemu")

Tiga baris inti doang. pdf.pages itu daftar halaman, extract_tables() balikin semua tabel yang kedeteksi di halaman itu.

Baris pertama tiap tabel biasanya header, makanya dipisah jadi columns.

Kenapa tabel PDF sering berantakan pas diekstrak?

Karena PDF gak nyimpen tabel sebagai tabel. PDF cuma nyimpen instruksi gambar: taruh huruf A di koordinat x=120 y=340, tarik garis dari sini ke sana. Konsep baris dan kolom itu hasil tebakan library dari posisi teks.

Jadi kalau kolom di laporanmu dipisah pakai spasi doang tanpa garis, tebakannya gampang meleset.

Ada tiga bentuk PDF yang perlu kamu bedain dulu sebelum nulis kode:

  • PDF teks dengan garis tabel. Paling gampang. Garis kotaknya jadi petunjuk batas kolom yang jelas.
  • PDF teks tanpa garis. Umum di laporan keuangan yang desainnya minimalis. Butuh setting tambahan.
  • PDF hasil scan. Isinya gambar. Harus lewat OCR dulu.

Cek tipenya cepat: coba blok teks di file PDF pakai mouse. Kalau bisa diblok, itu PDF teks. Kalau gak bisa, itu hasil scan.

pdfplumber, Camelot, atau tabula-py: pilih yang mana?

Ketiganya sama-sama bisa narik tabel dari PDF, tapi cara kerjanya beda. pdfplumber murni Python, Camelot butuh Ghostscript buat mode lattice, tabula-py jalan di atas Java. Buat kerjaan harian dengan laporan keuangan Indonesia, pdfplumber menang di kemudahan setup.

AspekpdfplumberCamelottabula-py
Dependensi luarGak adaGhostscript (mode lattice)Java runtime
Tabel bergarisBagusPaling bagusBagus
Tabel tanpa garisBagus (mode text)Cukup (mode stream)Cukup
Kontrol area halamanDetail, bisa crop bebasPakai koordinatPakai koordinat
Laporan akurasi bawaanGak adaAda, lewat parsing_reportGak ada
Cocok buatKerjaan harian, PDF campurTabel rapi bergaris, batch besarTim yang udah punya Java

Saran praktisku: mulai dari pdfplumber. Kalau hasilnya kacau dan tabelnya bergaris rapi, baru pindah ke Camelot.

Detail parameter tiap flavor Camelot ada di dokumentasi resminya.

Gimana langkah ekstrak tabel laporan keuangan step by step?

Lima langkah ini yang aku pakai tiap kali dapat PDF laporan baru. Urutannya penting: cek isi file dulu sebelum nulis logika ekstraksi, biar kamu gak buang waktu debug di halaman yang salah.

  1. Install library. Jalanin pip install pdfplumber pandas di virtual environment kamu.
  2. Cek halaman mana yang ada tabelnya. Jangan langsung proses semua halaman.
  3. Tarik satu tabel dulu. Lihat hasilnya, pastiin kolomnya bener.
  4. Bersihin angka. Ubah teks rupiah jadi float.
  5. Simpan ke CSV atau database. Baru lanjut ke analisis.

Langkah 2: cari halaman yang ada tabelnya

with pdfplumber.open(path) as pdf:
    for nomor, halaman in enumerate(pdf.pages, start=1):
        jumlah = len(halaman.find_tables())
        if jumlah > 0:
            teks_awal = (halaman.extract_text() or "")[:60]
            print(nomor, "|", jumlah, "tabel |", teks_awal.replace("\n", " "))

Output-nya kasih kamu peta isi file. Dari situ kamu tau halaman berapa yang isinya laba rugi, neraca, atau arus kas.

Langkah 3: tarik satu tabel dengan setting yang pas

Kalau tabelnya gak bergaris, deteksi default sering nyatuin dua kolom jadi satu. Ganti strateginya ke text.

setting = {
    "vertical_strategy": "text",
    "horizontal_strategy": "text",
    "intersection_tolerance": 5,
}

with pdfplumber.open(path) as pdf:
    halaman = pdf.pages[12]
    tabel = halaman.extract_table(setting)

for baris in tabel[:5]:
    print(baris)

Naikin intersection_tolerance kalau baris kepotong. Turunin kalau dua kolom nempel jadi satu.

Kalau header dan footer ikut kebaca

Potong area halamannya dulu pakai crop. Koordinatnya dalam satuan point, dihitung dari kiri atas.

area = halaman.crop((0, 90, halaman.width, halaman.height - 60))
tabel = area.extract_table(setting)

Angka 90 dan 60 itu tinggi kop surat dan footer nomor halaman. Sesuaikan sama layout laporanmu.

Gimana cara bersihin angka rupiah hasil ekstraksi?

Angka di laporan Indonesia ditulis 1.234.567,89. Python bacanya kebalik, titik dianggap desimal. Kalau kamu langsung float(), hasilnya error atau salah 1000 kali lipat. Fungsi di bawah ini nanganin pemisah ribuan, koma desimal, simbol Rp, dan kurung yang artinya nilai negatif.

import re

def bersihin_angka(nilai):
    if nilai is None:
        return None
    teks = str(nilai).strip()
    if teks in ("", "-", "–"):
        return None
    negatif = teks.startswith("(") and teks.endswith(")")
    teks = re.sub(r"[^\d,.]", "", teks)
    teks = teks.replace(".", "").replace(",", ".")
    if teks == "":
        return None
    angka = float(teks)
    return -angka if negatif else angka

print(bersihin_angka("Rp 1.234.567,89"))   # 1234567.89
print(bersihin_angka("(2.500.000)"))        # -2500000.0
print(bersihin_angka("-"))                  # None

Terapin ke kolom angka di DataFrame kamu:

for kolom in ["2024", "2023"]:
    df[kolom] = df[kolom].map(bersihin_angka)

df.to_csv("laba-rugi-bersih.csv", index=False)

Sebelum lanjut ke analisis, cek dulu ada berapa nilai yang jadi None. Angka None yang kebanyakan artinya parsing kamu masih meleset, bukan datanya yang kosong. Ini bagian dari kerjaan jaga data quality yang sering dilewatin.

Contoh kasus: laporan laba rugi Toko Berkah

Toko Berkah punya 4 cabang di Yogyakarta. Tiap kuartal, akuntan eksternal mereka kirim laporan laba rugi dalam PDF 22 halaman. Rekap manualnya makan waktu setengah hari kerja.

Struktur tabelnya begini setelah diekstrak dan dibersihin:

PosQ3 2025 (Rp)Q2 2025 (Rp)
Penjualan bersih1.842.500.0001.615.300.000
Harga pokok penjualan(1.271.325.000)(1.098.404.000)
Laba kotor571.175.000516.896.000
Beban operasional(402.400.000)(338.700.000)
Laba bersih168.775.000178.196.000

Angka yang menarik muncul setelah semuanya jadi float. Penjualan naik 14,1 persen dari kuartal sebelumnya, tapi laba bersih justru turun 5,3 persen. Penyebabnya beban operasional yang naik 18,8 persen, lebih cepat dari kenaikan omzetnya.

df["selisih"] = df["2025Q3"] - df["2025Q2"]
df["pertumbuhan_persen"] = (df["selisih"] / df["2025Q2"].abs() * 100).round(1)

print(df[["pos", "pertumbuhan_persen"]])

Insight kayak gini yang kelewat kalau kamu cuma baca PDF-nya sekali lewat. Begitu angkanya masuk pandas, bandingin antar periode cuma butuh satu baris kode.

Kalau skrip ini mau kamu jalanin tiap kuartal tanpa dipantengin, ubah dulu jadi file .py dan pasang penjadwal. Cara lengkapnya ada di artikel dari notebook ke skrip terjadwal.

Gimana kalau PDF-nya hasil scan?

PDF hasil scan isinya gambar, jadi extract_text() balikin string kosong. Solusinya OCR: proses yang ngenalin huruf dari gambar dan nambahin layer teks ke file PDF-nya.

Tool paling gampang buat ini ocrmypdf, yang di belakang layar pakai Tesseract.

pip install ocrmypdf
ocrmypdf -l ind+eng scan-laporan.pdf laporan-ocr.pdf

Flag -l ind+eng bilang ke Tesseract buat pakai model bahasa Indonesia dan Inggris sekaligus. Penting, soalnya laporan keuangan Indonesia biasanya campur dua bahasa itu.

Setelah jadi laporan-ocr.pdf, prosesnya balik lagi ke pdfplumber seperti biasa. Tapi turunin ekspektasi kamu. Digit 8 kebaca jadi 3, angka nol jadi huruf O, itu hal biasa di hasil scan. Selalu cek total baris hasil ekstraksi terhadap total yang tertulis di laporan.

Kesalahan umum waktu ekstrak tabel PDF

Empat hal ini yang paling sering bikin hasil ekstraksi salah tanpa kamu sadar.

1. Percaya hasil ekstraksi tanpa validasi

Selalu cocokin angka total hasil skrip sama angka total yang tertulis di PDF. Kalau laba kotor hasil hitungan kamu beda 1 rupiah pun, ada baris yang kelewat.

2. Proses semua halaman padahal cuma butuh 3

Laporan tahunan bisa 150 halaman. Batasi range halamannya, prosesnya jadi 20 kali lebih cepat dan noise-nya jauh berkurang.

3. Lupa baris yang kena merge cell

Sel yang digabung sering bikin satu baris punya nilai None di kolom pertama. Isi ulang pakai df["pos"] = df["pos"].ffill() supaya label kategorinya turun ke baris di bawahnya.

4. Nyimpen hasil ke Excel tanpa tipe data

Kalau angkanya masih berupa teks pas disimpan, semua rumus di spreadsheet kamu bakal balikin 0. Pastiin kolom angka udah bertipe float sebelum to_excel(). Cek cepat pakai df.dtypes.

Alur bersih-bersih kayak gini adalah bagian awal dari proses ETL yang bener. Ekstrak, transformasi, baru simpan.

Skrip lengkap yang bisa langsung kamu pakai

import re
import pdfplumber
import pandas as pd

SETTING = {
    "vertical_strategy": "text",
    "horizontal_strategy": "text",
    "intersection_tolerance": 5,
}

def bersihin_angka(nilai):
    if nilai is None:
        return None
    teks = str(nilai).strip()
    if teks in ("", "-"):
        return None
    negatif = teks.startswith("(") and teks.endswith(")")
    teks = re.sub(r"[^\d,.]", "", teks).replace(".", "").replace(",", ".")
    if teks == "":
        return None
    angka = float(teks)
    return -angka if negatif else angka

def ambil_tabel(path, halaman_awal, halaman_akhir):
    hasil = []
    with pdfplumber.open(path) as pdf:
        for i in range(halaman_awal - 1, halaman_akhir):
            for tabel in pdf.pages[i].extract_tables(SETTING):
                df = pd.DataFrame(tabel[1:], columns=tabel[0])
                df["halaman"] = i + 1
                hasil.append(df)
    return pd.concat(hasil, ignore_index=True) if hasil else pd.DataFrame()

df = ambil_tabel("laporan-keuangan-2024.pdf", 12, 15)
for kolom in df.columns:
    if kolom not in ("pos", "halaman"):
        df[kolom] = df[kolom].map(bersihin_angka)

df.to_csv("hasil-ekstraksi.csv", index=False)
print(df.head())

Ganti nama file, range halaman, dan nama kolom sesuai laporanmu. Sisanya jalan apa adanya.

Buat referensi fungsi pandas yang dipakai di sini, dokumentasi resmi pandas.pydata.org selalu jadi rujukan paling akurat.

FAQ

Library Python apa yang paling bagus buat ekstrak tabel dari PDF?

Buat mayoritas kasus, pdfplumber pilihan pertama yang paling aman. Instalnya cuma satu perintah pip, gak butuh Java atau Ghostscript, dan hasilnya langsung berupa list Python. Kalau tabel di PDF kamu punya garis kotak lengkap, coba Camelot flavor lattice karena deteksi kolomnya lebih rapi di kasus itu.

Kenapa hasil ekstraksi tabelnya kolomnya gak sesuai?

Biasanya karena PDF-nya gak punya garis tabel, jadi library nebak batas kolom dari jarak antar teks. Coba ganti table_settings ke strategy text dan atur intersection_tolerance. Kalau masih meleset, potong halaman pakai crop biar area yang dibaca cuma bagian tabelnya doang, bukan header dan footer laporan.

Bisa gak ekstrak tabel dari PDF hasil scan?

Gak bisa langsung. PDF hasil scan isinya gambar, jadi gak ada layer teks yang bisa dibaca pdfplumber. Kamu perlu OCR dulu pakai ocrmypdf atau Tesseract buat nambahin layer teks ke file-nya. Setelah itu baru proses ekstraksinya jalan seperti biasa, walau akurasinya tetap di bawah PDF asli.

Gimana cara ubah angka Rupiah dari PDF jadi angka yang bisa dihitung?

Angka di laporan Indonesia pakai titik sebagai pemisah ribuan dan koma buat desimal, kebalikan dari format Python. Hapus semua karakter selain digit, titik, dan koma. Lalu buang titiknya, ganti koma jadi titik, baru konversi ke float. Angka dalam kurung artinya negatif, jadi kasih tanda minus.

Berapa lama proses ekstraksi buat laporan tahunan yang tebal?

Waktu aku tes laporan tahunan 148 halaman di laptop biasa, pdfplumber butuh sekitar 40 detik buat scan semua halaman dan narik 63 tabel. Yang makan waktu justru tahap bersihin dan milih tabel mana yang beneran kamu butuhin. Batasi range halaman biar prosesnya jauh lebih singkat.

Langkah berikutnya

Tiga hal yang perlu kamu inget dari sini. Pertama, pdfplumber cukup buat 80 persen kasus laporan keuangan Indonesia. Kedua, angka rupiah wajib dibersihin sebelum dihitung, kalau gak hasilnya salah 1000 kali lipat. Ketiga, selalu cocokin total hasil skrip sama total di PDF aslinya.

Coba mulai dari satu file PDF yang paling sering kamu terima tiap bulan. Tarik satu tabel, bersihin satu kolom, simpan ke CSV. Itu udah cukup buat hemat waktu di siklus berikutnya.

Kalau kamu kerja di Jupyter, beberapa trik di artikel 28 trik Jupyter Notebook bikin proses coba-coba setting tabel jadi jauh lebih cepat.

Punya file PDF yang bandel dan gak mau kebaca? Ceritain strukturnya di kolom komentar, biar kita cari setting yang pas bareng-bareng.

Coba Langsung

Mau praktek langsung? Mulai latihan SQL gratis

Latihan interaktif, langsung di browser.

Buka NgulikSQL →
Bagikan:
Bima
Ditulis oleh

Bima

Founder & Data Professional

Founder Ngulik Data. Passionate about making data analysis accessible for everyone.

Artikel terkait

Pandas groupby: Agregasi Data ala Pivot di Python (2026)
Tips & Trik
20 Juli 2026•9 menit baca

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.

BimaBima
Pandas to_excel: Ekspor DataFrame ke Excel (2026)
Tips & Trik
18 Juli 2026•8 menit baca

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.

BimaBima
Pandas read_excel: Baca File Excel di Python
Tips & Trik
16 Juli 2026•8 menit baca

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.

BimaBima
Kembali ke Blog
Ngulik Data logoNgulik Data

Platform edukasi data lengkap untuk professionals Indonesia. Belajar SQL, Data Analysis, dan lebih banyak lagi dengan praktek langsung dan feedback real-time.

© 2026 Ngulik Data. Semua hak dilindungi.

TAUTAN
BantuanHargaDatasetBlogAfiliasi
LEGAL
Syarat & KetentuanKebijakan Privasi
Ngulik Data
DatasetLeaderboardBlogStore