Documenten laden en tekst extraheren
U laadt PDF's, Word-documenten en platte tekstbestanden met Python-bibliotheken, schoont de geëxtraheerde tekst op en bereidt deze voor op chunking en embedding.
Documenten laden en tekst extraheren is een gratis AI Engineering Academy-les op CoddyKit. Dit is les 1 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject AI Engineering Academy. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Waarom documenten laden niet eenvoudig is
De eerste fase van elke RAG-pipeline is onbewerkte tekst uit je documenten halen. Dat klinkt eenvoudig, maar is in de praktijk verrassend lastig. PDF-bestanden kunnen tekst opslaan als tekens, als afbeeldingen of als een combinatie van beide. Word-documenten bevatten opmaakmarkeringen die je moet verwijderen. HTML-pagina's bevatten naast de echte inhoud navigatiemenu's en advertenties. Een robuuste lader moet al deze gevallen afhandelen en schone, samenhangende tekst produceren om in fragmenten op te delen.
PDF's laden met pypdf
pypdf is de standaardbibliotheek van Python voor het lezen van PDF-bestanden die ingesloten tekst bevatten. De bibliotheek haalt tekst pagina voor pagina op en behoudt de oorspronkelijke paginagrenzen, die waardevolle metagegevens zijn. pypdf kan echter gescande PDF's niet lezen (afbeeldingen van tekst) — daarvoor is OCR nodig. Haal altijd paginanummers samen met de tekst op, zodat je in je RAG-citaten naar de exacte pagina kunt verwijzen.
from pypdf import PdfReader
def load_pdf(file_path):
reader = PdfReader(file_path)
pages = []
for page_num, page in enumerate(reader.pages, start=1):
text = page.extract_text()
if text and text.strip(): # skip blank pages
pages.append({
'text': text,
'metadata': {
'source': file_path,
'page': page_num,
'doc_type': 'pdf'
}
})
return pages
docs = load_pdf('annual_report.pdf')
print(f'Loaded {len(docs)} non-empty pages')Word-documenten laden met python-docx
Microsoft Word-bestanden (.docx) slaan inhoud op in XML-indeling. De bibliotheek python-docx parseert deze XML en stelt alinea's, tabellen en koppen beschikbaar als Python-objecten. Haal de tekst van alinea's opeenvolgend op en leg kopniveaus vast om de documentstructuur te behouden — sectiekoppen zijn waardevolle context die de kwaliteit van het opdelen in fragmenten en van het ophalen verbetert wanneer je ze samen met de tekst waarop ze betrekking hebben opneemt.
from docx import Document
def load_docx(file_path):
doc = Document(file_path)
sections = []
current_section = {'heading': '', 'text': ''}
for para in doc.paragraphs:
if para.style.name.startswith('Heading'):
if current_section['text'].strip():
sections.append(current_section.copy())
current_section = {'heading': para.text, 'text': ''}
else:
current_section['text'] += para.text + '\n'
if current_section['text'].strip():
sections.append(current_section)
return [{'text': f"{s['heading']}\n{s['text']}",
'metadata': {'source': file_path, 'section': s['heading']}}
for s in sections]Webpagina's en HTML laden
HTML-pagina's bevatten veel ruis: navigatiebalken, voetteksten, cookiemeldingen en advertentieblokken. Gebruik BeautifulSoup om de HTML te parsen en alleen het hoofdgedeelte van de inhoud eruit te halen. Richt je op elementen zoals <article>, <main> of inhoudsspecifieke CSS-klassen. Verwijder script-, style- en nav-tags voordat je tekst extraheert, zodat je fragmenten niet vervuild raken met JavaScript-code of menu-items.
import requests
from bs4 import BeautifulSoup
def load_url(url):
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
# Remove noise elements
for tag in soup(['script', 'style', 'nav', 'footer', 'header', 'aside']):
tag.decompose()
# Try to find main content
main = soup.find('article') or soup.find('main') or soup.find('body')
text = main.get_text(separator='\n', strip=True)
return [{'text': text, 'metadata': {'source': url, 'doc_type': 'html'}}]Gescande PDF's verwerken met OCR
Gescande pdf's slaan pagina's op als afbeeldingen zonder ingesloten tekst. Om tekst te extraheren heb je Optical Character Recognition (OCR) nodig. De bibliotheek pytesseract is een wrapper rond Google's OCR-engine Tesseract. OCR is langzamer en minder nauwkeurig dan teksextractie (80-95% tekennauwkeurigheid bij goede scans), dus geef altijd de voorkeur aan digitale pdf's als die beschikbaar zijn. Geef in de metagegevens aan wanneer OCR is gebruikt, zodat je extracties met een lage betrouwbaarheid kunt controleren.
import pytesseract
from pdf2image import convert_from_path
def load_scanned_pdf(file_path):
# Convert PDF pages to PIL images
pages_as_images = convert_from_path(file_path, dpi=300)
results = []
for page_num, img in enumerate(pages_as_images, start=1):
text = pytesseract.image_to_string(img, lang='eng')
results.append({
'text': text,
'metadata': {
'source': file_path,
'page': page_num,
'ocr': True # flag for quality review
}
})
return results<span>Unstructured gebruiken voor elk formaat</span>
De bibliotheek unstructured is een Zwitsers zakmes voor het laden van documenten en verwerkt pdf's, Word-, Excel- en PowerPoint-documenten, HTML, e-mails en meer met één uniforme API. De bibliotheek gebruikt heuristieken om documentelementen (titel, doorlopende tekst, tabel, koptekst) te herkennen en retourneert deze als gestructureerde objecten. Dit is vooral nuttig wanneer je corpus verschillende documenttypen bevat en je metagegevens op elementniveau wilt zonder parsers per indeling te schrijven.
from unstructured.partition.auto import partition
def load_any_document(file_path):
elements = partition(filename=file_path)
docs = []
for element in elements:
docs.append({
'text': str(element),
'metadata': {
'source': file_path,
'element_type': type(element).__name__,
'category': element.category
}
})
return docs
# Works on .pdf, .docx, .pptx, .html, .eml, .xlsx
docs = load_any_document('presentation.pptx')<span>Tekst opschonen na extractie</span>
Ruwe geëxtraheerde tekst is zelden netjes. Pdf-extractie levert vaak afgebroken woorden aan het einde van regels op door kolomindelingen, extra witruimte, paginakopteksten die op elke pagina worden herhaald en verminkte speciale tekens. Pas een opschoningspijplijn toe: verwijder overbodige witruimte, voeg afgebroken woorden over regeleinden heen opnieuw samen, verwijder paginakop- en voetteksten die via patroonherkenning zijn gevonden en normaliseer Unicode-tekens. Schone tekst leidt tot aanzienlijk beter samenhangende tekstblokken.
import re
def clean_text(text):
# Rejoin hyphenated line breaks (PDF column artifacts)
text = re.sub(r'-(\n)([a-z])', r'\2', text)
# Normalize whitespace
text = re.sub(r' +', ' ', text)
text = re.sub(r'\n{3,}', '\n\n', text)
# Remove page numbers standing alone on a line
text = re.sub(r'^\d+$', '', text, flags=re.MULTILINE)
# Strip leading/trailing whitespace from each line
lines = [line.strip() for line in text.split('\n')]
return '\n'.join(lines).strip()<span>Laden vanuit databases en API's</span>
Niet alle kennis staat in bestanden. Interne databases, CRM-systemen, tools voor ticketbeheer en REST-API's zijn ook bronnen. Laad gestructureerde databaseregels door relevante tabellen te koppelen en veldwaarden samen te voegen tot alinea's in natuurlijke taal die het embeddingmodel kan begrijpen. Haal voor API's gepagineerde resultaten op en serialiseer elke record als tekstdocument met sleutel-waardeparen.
import psycopg2
def load_from_database(conn_string, query):
conn = psycopg2.connect(conn_string)
cursor = conn.cursor()
cursor.execute(query)
columns = [desc[0] for desc in cursor.description]
docs = []
for row in cursor.fetchall():
# Convert row to natural language text
parts = [f'{col}: {val}' for col, val in zip(columns, row) if val]
text = '\n'.join(parts)
docs.append({'text': text, 'metadata': {'source': 'database'}})
conn.close()
return docs<span>Herkomst van documenten bijhouden</span>
Elk geladen document moet gedurende de hele pijplijn metagegevens over de herkomst bevatten: de bron-URL of het bestandspad, de documenttitel, auteur, aanmaakdatum en datum van laatste wijziging. Deze metagegevens gaan met elk tekstblok mee naar de vectordatabase en worden zichtbaar in citaten van het LLM. Zonder herkomst kun je gebruikers niet vertellen waar een antwoord vandaan komt, kun je geen specifieke documenten in de index bijwerken en kun je zoekresultaten niet filteren op bronkenmerken.
import os
from datetime import datetime
def load_pdf_with_provenance(file_path):
from pypdf import PdfReader
reader = PdfReader(file_path)
stat = os.stat(file_path)
base_metadata = {
'source': file_path,
'title': reader.metadata.get('/Title', os.path.basename(file_path)),
'author': reader.metadata.get('/Author', 'Unknown'),
'doc_type': 'pdf',
'file_size_kb': stat.st_size // 1024,
'loaded_at': datetime.utcnow().isoformat()
}
pages = []
for i, page in enumerate(reader.pages, 1):
text = page.extract_text()
if text and text.strip():
pages.append({'text': text, 'metadata': {**base_metadata, 'page': i}})
return pages<span>Grote documentverzamelingen in batches laden</span>
Wanneer je duizenden documenten indexeert, laad je ze parallel met concurrent.futures om I/O- en CPU-bronnen optimaal te benutten. Implementeer een voortgangstracker en foutenlogboek, zodat mislukte documentladingen niet stilletjes inhoud uit je index verwijderen. Laat nooit één beschadigd bestand de volledige laadtaak beëindigen — vang uitzonderingen per document af en ga door met het volgende.
from concurrent.futures import ThreadPoolExecutor, as_completed
def batch_load_documents(file_paths, max_workers=8):
all_docs = []
errors = []
with ThreadPoolExecutor(max_workers=max_workers) as executor:
future_to_path = {
executor.submit(load_pdf_with_provenance, p): p
for p in file_paths
}
for future in as_completed(future_to_path):
path = future_to_path[future]
try:
docs = future.result()
all_docs.extend(docs)
print(f'Loaded {len(docs)} pages from {path}')
except Exception as e:
errors.append({'path': path, 'error': str(e)})
return all_docs, errors<span>De kwaliteit van geladen inhoud valideren</span>
Controleer na het laden of je betekenisvolle inhoud hebt geëxtraheerd. Controleer het volgende: minimale tekstlengte (sla tekstblokken met minder dan 50 tekens over), taaldetectie (filter niet-Engelse pagina's als je RAG-systeem alleen Engels ondersteunt) en detectie van verminkte tekst (een hoog aandeel niet-ASCII-tekens kan wijzen op een OCR-fout of problemen met de tekencodering). Leg statistieken vast, zoals het aantal geladen pagina's, de gemiddelde paginalengte en het foutenpercentage, om problemen bij het laden vroegtijdig te ontdekken.
def validate_documents(docs, min_length=100):
valid = []
skipped = 0
for doc in docs:
text = doc['text']
if len(text) < min_length:
skipped += 1
continue
# Check for high non-ASCII ratio (garbled OCR)
non_ascii = sum(1 for c in text if ord(c) > 127)
if non_ascii / max(len(text), 1) > 0.3:
skipped += 1
continue
valid.append(doc)
print(f'Valid: {len(valid)}, Skipped: {skipped}')
return valid<span>Korte controle</span>
Test je begrip van de concepten uit AI Engineering in deze les.
<span>Samenvatting van de les</span>
In deze les heb je het volgende geleerd: laders per bestandsindeling voor pdf's met pypdf, Word-documenten met python-docx, HTML met BeautifulSoup en gescande documenten met OCR, de bibliotheek unstructured als uniforme lader voor meerdere indelingen, en best practices voor productie, waaronder tekst opschonen, metagegevens over herkomst, parallel laden in batches en inhoudsvalidatie. Hierna behandelen we strategieën voor het opdelen in tekstblokken, die bepalen hoe opgehaalde tekst aan de modelcontext wordt gekoppeld.
Leer Python met een AI-tutor — gratis
Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.
- Cursussen
- 30
- Lessen
- 120
Veelgestelde vragen
Is de les “Documenten laden en tekst extraheren” gratis?
Ja — de volledige tekst van “Documenten laden en tekst extraheren” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus AI Engineering Academy wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus AI Engineering Academy bevat in totaal 4 lessen.
Wat leer ik in “Documenten laden en tekst extraheren”?
U laadt PDF's, Word-documenten en platte tekstbestanden met Python-bibliotheken, schoont de geëxtraheerde tekst op en bereidt deze voor op chunking en embedding. Je oefent met AI Engineering Academy door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.
Heb ik ervaring nodig om met AI Engineering Academy te beginnen?
Ervaring vooraf is niet nodig. AI Engineering Academy op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 1 van 4.
Hoe lang duurt de les “Documenten laden en tekst extraheren”?
De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.
Kan ik code schrijven en uitvoeren in deze les over AI Engineering Academy?
Ja. Elke les over AI Engineering Academy bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.
Alle lessen in deze cursus
- Documenten laden en tekst extraheren
- Chunkingstrategieën: fixed, sentence en recursive
- Indexeren: chunks embedden en opslaan
- Queryen, ophalen en genereren