Das Problem: Strukturierte Daten aus Text extrahieren
Du hast hunderte Geschäftsdokumente, E-Mails oder Berichte und musst daraus systematisch Informationen extrahieren:
- Firmennamen aus Verträgen
- Geldbeträge aus Rechnungen
- Personennamen aus Pressemitteilungen
- Produktbezeichnungen aus Spezifikationen
Manuell ist das nicht skalierbar. Aber welcher automatisierte Ansatz ist der richtige?
Die 4 NER-Ansätze im Überblick
┌─────────────────────────────────────────────────────────────────┐
│ NER APPROACHES: VON EINFACH BIS KI-POWERED │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ REGEX │────▶│ spaCy │────▶│ GLiNER │ │
│ │ │ │ │ │ │ │
│ │ Pattern- │ │ Rule-based │ │ Zero-shot │ │
│ │ Matching │ │ + Statistical│ │ NER Model │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Schnell, │ │ Gute Balance │ │ Flexibel, │ │
│ │ Präzise │ │ Performance/ │ │ Custom │ │
│ │ aber rigide │ │ Genauigkeit │ │ Entity Types │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
│ │
│ ┌──────────────┐ │
│ │ LLM-based │ │
│ │ │ │
│ │ Höchste │ │
│ │ Genauigkeit │ │
│ └──────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Teuer, aber │ │
│ │ versteht │ │
│ │ Kontext │ │
│ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
Ansatz 1: Regex – Der Klassiker
Regex ist perfekt für strukturierte, vorhersehbare Muster.
Beispiel: E-Mail-Adressen und Geldbeträge extrahieren
import re
text = """
Kontakt: max.mustermann@firma.de
Rechnungsbetrag: EUR 12.500,00
Ansprechpartner: anna.schmidt@partner.com
Summe: € 8.750,50
"""
# E-Mail-Adressen
email_pattern = r'[\w.-]+@[\w.-]+\.\w+'
emails = re.findall(email_pattern, text)
print(f"E-Mails: {emails}")
# Output: ['max.mustermann@firma.de', 'anna.schmidt@partner.com']
# Geldbeträge (EUR/€ Format)
money_pattern = r'(?:EUR|€)\s*[\d.,]+'
amounts = re.findall(money_pattern, text)
print(f"Beträge: {amounts}")
# Output: ['EUR 12.500,00', '€ 8.750,50']
Wann Regex verwenden?
| Gut für | Schlecht für |
|---|---|
| E-Mail-Adressen | Personennamen |
| Telefonnummern | Firmennamen |
| Datumsformate | Kontextabhängige Entitäten |
| Postleitzahlen | Variationen und Tippfehler |
Fazit: Regex ist schnell und präzise, aber nur für strukturierte Muster geeignet.
Ansatz 2: spaCy – Der Industriestandard
spaCy bietet trainierte NER-Modelle für Standard-Entitäten.
Installation und Modell laden
pip install spacy
python -m spacy download de_core_news_lg
Beispiel: Entitäten aus Geschäftstext extrahieren
import spacy
nlp = spacy.load("de_core_news_lg")
text = """
Die BMW Group hat heute bekannt gegeben, dass CEO Oliver Zipse
eine Investition von 2,5 Milliarden Euro in das Werk München plant.
Der Aufsichtsrat tagt am 15. März 2026 in Berlin.
"""
doc = nlp(text)
for ent in doc.ents:
print(f"{ent.text:25} → {ent.label_}")
Output:
BMW Group → ORG
Oliver Zipse → PER
2,5 Milliarden Euro → MONEY
München → LOC
15. März 2026 → DATE
Berlin → LOC
spaCy Entity Types
| Label | Bedeutung | Beispiele |
|---|---|---|
PER |
Person | Oliver Zipse, Angela Merkel |
ORG |
Organisation | BMW Group, Deutsche Bank |
LOC |
Ort | München, Berlin |
MONEY |
Geldbetrag | 2,5 Milliarden Euro |
DATE |
Datum | 15. März 2026 |
Wann spaCy verwenden?
| Gut für | Schlecht für |
|---|---|
| Standard-Entitäten (PER, ORG, LOC) | Domänenspezifische Entitäten |
| Deutsche/Englische Texte | Custom Entity Types |
| Produktionsumgebungen | Zero-Shot ohne Training |
| Batch-Verarbeitung | Seltene Entitätstypen |
Ansatz 3: GLiNER – Zero-Shot Custom Entities
GLiNER ist ein Zero-Shot NER-Modell: Du definierst beliebige Entity-Typen, ohne zu trainieren.
Installation
pip install gliner
Beispiel: Custom Entities extrahieren
from gliner import GLiNER
model = GLiNER.from_pretrained("urchade/gliner_multi-v2.1")
text = """
Die Tesla Model 3 Performance erreicht eine Reichweite von 547 km
und beschleunigt in 3,3 Sekunden von 0 auf 100 km/h.
CEO Elon Musk präsentierte das Update auf der CES 2026.
"""
# Custom Entity Types definieren
labels = ["Automodell", "Reichweite", "Beschleunigung", "Person", "Event"]
entities = model.predict_entities(text, labels)
for entity in entities:
print(f"{entity['text']:30} → {entity['label']}")
Output:
Tesla Model 3 Performance → Automodell
547 km → Reichweite
3,3 Sekunden von 0 auf 100 km/h → Beschleunigung
Elon Musk → Person
CES 2026 → Event
GLiNER vs spaCy
| Aspekt | spaCy | GLiNER |
|---|---|---|
| Entity Types | Vordefiniert | Frei definierbar |
| Training nötig? | Für Custom: Ja | Nein (Zero-Shot) |
| Geschwindigkeit | Schneller | Langsamer |
| Genauigkeit | Hoch für Standard | Hoch für Custom |
| GPU empfohlen? | Nein | Ja |
Wann GLiNER verwenden?
| Gut für | Schlecht für |
|---|---|
| Domänenspezifische Entitäten | Hohe Batch-Volumen |
| Prototyping ohne Training | Ressourcen-limitierte Umgebungen |
| Wechselnde Entity-Typen | Wenn spaCy ausreicht |
Ansatz 4: LLM-basierte Extraktion
LLMs wie GPT-4 oder Claude verstehen Kontext und können komplexe Extraktionen durchführen.
Beispiel mit OpenAI
from openai import OpenAI
import json
client = OpenAI()
text = """
In der Vorstandssitzung vom 12.02.2026 wurde beschlossen, dass
Dr. Maria Weber (CFO) und Thomas Klein (CTO) das Joint Venture
mit Siemens AG im Wert von 450 Mio. EUR federführend verhandeln.
Die Unterzeichnung ist für Q2 2026 geplant.
"""
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{
"role": "system",
"content": """Extrahiere strukturierte Informationen aus dem Text.
Antworte als JSON mit folgenden Feldern:
- personen: Liste von {name, rolle}
- organisationen: Liste von Firmennamen
- geldbetraege: Liste von {betrag, waehrung}
- termine: Liste von {datum, beschreibung}"""
},
{"role": "user", "content": text}
],
response_format={"type": "json_object"}
)
result = json.loads(response.choices[0].message.content)
print(json.dumps(result, indent=2, ensure_ascii=False))
Output:
{
"personen": [
{"name": "Dr. Maria Weber", "rolle": "CFO"},
{"name": "Thomas Klein", "rolle": "CTO"}
],
"organisationen": ["Siemens AG"],
"geldbetraege": [
{"betrag": "450 Mio.", "waehrung": "EUR"}
],
"termine": [
{"datum": "12.02.2026", "beschreibung": "Vorstandssitzung"},
{"datum": "Q2 2026", "beschreibung": "Geplante Unterzeichnung"}
]
}
Wann LLM-basierte Extraktion verwenden?
| Gut für | Schlecht für |
|---|---|
| Komplexe, kontextabhängige Extraktion | Hohe Volumen (Kosten!) |
| Strukturierte JSON-Ausgaben | Latenz-kritische Anwendungen |
| Relationen zwischen Entitäten | Datenschutz-sensible Daten |
| Unstrukturierte Dokumente | Wenn einfachere Methoden reichen |
Vergleich: Welcher Ansatz für welchen Use Case?
Performance-Vergleich
| Ansatz | Geschwindigkeit | Genauigkeit (Standard) | Genauigkeit (Custom) | Kosten |
|---|---|---|---|---|
| Regex | ⚡⚡⚡⚡⚡ | ⚡⚡⚡⚡⚡ (für Pattern) | ❌ | Gratis |
| spaCy | ⚡⚡⚡⚡ | ⚡⚡⚡⚡ | ⚡⚡ (Training nötig) | Gratis |
| GLiNER | ⚡⚡ | ⚡⚡⚡ | ⚡⚡⚡⚡ | Gratis |
| LLM | ⚡ | ⚡⚡⚡⚡⚡ | ⚡⚡⚡⚡⚡ | €€€ |
Entscheidungsbaum
┌─────────────────────────────────────────────────────────────────┐
│ WELCHER NER-ANSATZ? │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Sind die Entitäten strukturiert (E-Mail, Datum, PLZ)? │
│ │ │
│ ├── JA → Regex │
│ │ │
│ └── NEIN ↓ │
│ │
│ Sind es Standard-Entitäten (Person, Ort, Organisation)? │
│ │ │
│ ├── JA → spaCy │
│ │ │
│ └── NEIN ↓ │
│ │
│ Hast du Zeit/Daten für Training? │
│ │ │
│ ├── JA → spaCy Custom Training │
│ │ │
│ └── NEIN ↓ │
│ │
│ Ist Latenz/Kosten kritisch? │
│ │ │
│ ├── JA → GLiNER │
│ │ │
│ └── NEIN → LLM-basiert │
│ │
└─────────────────────────────────────────────────────────────────┘
Kombinierter Ansatz: Das Beste aus allen Welten
In der Praxis kombinierst du oft mehrere Ansätze:
import re
import spacy
from gliner import GLiNER
def hybrid_extraction(text):
results = {}
# 1. Regex für strukturierte Daten
results["emails"] = re.findall(r'[\w.-]+@[\w.-]+\.\w+', text)
results["phone"] = re.findall(r'\+?[\d\s-]{10,}', text)
# 2. spaCy für Standard-Entitäten
nlp = spacy.load("de_core_news_lg")
doc = nlp(text)
results["persons"] = [ent.text for ent in doc.ents if ent.label_ == "PER"]
results["organizations"] = [ent.text for ent in doc.ents if ent.label_ == "ORG"]
# 3. GLiNER für domänenspezifische Entitäten
gliner = GLiNER.from_pretrained("urchade/gliner_multi-v2.1")
custom_entities = gliner.predict_entities(text, ["Produktname", "Technologie"])
results["products"] = [e["text"] for e in custom_entities if e["label"] == "Produktname"]
return results
Fazit: Der richtige Ansatz für dein Projekt
Quick Summary
| Du brauchst… | Verwende… |
|---|---|
| E-Mails, Telefonnummern, PLZ | Regex |
| Personen, Orte, Organisationen | spaCy |
| Custom Entities ohne Training | GLiNER |
| Komplexe, kontextabhängige Extraktion | LLM |
| Alles zusammen | Hybrid-Ansatz |
Meine Empfehlung
- Starte mit spaCy – deckt 80% der Fälle ab
- Ergänze mit Regex – für strukturierte Muster
- Nutze GLiNER für Prototypen – schnell Custom Entities testen
- LLM nur wenn nötig – wenn Kontext entscheidend ist
Die beste NER-Lösung ist die, die zu deinem Use Case passt – nicht die technisch aufwändigste.
Ressourcen
Du möchtest Entity Extraction oder NLP in deinem Unternehmen einsetzen? Dann schreib mir einfach auf LinkedIn oder buche direkt einen kostenlosen Call.