Skip to content

PDF-Text extrahieren in Python

Text aus PDFs zu extrahieren gehört zu den häufigsten Aufgaben in der Dokumentverarbeitung — vom Aufbau eines Suchindex über das Einspeisen in ein RAG-System bis hin zu Data Mining und Compliance-Workflows. Diese Anleitung zeigt Ihnen Schritt für Schritt, wie Sie mit PDF Oxide Text aus PDFs in Python, JavaScript und Rust extrahieren: als reiner Text, mit zeichengenauen Koordinaten, als formatierte Spans, per OCR für gescannte Seiten, bei verschlüsselten Dateien und mit einem Performance-Tuning für große Stapelverarbeitung.

Drei Zeilen genügen, um Text aus einem beliebigen PDF zu lesen:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("document.pdf")
text = doc.extract_text(0)  # Seite 0
print(text)

WASM

import { WasmPdfDocument } from "pdf-oxide-wasm";

const bytes = new Uint8Array(buffer);
const doc = new WasmPdfDocument(bytes);
const text = doc.extractText(0); // Seite 0
console.log(text);
doc.free();

Rust

use pdf_oxide::PdfDocument;

let mut doc = PdfDocument::open("document.pdf")?;
let text = doc.extract_text(0)?;
println!("{}", text);

Go

package main

import (
    "fmt"
    "log"
    pdfoxide "github.com/yfedoseev/pdf_oxide/go"
)

func main() {
    doc, err := pdfoxide.Open("document.pdf")
    if err != nil { log.Fatal(err) }
    defer doc.Close()

    text, err := doc.ExtractText(0) // Seite 0
    if err != nil { log.Fatal(err) }
    fmt.Println(text)
}

C#

using PdfOxide;

using var doc = PdfDocument.Open("document.pdf");
var text = doc.ExtractText(0); // Seite 0
Console.WriteLine(text);

PDF Oxide extrahiert Text mit einem Durchschnitt von 0,8 ms pro Seite — fünfmal schneller als PyMuPDF, fünfzehnmal schneller als pypdf — bei einer Erfolgsquote von 100 Prozent auf 3.830 getesteten PDFs.

Warum PDF-Textextraktion so schwierig ist

PDF ist ein visuelles Format, kein Textformat. Anders als HTML oder Markdown speichert eine PDF-Datei keine „Absätze" oder „Sätze", sondern einzelne Zeichen an festen Koordinaten auf einer Seite. Damit aus diesen Zeichen lesbarer Text wird, müssen mehrere Schritte zusammenspielen:

  • Schriftarten dekodieren — PDF-Schriften ordnen Zeichencodes über Kodierungstabellen (WinAnsi, MacRoman, Unicode-CMaps, Type 1, TrueType, CIDFont) einzelnen Glyphen zu. Der Code 0x41 kann in einer Schrift „A" bedeuten, in einer anderen „α".
  • Text-Streams parsen — Operatoren wie Tj, TJ, ' und " positionieren Zeichen auf der Seite. Kerning-Korrekturen in TJ-Arrays verschieben Zeichen um Bruchteile eines Punkts, und fehlende Leerzeichen müssen aus den Abständen zwischen den Zeichenpositionen abgeleitet werden.
  • Layout rekonstruieren — Zeichen auf einer Seite haben keine explizite Leserichtung. Zweispaltige Layouts, Kopf- und Fußzeilen, Tabellen und Seitenleisten müssen räumlich analysiert werden, um einen linearen Textfluss zu erzeugen.
  • Kodierungs-Sonderfälle — CJK-Text (Chinesisch, Japanisch, Koreanisch) verwendet CIDFont/CMap-Kodierungen mit Tausenden von Glyphen. Arabisch und Hebräisch benötigen Rechts-nach-links-Umordnung. Ligaturen (fi, fl, ffi) müssen zerlegt werden.
  • Eingebettete Teilmengen — Viele PDFs betten nur die tatsächlich genutzten Glyphen mit eigenen Kodierungsvektoren ein. Eine Schrift kann Glyphen-Index 1→„T", 2→„h", 3→„e" zuordnen, ohne einer Standardkodierung zu folgen.

Genau deshalb liefern verschiedene PDF-Bibliotheken für dieselbe Datei unterschiedliche Textergebnisse — und manche scheitern bei komplexen Dokumenten komplett. PDF Oxide deckt all diese Fälle mit einem in Rust geschriebenen Parser ab, der auf 3.830 realen PDFs mit einer Erfolgsquote von 100 Prozent getestet wurde.

Installation

Python (PyPI):

pip install pdf_oxide

Vorgefertigte Wheels für Linux (x86_64, aarch64), macOS (Intel und Apple Silicon) sowie Windows (x86_64). Python 3.8 oder neuer. Keine Systemabhängigkeiten — der Rust-Core wird direkt in das Wheel kompiliert, Poppler, MuPDF oder andere C-Bibliotheken müssen Sie also nicht installieren.

JavaScript (npm):

npm install pdf-oxide-wasm

Läuft in Node.js 18 und neuer sowie in modernen Browsern. Das WASM-Binary ist im Paket enthalten.

Rust (Cargo):

cargo add pdf_oxide

Benötigt Rust 1.70 oder neuer. Ausser einer Standard-Rust-Toolchain sind keine Systemabhängigkeiten erforderlich.

Alle Seiten extrahieren

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("report.pdf")
full_text = []
for i in range(doc.page_count()):
    text = doc.extract_text(i)
    full_text.append(text)

print("\n".join(full_text))

WASM

const doc = new WasmPdfDocument(bytes);
const fullText = doc.extractAllText();
console.log(fullText);
doc.free();

Rust

let mut doc = PdfDocument::open("report.pdf")?;
let mut full_text = Vec::new();
for i in 0..doc.page_count()? {
    full_text.push(doc.extract_text(i)?);
}
println!("{}", full_text.join("\n"));

Go

doc, err := pdfoxide.Open("report.pdf")
if err != nil { log.Fatal(err) }
defer doc.Close()

full, err := doc.ExtractAllText()
if err != nil { log.Fatal(err) }
fmt.Println(full)

C#

using var doc = PdfDocument.Open("report.pdf");
var parts = new List<string>();
for (int i = 0; i < doc.PageCount; i++)
    parts.Add(doc.ExtractText(i));
Console.WriteLine(string.Join("\n", parts));

Text mit Zeichenpositionen extrahieren

Holen Sie sich präzise Koordinaten, Schriftnamen und Schriftgrößen für jedes einzelne Zeichen:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("paper.pdf")
chars = doc.extract_chars(0)

for ch in chars[:20]:
    print(f"'{ch.char}' at ({ch.x:.1f}, {ch.y:.1f}) "
          f"font={ch.font_name} size={ch.font_size:.1f}")

WASM

const doc = new WasmPdfDocument(bytes);
const chars = doc.extractChars(0);
for (const ch of chars.slice(0, 20)) {
    console.log(`'${ch.char}' at (${ch.x.toFixed(1)}, ${ch.y.toFixed(1)}) font=${ch.fontName} size=${ch.fontSize.toFixed(1)}`);
}
doc.free();

Rust

let mut doc = PdfDocument::open("paper.pdf")?;
let chars = doc.extract_chars(0)?;
for ch in chars.iter().take(20) {
    println!("'{}' at ({:.1}, {:.1}) font={} size={:.1}",
        ch.char, ch.x, ch.y, ch.font_name, ch.font_size);
}

Go

doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()

chars, _ := doc.ExtractChars(0)
for _, ch := range chars[:20] {
    fmt.Printf("%q at (%.1f, %.1f) font=%s size=%.1f\n",
        ch.Char, ch.X, ch.Y, ch.FontName, ch.FontSize)
}

C#

using var doc = PdfDocument.Open("paper.pdf");
var chars = doc.ExtractChars(0);
foreach (var ch in chars.Take(20))
    Console.WriteLine($"'{ch.Char}' at ({ch.X:F1}, {ch.Y:F1}) font={ch.FontName} size={ch.FontSize:F1}");

Zu jedem Zeichen erhalten Sie:

Feld Typ Beschreibung
char str Das Unicode-Zeichen
x, y float Position in Punkten
font_size float Schriftgröße in Punkten
font_name str PostScript-Schriftname
bbox tuple Begrenzungsrahmen (x0, y0, x1, y1)

Die zeichengenaue Extraktion ist ideal, um Tabellen zu rekonstruieren, Überschriften anhand der Schriftgröße zu erkennen oder Bounding Boxes um Textregionen zu legen. Sie können Zeichen beispielsweise über die y-Koordinate in Zeilen gruppieren und Spaltengrenzen anhand größerer Lücken in den x-Positionen bestimmen.

Formatierte Text-Spans extrahieren

Fassen Sie zusammenhängende Zeichen mit gleicher Schrift und Größe zu Spans zusammen:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("paper.pdf")
spans = doc.extract_spans(0)

for span in spans:
    print(f"'{span.text}' font={span.font_name} size={span.font_size:.1f}")

WASM

const doc = new WasmPdfDocument(bytes);
const spans = doc.extractSpans(0);
for (const span of spans) {
    console.log(`'${span.text}' font=${span.fontName} size=${span.fontSize.toFixed(1)}`);
}
doc.free();

Rust

let mut doc = PdfDocument::open("paper.pdf")?;
let spans = doc.extract_spans(0)?;
for span in &spans {
    println!("'{}' font={} size={:.1}", span.text, span.font_name, span.font_size);
}

Praktisch, um Überschriften oder fetten Text zu erkennen oder daraus eine strukturierte Ausgabe zu erzeugen.

Stapelverarbeitung

Verarbeiten Sie Hunderte oder Tausende PDFs in einem Durchlauf:

from pdf_oxide import PdfDocument, PdfError
from pathlib import Path

pdf_dir = Path("documents/")
for pdf_path in pdf_dir.glob("*.pdf"):
    try:
        doc = PdfDocument(str(pdf_path))
        for i in range(doc.page_count()):
            text = doc.extract_text(i)
            # Text weiterverarbeiten ...
    except PdfError as e:
        print(f"Uebersprungen {pdf_path.name}: {e}")

Bei 0,8 ms pro Seite benötigt die Verarbeitung von 3.830 PDFs rund 3,1 Sekunden. Für produktive Pipelines finden Sie im Leitfaden zur Stapelverarbeitung Muster für Parallelverarbeitung mit Multiprocessing und asynchronem I/O.

Gescannte PDFs verarbeiten (OCR)

Enthält ein PDF eingescannte Bilder statt echtem Text, liefert extract_text() nur eine leere oder sehr kurze Zeichenkette zurück. Nutzen Sie in diesem Fall die eingebaute OCR von PDF Oxide:

from pdf_oxide import PdfDocument

doc = PdfDocument("scanned.pdf")
text = doc.extract_text(0)

if not text.strip():
    # Seite ist vermutlich ein Scan — OCR verwenden
    text = doc.extract_text_ocr(0)
    print(text)

PDF Oxide nutzt PaddleOCR über die ONNX Runtime — eine Tesseract-Installation ist nicht nötig. Details zu Modellauswahl und Konfiguration stehen im OCR-Leitfaden.

Verschlüsselte PDFs verarbeiten

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("protected.pdf", password="secret")
text = doc.extract_text(0)
print(text)

WASM

const doc = new WasmPdfDocument(bytes);
doc.authenticate("secret");
const text = doc.extractText(0);
console.log(text);
doc.free();

Rust

let mut doc = PdfDocument::open_with_password("protected.pdf", "secret")?;
let text = doc.extract_text(0)?;
println!("{}", text);

Go

doc, _ := pdfoxide.Open("protected.pdf")
defer doc.Close()

if _, err := doc.Authenticate("secret"); err != nil { log.Fatal(err) }
text, _ := doc.ExtractText(0)
fmt.Println(text)

C#

using var doc = PdfDocument.OpenWithPassword("protected.pdf", "secret");
Console.WriteLine(doc.ExtractText(0));

Unterstützt werden PDFs mit AES-256-, AES-128- und RC4-Verschlüsselung. Während pdfplumber verschlüsselte Dateien gar nicht öffnen kann und pdfminer an AES-256 scheitert, verarbeitet PDF Oxide alle gängigen PDF-Verschlüsselungen transparent.

Ausgabe als Markdown

Für eine strukturierte Ausgabe mit Überschriften und Formatierungen:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("paper.pdf")
md = doc.to_markdown(0, detect_headings=True)
print(md)

WASM

const doc = new WasmPdfDocument(bytes);
const md = doc.toMarkdown(0);
console.log(md);
doc.free();

Rust

let mut doc = PdfDocument::open("paper.pdf")?;
let md = doc.to_markdown(0, true)?;
println!("{}", md);

Go

doc, _ := pdfoxide.Open("paper.pdf")
defer doc.Close()

md, _ := doc.ToMarkdown(0)
fmt.Println(md)

C#

using var doc = PdfDocument.Open("paper.pdf");
Console.WriteLine(doc.ToMarkdown(0));

Weitere Integrationsmuster für RAG und LLMs finden Sie im Leitfaden PDF zu Markdown.

Im PDF suchen

Finden Sie Textstellen seitenübergreifend, inklusive Positionsdaten:

Python

from pdf_oxide import PdfDocument

doc = PdfDocument("manual.pdf")
results = doc.search("configuration")
for r in results:
    print(f"Page {r.page}: '{r.text}' at ({r.x:.0f}, {r.y:.0f})")

WASM

const doc = new WasmPdfDocument(bytes);
const results = doc.search("configuration", false);
for (const r of results) {
    console.log(`Page ${r.page}: '${r.text}' at (${r.x.toFixed(0)}, ${r.y.toFixed(0)})`);
}
doc.free();

Rust

let mut pdf = Pdf::open("manual.pdf")?;
let results = pdf.search("configuration")?;
for r in &results {
    println!("Page {}: '{}' at ({:.0}, {:.0})", r.page, r.text, r.bbox.x, r.bbox.y);
}

Go

doc, _ := pdfoxide.Open("manual.pdf")
defer doc.Close()

results, _ := doc.SearchAll("configuration", false)
for _, r := range results {
    fmt.Printf("Page %d: %q at (%.0f, %.0f)\n", r.PageIndex, r.Text, r.X, r.Y)
}

C#

using var doc = PdfDocument.Open("manual.pdf");
foreach (var r in doc.SearchAll("configuration", caseSensitive: false))
    Console.WriteLine($"Page {r.PageIndex}: '{r.Text}' at ({r.X:F0}, {r.Y:F0})");

Vergleich mit anderen Python-Bibliotheken für PDFs

Für die PDF-Textextraktion in Python gibt es mehrere Optionen. So schneiden sie im Vergleich ab:

  • pypdf — reines Python, keine C-Abhängigkeiten. Einfach zu installieren, aber langsam (12 ms pro Seite) und scheitert an 1,6 Prozent der PDFs, weil Schrift- und Kodierungsunterstützung nicht ausreicht. Keine Zeichenpositionen. Brauchbar für einfache PDFs, wenn Geschwindigkeit keine Rolle spielt.
  • pdfplumber — baut auf pdfminer auf und liefert ausführliche Zeichen- und Tabelleninformationen. Sehr langsam (23 ms pro Seite) und kann verschlüsselte PDFs nicht öffnen. Sinnvoll für Tabellenextraktion, wenn Sie Zellen-Details benötigen und Performance zweitrangig ist.
  • PyMuPDF (fitz) — Python-Bindings zur C-Bibliothek MuPDF. Schnell (4,6 ms pro Seite) und zuverlässig (99,3 Prozent Erfolgsquote). Erfordert eine C-Bibliothek und steht unter AGPL. Eine solide Wahl, wenn die Lizenz zu Ihrem Projekt passt.
  • pypdfium2 — Python-Bindings zur PDFium-Engine von Google. Schnell (4,1 ms pro Seite), aber mit hoher p99-Latenz (42 ms) bei komplexen Dokumenten. Die API ist deutlich schmaler als bei PyMuPDF.
  • pdfminer.six — reines Python mit detaillierter Layoutanalyse. Sehr langsam und kaum noch gepflegt. Scheitert an AES-256-verschlüsselten PDFs. Wurde grösstenteils von pdfplumber abgelöst.
  • PDF Oxide — Rust-Kern mit Python-Bindings über PyO3. Die schnellste Option (0,8 ms pro Seite), 100 Prozent Erfolgsquote, unterstützt alle gängigen Verschlüsselungen und bringt OCR direkt mit. MIT-lizenziert, ohne Systemabhängigkeiten.

PDF Oxide wurde gezielt gebaut, um die Lücken der vorhandenen Bibliotheken zu schließen: die Performancegrenzen reiner Python-Parser, die Lizenzeinschränkungen von MuPDF und die Stabilitätsprobleme, an denen andere Bibliotheken bei realen PDFs mit ungewöhnlichen Schriften, beschädigten Cross-Reference-Tabellen oder exotischen Kodierungen scheitern.

Performance: Wie schnell ist PDF Oxide?

Gemessen auf 3.830 PDFs aus drei unabhängigen öffentlichen Testkorpora:

Bibliothek Mittel p99 Erfolgsquote
PDF Oxide 0,8 ms 9 ms 100 %
PyMuPDF 4,6 ms 28 ms 99,3 %
pypdfium2 4,1 ms 42 ms 99,2 %
pypdf 12,1 ms 97 ms 98,4 %
pdfplumber 23,2 ms 189 ms 98,8 %

Für eine Pipeline mit 10.000 PDFs bedeutet das:

  • PDF Oxide: 8 Sekunden
  • PyMuPDF: 46 Sekunden
  • pypdf: 2 Minuten
  • pdfplumber: 3,9 Minuten

Methodik und Anleitung zum Nachstellen finden Sie in den ausführlichen Benchmarks.

Häufige Probleme und Lösungen

Leere Textausgabe

Liefert extract_text() eine leere Zeichenkette zurück, enthält die Seite wahrscheinlich eingescannte Bilder statt echtem Text. Verwenden Sie in diesem Fall extract_text_ocr(). Die Einrichtung ist unter Gescannte PDFs mit OCR beschrieben.

Fehlerhafte oder unleserliche Zeichen

Das deutet meist auf eine Schriftart mit nicht standardkonformem Kodierungsvektor oder ohne ToUnicode-CMap hin. PDF Oxide fängt die meisten dieser Fälle ab, aber bei absichtlich verschleierten PDFs (DRM-geschützter Inhalt) kann die Ausgabe dennoch falsch sein.

Fehlende Leerzeichen oder zusammengeklebte Wörter

PDF-Textoperatoren platzieren Zeichen einzeln. Leerzeichen müssen aus dem Abstand zwischen Zeichen relativ zur Leerzeichenbreite der Schrift abgeleitet werden. Kleben die Wörter zusammen, nutzen Sie extract_chars() und implementieren Sie eine eigene Abstandslogik auf Basis der Koordinaten.

Abweichende Ausgabe gegenüber anderen Bibliotheken

Jede Bibliothek verwendet eigene Heuristiken für Leerzeichen-Erkennung, Zeilenumbrüche und Leserichtung. PDF Oxide erreicht auf 3.830 PDFs eine Textübereinstimmung von 99,5 Prozent mit PyMuPDF. Die restlichen 0,5 Prozent betreffen vor allem die Whitespace-Normalisierung und den Umgang mit Ligaturen.

Typische Einsatzgebiete

Volltextsuche — Extrahieren Sie den Text jeder Seite aller PDFs in einem Dokumentenarchiv und speisen Sie das Ergebnis in Elasticsearch, Typesense oder eine Vektordatenbank ein. Dank der Geschwindigkeit von PDF Oxide können Sie Tausende Dokumente bei Bedarf neu indizieren.

RAG-Pipelines (Retrieval-Augmented Generation) — Extrahieren und zerlegen Sie PDF-Text in Chunks, die Sie anschließend mit OpenAI, Cohere oder Open-Source-Modellen einbetten. Mit extract_spans() bleibt die Überschriftenstruktur erhalten, sodass Chunks sauber an den Dokumentabschnitten ausgerichtet sind. Siehe den Leitfaden PDF zu Markdown für LLM-optimierte Ausgaben.

Compliance und Audit — Durchsuchen Sie Verträge, Rechnungen und regulatorische Einreichungen nach bestimmten Klauseln oder Schlüsselwörtern. doc.search() liefert Treffer über alle Seiten hinweg mit präzisen Positionen, oder Sie extrahieren den Volltext für eine NLP-basierte Klauselerkennung.

Datenextraktion — Holen Sie strukturierte Daten aus Rechnungen, Belegen, Kontoauszügen und Formularen. Kombinieren Sie extract_chars() mit fachspezifischen Regeln, um Felder wie „Gesamtbetrag" oder „Rechnungsdatum" zu finden und die Werte daneben auszulesen.

Wissenschaftliche Arbeit — Verarbeiten Sie Tausende Forschungsarbeiten für Literaturrecherchen, Zitationsextraktionen oder Meta-Analysen. PDF Oxide kommt mit der gesamten Bandbreite an PDF-Produzenten (LaTeX, Word, InDesign, Quark) und den in der Wissenschaft üblichen Schriftkodierungen zurecht.

Verwandte Seiten