Tutorial Marker: Mengubah PDF dan Dokumen Menjadi Markdown untuk Pipeline RAG
Dalam dunia AI dan data engineering, kemampuan mengekstrak teks dari dokumen PDF, EPUB, dan gambar menjadi format terstruktur adalah kebutuhan fundamental. Marker adalah library open-source buatan VikParuchuri yang mampu mengkonversi dokumen menjadi Markdown berkualitas tinggi dengan akurasi luar biasa, termasuk mengenali tabel, rumus matematika, dan layout dokumen yang kompleks.
Tutorial ini akan membahas cara menggunakan Marker secara lengkap, mulai dari instalasi hingga integrasi ke pipeline RAG (Retrieval-Augmented Generation) produksi.
Mengapa Marker?
Sebelum Marker, developer biasanya mengandalkan PyPDF2, pdfminer, atau Tesseract OCR untuk ekstraksi teks dari PDF. Namun tools tersebut sering menghasilkan output yang berantakan, kehilangan format tabel, dan gagal mengenali layout multi-kolom.
Marker menyelesaikan masalah ini dengan pendekatan berbeda. Library ini menggunakan kombinasi model deep learning untuk:
- Mendeteksi layout halaman (header, footer, kolom, tabel)
- Mengenali teks dengan OCR berbasis Surya
- Mengkonversi tabel menjadi format Markdown yang rapi
- Memproses rumus matematika menjadi LaTeX
- Menangani dokumen multi-bahasa
Marker mendukung format input PDF, EPUB, MOBI, dan berbagai format gambar. Output yang dihasilkan berupa Markdown bersih yang siap digunakan untuk indexing, RAG pipeline, atau analisis lebih lanjut.
Instalasi
Prasyarat Sistem
Marker membutuhkan Python 3.9 atau lebih baru. Untuk performa optimal, GPU dengan CUDA sangat direkomendasikan meskipun CPU juga didukung.
python --version
Pastikan versi Python Anda minimal 3.9.
Instalasi via pip
Cara paling mudah menginstall Marker adalah menggunakan pip:
pip install marker-pdf
Ini akan menginstall Marker beserta semua dependensi yang diperlukan, termasuk model Surya untuk OCR dan deteksi layout.
Instalasi dari Source
Jika Anda ingin versi terbaru atau berkontribusi ke development:
git clone https://github.com/VikParuchuri/marker.git
cd marker
pip install -e .
Instalasi dengan GPU Support
Untuk mengaktifkan akselerasi GPU dengan CUDA:
pip install marker-pdf[gpu]
Pastikan CUDA toolkit sudah terinstall di sistem Anda. Verifikasi dengan:
python -c "import torch; print(torch.cuda.isavailable())"
Download Model
Saat pertama kali dijalankan, Marker akan otomatis mendownload model yang diperlukan dari Hugging Face. Model-model ini termasuk:
- Model deteksi layout
- Model OCR (Surya)
- Model pengenalan tabel
- Model konversi rumus matematika
Total ukuran model sekitar 2-3 GB. Pastikan koneksi internet stabil saat pertama kali menjalankan.
Basic Usage
Konversi File Tunggal via CLI
Marker menyediakan command-line interface yang mudah digunakan:
markersingle /path/to/document.pdf /path/to/output/folder
Perintah ini akan menghasilkan file Markdown di folder output beserta gambar yang diekstrak dari dokumen.
Konversi Batch
Untuk mengkonversi banyak dokumen sekaligus:
marker /path/to/input/folder /path/to/output/folder
Marker akan memproses semua file PDF, EPUB, dan MOBI di folder input secara paralel.
Penggunaan via Python API
Untuk integrasi ke aplikasi Python, gunakan API langsung:
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
Inisialisasi converter dengan model
converter = PdfConverter(
artifactdict=createmodeldict(),
)
Konversi dokumen
rendered = converter("document.pdf")
Dapatkan teks Markdown dan metadata
text, metadata, images = textfromrendered(rendered)
print(text) # Output Markdown
print(metadata) # Metadata dokumen
Contoh Output
Misalnya Anda memiliki PDF laporan keuangan dengan tabel. Marker akan menghasilkan output seperti:
# Laporan Keuangan Q1 2026
Ringkasan Pendapatan
| Kategori | Q1 2025 | Q1 2026 | Pertumbuhan |
|----------|---------|---------|-------------|
| Produk A | Rp 500M | Rp 750M | 50% |
| Produk B | Rp 300M | Rp 420M | 40% |
| Total | Rp 800M | Rp 1.17T | 46% |
Pendapatan perusahaan meningkat sebesar 46% year-over-year...
Perhatikan bagaimana tabel dikonversi dengan rapi ke format Markdown, lengkap dengan alignment.
Konfigurasi Lanjutan
Mengatur Parameter Konversi
Marker menyediakan berbagai parameter untuk mengontrol proses konversi:
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
converter = PdfConverter(
artifactdict=createmodeldict(),
)
Konfigurasi melalui config
import marker.settings as settings
Aktifkan debug mode untuk melihat proses detail
settings.DEBUG = True
Atur jumlah worker untuk batch processing
settings.WORKERNUM = 4
Atur device (cuda atau cpu)
settings.TORCHDEVICE = "cuda"
Memproses Halaman Tertentu
Jika Anda hanya membutuhkan halaman tertentu dari dokumen:
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
converter = PdfConverter(
artifactdict=createmodeldict(),
)
Konversi hanya halaman 1-5
rendered = converter("document.pdf", pagerange=[0, 5])
text, metadata, images = textfromrendered(rendered)
Menangani OCR untuk Dokumen Scan
Untuk dokumen hasil scan yang tidak memiliki text layer:
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
import marker.settings as settings
Force OCR pada semua halaman
settings.OCRALLPAGES = True
converter = PdfConverter(
artifactdict=createmodeldict(),
)
rendered = converter("scanneddocument.pdf")
text, metadata, images = textfromrendered(rendered)
Konfigurasi Bahasa OCR
Marker mendukung OCR multi-bahasa melalui Surya:
import marker.settings as settings
Daftar bahasa yang didukung untuk OCR
settings.OCRLANGUAGES = ["id", "en"] # Indonesia dan English
Advanced Usage
Integrasi dengan Pipeline RAG
Salah satu use case utama Marker adalah menyiapkan dokumen untuk pipeline RAG. Berikut contoh integrasi dengan LangChain:
import os
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
from langchain.textsplitter import RecursiveCharacterTextSplitter
from langchaincommunity.vectorstores import Chroma
from langchaincommunity.embeddings import HuggingFaceEmbeddings
def processdocuments(pdffolder: str) -> list:
"""Konversi semua PDF ke Markdown menggunakan Marker."""
converter = PdfConverter(
artifactdict=createmodeldict(),
)
documents = []
for filename in os.listdir(pdffolder):
if filename.endswith(".pdf"):
filepath = os.path.join(pdffolder, filename)
rendered = converter(filepath)
text, metadata, images = textfromrendered(rendered)
documents.append({
"content": text,
"metadata": {
"source": filename,
*metadata
}
})
return documents
def buildragindex(documents: list):
"""Bangun index RAG dari dokumen yang sudah dikonversi."""
textsplitter = RecursiveCharacterTextSplitter(
chunksize=1000,
chunkoverlap=200,
separators=["\n## ", "\n### ", "\n\n", "\n", " "]
)
chunks = []
for doc in documents:
splits = textsplitter.splittext(doc["content"])
for split in splits:
chunks.append({
"text": split,
"metadata": doc["metadata"]
})
embeddings = HuggingFaceEmbeddings(
modelname="sentence-transformers/all-MiniLM-L6-v2"
)
texts = [c["text"] for c in chunks]
metadatas = [c["metadata"] for c in chunks]
vectorstore = Chroma.fromtexts(
texts=texts,
metadatas=metadatas,
embedding=embeddings,
persistdirectory="./chromadb"
)
return vectorstore
Penggunaan
pdffolder = "./documents"
docs = processdocuments(pdffolder)
vectorstore = buildragindex(docs)
Query
results = vectorstore.similaritysearch("pendapatan Q1 2026", k=3)
for r in results:
print(r.pagecontent)
print(r.metadata)
print("---")
Batch Processing dengan Monitoring
Untuk memproses dokumen dalam jumlah besar dengan monitoring progress:
import os
import json
import time
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
def batchconvertwithmonitoring(
inputfolder: str,
outputfolder: str,
logfile: str = "conversionlog.json"
):
"""Batch convert dengan logging dan error handling."""
os.makedirs(outputfolder, existok=True)
converter = PdfConverter(
artifactdict=createmodeldict(),
)
pdffiles = [f for f in os.listdir(inputfolder) if f.endswith(".pdf")]
total = len(pdffiles)
results = []
print(f"Memproses {total} dokumen...")
for i, filename in enumerate(pdffiles, 1):
filepath = os.path.join(inputfolder, filename)
starttime = time.time()
try:
rendered = converter(filepath)
text, metadata, images = textfromrendered(rendered)
# Simpan output Markdown
outputname = filename.replace(".pdf", ".md")
outputpath = os.path.join(outputfolder, outputname)
with open(outputpath, "w", encoding="utf-8") as f:
f.write(text)
# Simpan gambar jika ada
if images:
imgfolder = os.path.join(
outputfolder,
filename.replace(".pdf", "images")
)
os.makedirs(imgfolder, existok=True)
for imgname, imgdata in images.items():
imgpath = os.path.join(imgfolder, imgname)
with open(imgpath, "wb") as f:
f.write(imgdata)
elapsed = time.time() - starttime
result = {
"file": filename,
"status": "success",
"output": outputpath,
"pages": metadata.get("pages", 0),
"timeseconds": round(elapsed, 2),
"imagesextracted": len(images) if images else 0
}
print(f"[{i}/{total}] OK: {filename} ({elapsed:.1f}s)")
except Exception as e:
elapsed = time.time() - starttime
result = {
"file": filename,
"status": "error",
"error": str(e),
"timeseconds": round(elapsed, 2)
}
print(f"[{i}/{total}] ERROR: {filename} - {e}")
results.append(result)
# Simpan log
with open(logfile, "w") as f:
json.dump(results, f, indent=2)
success = sum(1 for r in results if r["status"] == "success")
print(f"\nSelesai: {success}/{total} berhasil dikonversi")
print(f"Log disimpan ke: {logfile}")
return results
Jalankan
results = batchconvertwithmonitoring(
inputfolder="./pdfdocuments",
outputfolder="./markdownoutput"
)
Membuat REST API dengan FastAPI
Untuk menyediakan Marker sebagai service:
import os
import tempfile
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import JSONResponse
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
app = FastAPI(title="Marker PDF Converter API")
converter = None
@app.onevent("startup")
async def loadmodels():
global converter
converter = PdfConverter(
artifactdict=createmodeldict(),
)
@app.post("/convert")
async def convertpdf(file: UploadFile = File(...)):
"""Konversi PDF ke Markdown."""
if not file.filename.endswith((".pdf", ".epub", ".mobi")):
raise HTTPException(
statuscode=400,
detail="Format file tidak didukung. Gunakan PDF, EPUB, atau MOBI."
)
with tempfile.NamedTemporaryFile(
suffix=os.path.splitext(file.filename)[1],
delete=False
) as tmp:
content = await file.read()
tmp.write(content)
tmppath = tmp.name
try:
rendered = converter(tmppath)
text, metadata, images = textfromrendered(rendered)
return JSONResponse({
"filename": file.filename,
"markdown": text,
"metadata": metadata,
"imagescount": len(images) if images else 0
})
except Exception as e:
raise HTTPException(statuscode=500, detail=str(e))
finally:
os.unlink(tmppath)
@app.get("/health")
async def health():
return {"status": "ok", "modelloaded": converter is not None}
Jalankan server:
pip install fastapi uvicorn python-multipart
uvicorn markerapi:app --host 0.0.0.0 --port 8000
Test dengan curl:
curl -X POST http://localhost:8000/convert \
-F "file=@document.pdf" \
| python -m json.tool
Perbandingan Kualitas dengan Tools Lain
Berikut perbandingan Marker dengan tools ekstraksi teks lainnya pada dokumen dengan tabel kompleks:
import time
def compareextraction(pdfpath: str):
"""Bandingkan hasil ekstraksi berbagai tools."""
# Marker
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
start = time.time()
converter = PdfConverter(artifactdict=createmodeldict())
rendered = converter(pdfpath)
markertext, , = textfromrendered(rendered)
markertime = time.time() - start
# PyPDF2
from PyPDF2 import PdfReader
start = time.time()
reader = PdfReader(pdfpath)
pypdftext = ""
for page in reader.pages:
pypdftext += page.extracttext() or ""
pypdftime = time.time() - start
# pdfminer
from pdfminer.highlevel import extracttext
start = time.time()
pdfminertext = extracttext(pdfpath)
pdfminertime = time.time() - start
print("=== Perbandingan Hasil ===")
print(f"Marker : {len(markertext)} chars, {markertime:.2f}s")
print(f"PyPDF2 : {len(pypdftext)} chars, {pypdftime:.2f}s")
print(f"pdfminer : {len(pdfminertext)} chars, {pdfminertime:.2f}s")
print()
print("--- Marker Output (first 500 chars) ---")
print(markertext[:500])
print()
print("--- PyPDF2 Output (first 500 chars) ---")
print(pypdftext[:500])
compareextraction("complexreport.pdf")
Best Practices
1. Optimasi Memori untuk Dokumen Besar
Saat memproses dokumen besar (ratusan halaman), perhatikan penggunaan memori:
import gc
import torch
def processlargedocument(pdfpath: str, batchsize: int = 20):
"""Proses dokumen besar per batch halaman."""
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
import fitz # PyMuPDF
doc = fitz.open(pdfpath)
totalpages = len(doc)
doc.close()
alltext = []
converter = PdfConverter(artifactdict=createmodeldict())
for start in range(0, totalpages, batchsize):
end = min(start + batchsize, totalpages)
rendered = converter(pdfpath, pagerange=[start, end])
text, , = textfromrendered(rendered)
alltext.append(text)
gc.collect()
if torch.cuda.isavailable():
torch.cuda.emptycache()
return "\n\n".join(alltext)
2. Caching Hasil Konversi
Hindari konversi ulang dokumen yang sama:
import hashlib
import json
import os
CACHEDIR = "./markercache"
def getfilehash(filepath: str) -> str:
"""Hitung hash SHA256 dari file."""
sha256 = hashlib.sha256()
with open(filepath, "rb") as f:
for chunk in iter(lambda: f.read(8192), b""):
sha256.update(chunk)
return sha256.hexdigest()
def convertwithcache(pdfpath: str) -> str:
"""Konversi dengan caching berbasis hash file."""
os.makedirs(CACHEDIR, existok=True)
filehash = getfilehash(pdfpath)
cachepath = os.path.join(CACHEDIR, f"{filehash}.json")
if os.path.exists(cachepath):
with open(cachepath, "r") as f:
cached = json.load(f)
print(f"Cache hit: {pdfpath}")
return cached["text"]
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
converter = PdfConverter(artifactdict=createmodeldict())
rendered = converter(pdfpath)
text, metadata, = textfromrendered(rendered)
with open(cachepath, "w") as f:
json.dump({"text": text, "metadata": metadata}, f)
print(f"Converted and cached: {pdfpath}")
return text
3. Validasi Output
Selalu validasi kualitas output sebelum menggunakannya:
def validateconversion(text: str, minlength: int = 100) -> dict:
"""Validasi kualitas hasil konversi."""
issues = []
if len(text) < minlength:
issues.append(f"Teks terlalu pendek ({len(text)} chars)")
lines = text.split("\n")
emptyratio = sum(1 for l in lines if not l.strip()) / max(len(lines), 1)
if emptyratio > 0.7:
issues.append(f"Terlalu banyak baris kosong ({emptyratio:.0%})")
garbled = sum(1 for c in text if ord(c) > 65535) / max(len(text), 1)
if garbled > 0.05:
issues.append(f"Kemungkinan karakter rusak ({garbled:.1%})")
tablecount = text.count("|---|")
headingcount = text.count("\n#")
return {
"valid": len(issues) == 0,
"issues": issues,
"stats": {
"length": len(text),
"lines": len(lines),
"tables": tablecount,
"headings": headingcount,
"emptylineratio": round(emptyratio, 2)
}
}
4. Penanganan Error yang Baik
from pathlib import Path
def safeconvert(pdfpath: str, fallbackocr: bool = True) -> dict:
"""Konversi dengan error handling lengkap."""
path = Path(pdfpath)
if not path.exists():
return {"status": "error", "error": f"File tidak ditemukan: {pdfpath}"}
if not path.suffix.lower() in [".pdf", ".epub", ".mobi"]:
return {"status": "error", "error": f"Format tidak didukung: {path.suffix}"}
filesizemb = path.stat().stsize / (1024 1024)
if filesizemb > 500:
return {"status": "error", "error": f"File terlalu besar: {filesizemb:.0f}MB (max 500MB)"}
try:
from marker.converters.pdf import PdfConverter
from marker.models import createmodeldict
from marker.output import textfromrendered
import marker.settings as settings
if fallbackocr:
settings.OCRALLPAGES = True
converter = PdfConverter(artifactdict=createmodeldict())
rendered = converter(pdfpath)
text, metadata, images = textfromrendered(rendered)
validation = validateconversion(text)
return {
"status": "success",
"text": text,
"metadata": metadata,
"imagescount": len(images) if images else 0,
"validation": validation
}
except Exception as e:
return {"status": "error", "error": str(e)}
5. Tips Performa
Beberapa tips untuk mengoptimalkan performa Marker:
- Gunakan GPU: Proses konversi 3-5x lebih cepat dengan GPU CUDA
- Batch processing: Gunakan
markerCLI untuk banyak file sekaligus, bukan loopmarkersingle - Page range: Jika hanya butuh halaman tertentu, gunakan parameter
pagerange - Disk SSD: Model Marker cukup besar, SSD mempercepat loading model
- Caching: Implementasi caching untuk dokumen yang sering diakses
Kesimpulan
Marker adalah solusi yang sangat powerful untuk mengkonversi dokumen PDF, EPUB, dan MOBI menjadi Markdown berkualitas tinggi. Keunggulan utamanya adalah:
- Akurasi tinggi dalam mengenali layout, tabel, dan rumus matematika
- Multi-format mendukung PDF, EPUB, MOBI, dan gambar
- Multi-bahasa dengan dukungan OCR Surya
- Open-source dan aktif dikembangkan
- Mudah diintegrasikan ke pipeline RAG dan aplikasi Python
Dengan Marker, Anda bisa membangun pipeline document processing yang handal untuk berbagai kebutuhan, mulai dari knowledge base perusahaan, sistem pencarian dokumen, hingga chatbot berbasis RAG yang menggunakan dokumen internal sebagai sumber pengetahuan.
Untuk informasi lebih lanjut, kunjungi repository resmi Marker di GitHub dan dokumentasi Surya OCR untuk konfigurasi bahasa tambahan.