PDF zu Markdown in Python
Die Konvertierung von PDF zu Markdown gehört zu den wichtigsten Schritten in modernen Dokument-Pipelines. Egal ob Sie eine LLM-Anwendung oder eine RAG-Pipeline aufbauen oder einfach Dokumente in einem lesbaren Format archivieren wollen: PDF-Dateien per Python nach Markdown zu konvertieren liefert Ihnen eine strukturierte, portable Ausgabe, die sich überall weiterverarbeiten lässt.
Warum überhaupt PDF nach Markdown?
Markdown hat sich als Standardformat für den Datenaustausch in KI- und Dokumenten-Workflows etabliert. Hier die Gründe, warum sich die Konvertierung lohnt:
LLM-Kontextfenster arbeiten am besten mit strukturiertem Text. Große Sprachmodelle wie GPT-4, Claude oder Llama liefern spürbar bessere Ergebnisse, wenn sie sauberes Markdown statt Rohtext als Eingabe bekommen. Überschriften geben dem Modell eine Karte des Dokuments, und Formatierungen wie fett und kursiv tragen eine semantische Bedeutung, die beim reinen Text verlorengeht.
RAG-Pipelines brauchen saubere, gechunkte Texte mit erhaltenen Überschriften. Retrieval-Augmented-Generation-Systeme zerlegen Dokumente in Chunks, erzeugen Embeddings und holen zur Abfragezeit die relevantesten Stücke zurück. Markdown-Überschriften sind dafür natürliche Chunk-Grenzen — ein Split an ## liefert Ihnen semantisch zusammenhängende Abschnitte, jeweils mit einem Titel für den Chunk. Reine Textextraktion verliert diese Grenzen komplett, und Sie müssen auf Heuristiken wie Absatzlänge oder Satzanzahl zurückgreifen.
Markdown bewahrt die Struktur und bleibt trotzdem Klartext. Überschriften, Aufzählungen, nummerierte Listen, Tabellen, Fettdruck und Kursivschrift überstehen die Konvertierung in einem Format, das sowohl lesbar als auch maschinell parsbar ist. Eine Markdown-Datei ist einfach eine Textdatei und funktioniert mit Versionskontrolle, Volltextsuche und jeder beliebigen Programmiersprache.
Die Alternativen sind schlechter. Reiner Text verliert die gesamte Struktur: Überschriften sind nicht mehr von Fliesstext unterscheidbar, Tabellen zerfallen zu wirren Zeilen und Listen verlieren ihre Hierarchie. HTML-Konvertierung erhält die Struktur, bläht die Datei aber enorm auf — aus 2 KB Markdown werden schnell 15 KB HTML mit verschachtelten <div>-Tags, CSS-Klassen und Escape-Sequenzen. Markdown ist der goldene Mittelweg: strukturiert, schlank und universell unterstützt.
Schnellstart
Eine PDF-Seite in drei Zeilen nach sauberem Markdown konvertieren:
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)
WASM
import { WasmPdfDocument } from "pdf-oxide-wasm";
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0);
console.log(md);
doc.free();
Rust
use pdf_oxide::PdfDocument;
let mut doc = PdfDocument::open("paper.pdf")?;
let md = doc.to_markdown(0, true)?;
println!("{}", md);
Go
package main
import (
"fmt"
"log"
pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)
func main() {
doc, err := pdfoxide.Open("paper.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()
md, err := doc.ToMarkdown(0)
if err != nil { log.Fatal(err) }
fmt.Println(md)
}
C#
using PdfOxide;
using var doc = PdfDocument.Open("paper.pdf");
Console.WriteLine(doc.ToMarkdown(0));
PDF Oxide erkennt Überschriften anhand von Schriftgrößen-Clustern, behält Fett- und Kursivformatierung, überführt Tabellen in GFM-Syntax und bettet bei Bedarf Bilder ein. Keine andere Python-Bibliothek für PDFs liefert diese Markdown-Konvertierung direkt mit.
Installation
pip install pdf_oxide
Komplettes Dokument konvertieren
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("book.pdf")
md = doc.to_markdown_all(detect_headings=True)
with open("book.md", "w", encoding="utf-8") as f:
f.write(md)
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdownAll();
console.log(md);
doc.free();
Rust
let mut doc = PdfDocument::open("book.pdf")?;
let md = doc.to_markdown_all(true)?;
std::fs::write("book.md", &md)?;
Go
doc, _ := pdfoxide.Open("book.pdf")
defer doc.Close()
md, _ := doc.ToMarkdownAll()
_ = os.WriteFile("book.md", []byte(md), 0644)
C#
using var doc = PdfDocument.Open("book.pdf");
File.WriteAllText("book.md", doc.ToMarkdownAll());
to_markdown_all() konvertiert sämtliche Seiten und trennt sie mit ---.
Optionen für die Konvertierung
| Parameter | Voreinstellung | Beschreibung |
|---|---|---|
detect_headings |
True |
Schriftgrößen auf #-, ##-, ###-Überschriften abbilden |
preserve_layout |
False |
Visuelle Positionierung erhalten |
include_images |
True |
Bilder in die Ausgabe aufnehmen |
embed_images |
True |
Als base64-kodierte Data-URIs einbetten |
image_output_dir |
None |
Bilder stattdessen in dieses Verzeichnis speichern |
Nur Überschriften, keine Bilder
doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True, include_images=False)
Bilder in einem Verzeichnis ablegen
doc = PdfDocument("report.pdf")
md = doc.to_markdown(0,
detect_headings=True,
embed_images=False,
image_output_dir="output/images"
)
with open("output/report.md", "w") as f:
f.write(md)
Integration in RAG- und LLM-Pipelines
Markdown ist das ideale Format für RAG-Pipelines. Überschriften liefern natürliche Chunk-Grenzen, und die erhaltene Struktur bewahrt Bedeutung, die bei reinem Text verlorengeht.
Nach Überschriften chunken
Python
from pdf_oxide import PdfDocument
import re
doc = PdfDocument("paper.pdf")
md = doc.to_markdown_all(detect_headings=True)
# An Ueberschriften fuer semantisches Chunking splitten
chunks = re.split(r'\n(?=#{1,3} )', md)
chunks = [chunk.strip() for chunk in chunks if chunk.strip()]
for i, chunk in enumerate(chunks):
print(f"Chunk {i}: {chunk[:80]}...")
WASM
const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdownAll();
// An Ueberschriften fuer semantisches Chunking splitten
const chunks = md.split(/\n(?=#{1,3} )/).filter(c => c.trim());
chunks.forEach((chunk, i) => {
console.log(`Chunk ${i}: ${chunk.slice(0, 80)}...`);
});
doc.free();
Rust
let mut doc = PdfDocument::open("paper.pdf")?;
let md = doc.to_markdown_all(true)?;
let chunks: Vec<&str> = md.split("\n#")
.map(|c| c.trim())
.filter(|c| !c.is_empty())
.collect();
for (i, chunk) in chunks.iter().enumerate() {
println!("Chunk {}: {}...", i, &chunk[..chunk.len().min(80)]);
}
Go
doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()
md, _ := doc.ToMarkdownAll()
re := regexp.MustCompile(`\n(?=#{1,3} )`)
for i, chunk := range re.Split(md, -1) {
chunk = strings.TrimSpace(chunk)
if chunk == "" { continue }
if len(chunk) > 80 { chunk = chunk[:80] }
fmt.Printf("Chunk %d: %s...\n", i, chunk)
}
C#
using var doc = PdfDocument.Open("paper.pdf");
var md = doc.ToMarkdownAll();
var chunks = Regex.Split(md, @"\n(?=#{1,3} )")
.Select(c => c.Trim())
.Where(c => c.Length > 0)
.ToList();
for (int i = 0; i < chunks.Count; i++)
{
var preview = chunks[i].Length > 80 ? chunks[i][..80] : chunks[i];
Console.WriteLine($"Chunk {i}: {preview}...");
}
Chunking auf Seitenebene
Python
from pdf_oxide import PdfDocument
doc = PdfDocument("report.pdf")
chunks = []
for i in range(doc.page_count()):
md = doc.to_markdown(i, detect_headings=True, include_images=False)
chunks.append({
"page": i,
"content": md,
"source": "report.pdf"
})
WASM
const doc = new WasmPdfDocument(bytes);
const chunks = [];
for (let i = 0; i < doc.pageCount(); i++) {
const md = doc.toMarkdown(i);
chunks.push({ page: i, content: md, source: "report.pdf" });
}
doc.free();
Rust
let mut doc = PdfDocument::open("report.pdf")?;
let mut chunks = Vec::new();
for i in 0..doc.page_count()? {
let md = doc.to_markdown(i, true)?;
chunks.push((i, md));
}
Go
doc, _ := pdfoxide.Open("report.pdf")
defer doc.Close()
type Chunk struct {
Page int
Content string
Source string
}
n, _ := doc.PageCount()
chunks := make([]Chunk, 0, n)
for i := 0; i < n; i++ {
md, _ := doc.ToMarkdown(i)
chunks = append(chunks, Chunk{Page: i, Content: md, Source: "report.pdf"})
}
C#
using var doc = PdfDocument.Open("report.pdf");
var chunks = Enumerable.Range(0, doc.PageCount)
.Select(i => new { Page = i, Content = doc.ToMarkdown(i), Source = "report.pdf" })
.ToList();
Stapel-Konvertierung für eine Vektordatenbank
from pdf_oxide import PdfDocument, PdfError
from pathlib import Path
pdf_dir = Path("documents/")
documents = []
for pdf_path in pdf_dir.glob("*.pdf"):
try:
doc = PdfDocument(str(pdf_path))
md = doc.to_markdown_all(detect_headings=True, include_images=False)
documents.append({
"source": pdf_path.name,
"content": md,
"pages": doc.page_count()
})
except PdfError as e:
print(f"Uebersprungen {pdf_path.name}: {e}")
print(f"Konvertiert: {len(documents)} PDFs")
Bei 0,8 ms pro Seite ist die Konvertierung Tausender PDFs für Ihre Vektordatenbank eine Angelegenheit von Sekunden, nicht Minuten.
Wie die Überschriftenerkennung funktioniert
PDF Oxide clustert die auf der Seite gefundenen Schriftgrößen, um Überschriftenebenen abzuleiten:
- Alle Text-Spans mit Schriftgröße und Gewicht erfassen
- Spans nach Größe clustern — die häufigste Größe gilt als Fliesstext
- Grössere oder fettere Größen auf
#(am größten),##und###abbilden - Inline-Formatierungen wie fett (
**text**) und kursiv (*text*) erhalten
Das funktioniert zuverlässig bei wissenschaftlichen Arbeiten, Berichten und Dokumentationen. Bei PDFs mit ungewöhnlichen Schriftschemata deaktivieren Sie die Erkennung einfach:
md = doc.to_markdown(0, detect_headings=False)
PDF zu Markdown für LLM- und RAG-Pipelines
Die eingebaute Markdown-Konvertierung von PDF Oxide ist gezielt für KI-Workflows konzipiert. Die erkannte Überschriftenhierarchie bildet die semantische Struktur direkt ab, sodass die nachgelagerte Verarbeitung unkompliziert bleibt.
Markdown an ein LLM übergeben
Konvertieren Sie ein PDF und schicken Sie das Markdown direkt an ein Sprachmodell zur Zusammenfassung, für Q&A oder zur Analyse:
from pdf_oxide import PdfDocument
doc = PdfDocument("quarterly-report.pdf")
md = doc.to_markdown_all(detect_headings=True, include_images=False)
# An beliebige LLM-API senden — die Markdown-Struktur hilft dem Modell,
# die Gliederung des Dokuments zu verstehen
prompt = f"""Fasse das folgende Dokument zusammen. Achte auf die
Ueberschriftenstruktur, um die Hauptabschnitte zu erkennen.
{md}
"""
# response = llm_client.generate(prompt)
Da PDF Oxide die Überschriftenhierarchie (#, ##, ###) erhält, kann das LLM Abschnittstitel vom Fliesstext unterscheiden und abschnittsbewusste Zusammenfassungen liefern. Bei reiner Textextraktion muss das Modell den Anfang und das Ende von Abschnitten raten.
Nach Überschriften chunken für RAG
An Markdown-Überschriften zu splitten liefert semantisch sinnvolle Chunks, die gut eingebettet und präzise retrievd werden:
from pdf_oxide import PdfDocument
import re
doc = PdfDocument("technical-manual.pdf")
md = doc.to_markdown_all(detect_headings=True, include_images=False)
# An Ueberschriften in Chunks zerlegen
chunks = re.split(r'\n(?=#{1,3} )', md)
chunks = [c.strip() for c in chunks if c.strip()]
# Jeder Chunk beginnt mit einer Ueberschrift — ideal als Metadatum
for chunk in chunks:
lines = chunk.split('\n', 1)
title = lines[0].lstrip('#').strip()
body = lines[1].strip() if len(lines) > 1 else ""
# embed_and_store(title=title, content=body, source="technical-manual.pdf")
So erhalten Sie Chunks, die zusammenhängend sind (jeder Chunk entspricht einem kompletten Abschnitt), einen Titel besitzen (die Überschrift dient als Metadatum für das Retrieval) und in der Länge konsistent bleiben (Autoren erzeugen meist Abschnitte vergleichbarer Länge). Die Überschriftenerkennung von PDF Oxide macht das ganz ohne manuelle Konfiguration möglich — der Clustering-Algorithmus auf Basis der Schriftgrößen erkennt die Ebenen automatisch.
Warum PDF Oxide ideal für KI-Pipelines ist
Mit 0,8 ms pro Seite ist PDF Oxide schnell genug, um Dokumente spontan zur Abfragezeit zu konvertieren, nicht nur beim Indizieren. Das eröffnet Workflows, die mit langsameren Tools unpraktikabel wären:
- On-Demand-Konvertierung: Markdown-Ausgabe direkt, wenn ein Anwender ein PDF hochlädt — ohne spürbare Verzögerung
- Neu-Verarbeitung: Aktualisieren Sie Ihren RAG-Index, indem Sie alle PDFs nach einer Änderung der Chunking-Strategie neu konvertieren — Tausende Seiten in Sekunden
- Streaming-Pipelines: Konvertieren Sie PDFs direkt beim Eintreffen in einer Queue, ohne einen Rückstau aufzubauen
Stapelverarbeitung
Konvertieren Sie ein ganzes Verzeichnis voller PDFs in Markdown-Dateien:
from pdf_oxide import PdfDocument
from pathlib import Path
for pdf_path in Path("documents/").glob("*.pdf"):
doc = PdfDocument(str(pdf_path))
md_parts = []
for i in range(doc.page_count()):
md_parts.append(doc.to_markdown(i, detect_headings=True))
md_path = pdf_path.with_suffix(".md")
md_path.write_text("\n\n".join(md_parts))
print(f"Konvertiert {pdf_path.name} -> {md_path.name}")
Bei Sub-Millisekunden pro Seite werden Hunderte PDFs in Sekunden konvertiert. Für produktive Workloads mit Tausenden Dateien zeigt der Leitfaden zur Stapelverarbeitung Muster für parallele Verarbeitung.
PDF zu Markdown: PDF Oxide im Vergleich
| Werkzeug | Geschwindigkeit | Eingebaut | Überschriftenerkennung | Tabellen-Erhalt |
|---|---|---|---|---|
| PDF Oxide | 0,8 ms | Ja | Ja | Ja |
| pymupdf4llm | 55,5 ms (69× langsamer) | Nein (separates Paket) | Ja | Ja |
| marker | rund 500 ms und mehr | Nein (separates Tool) | Ja | Ja |
| pdfplumber + Eigencode | rund 23 ms und mehr | Nein (manuell) | Nein | Manuell |
| pypdf + Eigencode | rund 12 ms und mehr | Nein (manuell) | Nein | Nein |
PDF Oxide ist die einzige Python-Bibliothek für PDFs mit eingebauter, schneller Markdown-Konvertierung. Überschriften werden per Schriftgrößen-Clustering erkannt, Tabellen in GitHub Flavored Markdown überführt und Inline-Formatierungen bewahrt — alles in einem einzigen to_markdown()-Aufruf.
pymupdf4llm benötigt PyMuPDF (AGPL-lizenziert) plus das Zusatzpaket pymupdf4llm. Es ist 69-mal langsamer als PDF Oxide und bringt Copyleft-Auflagen mit, die mit proprietären Anwendungen oft nicht vereinbar sind.
marker ist ein eigenständiges Tool, keine Bibliothek. Es nutzt Deep-Learning-Modelle zur Layouterkennung — das macht es bei komplexen Layouts präzise, aber um Grössenordnungen langsamer. Außerdem benötigt es für gute Performance erheblich GPU-Speicher.
pdfplumber und pypdf bieten überhaupt keine Markdown-Konvertierung. Sie müssten selbst Code schreiben, um Überschriften zu erkennen, Tabellen zu rekonstruieren und die Ausgabe als Markdown zu formatieren — ein spürbarer Aufwand, um das nachzubauen, was PDF Oxide out of the box liefert.
Verwandte Seiten
- API zur Markdown-Konvertierung — vollständige API-Referenz
- PDF für RAG-Pipelines — ausführlicher RAG-Integrationsleitfaden
- Text aus PDF extrahieren — reine Textextraktion
- Stapelverarbeitung — Muster für Parallelverarbeitung