Files
tilbudgivern/pdf_extractor.py
2025-10-15 09:57:16 +00:00

239 lines
7.9 KiB
Python

#!/usr/bin/env python3
"""
PDF Text Extractor - Udvinder tekst fra både almindelige og scannede PDF'er
Støtter flere metoder: pdfplumber, PyPDF2 og OCR med tesseract
"""
import sys
import os
import re
from pathlib import Path
import json
# PDF processing libraries
import pdfplumber
import PyPDF2
import pytesseract
from pdf2image import convert_from_path
from PIL import Image
def extract_with_pdfplumber(pdf_path):
"""Udtrækker tekst med pdfplumber - bedst til moderne PDF'er"""
try:
text = ""
with pdfplumber.open(pdf_path) as pdf:
for page in pdf.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
return text.strip()
except Exception as e:
print(f"pdfplumber fejl: {e}")
return None
def extract_with_pypdf2(pdf_path):
"""Udtrækker tekst med PyPDF2 - god backup metode"""
try:
text = ""
with open(pdf_path, 'rb') as file:
reader = PyPDF2.PdfReader(file)
for page in reader.pages:
page_text = page.extract_text()
if page_text:
text += page_text + "\n"
return text.strip()
except Exception as e:
print(f"PyPDF2 fejl: {e}")
return None
def extract_with_ocr(pdf_path, language='dan+eng'):
"""Udtrækker tekst med OCR - til scannede PDF'er"""
try:
print(f"Starter OCR behandling af {pdf_path}...")
# Konverter PDF til billeder
images = convert_from_path(pdf_path, dpi=300)
text = ""
for i, image in enumerate(images):
print(f"Behandler side {i+1}/{len(images)}...")
# Kør OCR på billedet
page_text = pytesseract.image_to_string(image, lang=language)
if page_text.strip():
text += f"\n--- SIDE {i+1} ---\n"
text += page_text + "\n"
return text.strip()
except Exception as e:
print(f"OCR fejl: {e}")
return None
def clean_text(text):
"""Rengør og formater den udtrukne tekst"""
if not text:
return ""
# Fjern excessive whitespace
text = re.sub(r'\s+', ' ', text)
# Fix line breaks
text = re.sub(r'([.!?])\s+([A-ZÆØÅ])', r'\1\n\2', text)
# Clean up
text = text.strip()
return text
def analyze_construction_content(text):
"""Analysér tekstindhold for tømrer/byggerelateret information"""
if not text:
return {"found": False, "categories": [], "tasks": []}
# Søgeord for forskellige kategorier
construction_keywords = {
"fundamenter": ["fundament", "sokkel", "grundmur", "beton", "armering"],
"bindingsværk": ["stolpe", "rem", "bindingsværk", "spær", "bjælke"],
"tagarbejde": ["tag", "tagsten", "tegl", "rygning", "tagrender", "nedfald"],
"gulve": ["gulv", "gulvbjælke", "underlag", "isolering", "gulvbrædder"],
"vægge": ["væg", "ydervæg", "skillevæg", "isolering", "vindspærre"],
"døre_vinduer": ["dør", "vindue", "karm", "hængsel", "lås"],
"trapper": ["trappe", "trin", "vange", "gelænder", "repos"],
"værktøj": ["hammer", "save", "bor", "skruer", "søm", "målebånd"],
"materialer": ["træ", "brædder", "lægter", "krydsfiner", "spånplade"],
"teknikker": ["samling", "boring", "savning", "høvling", "slibning"]
}
found_categories = []
found_tasks = []
text_lower = text.lower()
for category, keywords in construction_keywords.items():
category_matches = []
for keyword in keywords:
if keyword in text_lower:
category_matches.append(keyword)
if category_matches:
found_categories.append({
"category": category,
"matches": category_matches,
"count": len(category_matches)
})
# Find potentielle opgaver (linjier der starter med numre eller punkter)
task_patterns = [
r'(\d+\.?\s+[A-ZÆØÅ][^.!?]*[.!?])', # Nummererede punkter
r'(-\s+[A-ZÆØÅ][^.!?]*[.!?])', # Bullet points
r'([A-ZÆØÅ][^.!?]{20,}[.!?])' # Lange sætninger (potentielle instruktioner)
]
for pattern in task_patterns:
matches = re.findall(pattern, text)
for match in matches:
if len(match) > 30: # Kun længere instruktioner
found_tasks.append(match.strip())
return {
"found": len(found_categories) > 0,
"categories": found_categories,
"tasks": found_tasks[:20], # Max 20 opgaver
"total_text_length": len(text),
"total_words": len(text.split())
}
def extract_pdf_text(pdf_path, output_file=None):
"""Hovedfunktion der prøver alle metoder for at udtrække tekst"""
if not os.path.exists(pdf_path):
print(f"Fil ikke fundet: {pdf_path}")
return None
print(f"Behandler PDF: {pdf_path}")
# Prøv pdfplumber først (bedst til normale PDF'er)
print("Prøver pdfplumber...")
text = extract_with_pdfplumber(pdf_path)
method_used = "pdfplumber"
# Hvis det ikke virker, prøv PyPDF2
if not text or len(text.strip()) < 100:
print("Prøver PyPDF2...")
text = extract_with_pypdf2(pdf_path)
method_used = "PyPDF2"
# Hvis stadig intet, prøv OCR
if not text or len(text.strip()) < 100:
print("Tekst ikke fundet - starter OCR...")
text = extract_with_ocr(pdf_path)
method_used = "OCR"
if text:
# Rengør teksten
text = clean_text(text)
# Analyser indhold
analysis = analyze_construction_content(text)
print(f"\n✅ Tekst ekstraheret med {method_used}")
print(f"📝 Total længde: {len(text)} tegn")
print(f"📊 Analyse: {'Byggerelateret indhold fundet' if analysis['found'] else 'Intet byggerelateret indhold fundet'}")
if analysis['found']:
print(f"🏗️ Kategorier fundet: {len(analysis['categories'])}")
print(f"📋 Opgaver fundet: {len(analysis['tasks'])}")
# Gem til fil hvis specificeret
if output_file:
with open(output_file, 'w', encoding='utf-8') as f:
f.write("=== PDF TEKST EKSTRAKTION ===\n")
f.write(f"Kilde: {pdf_path}\n")
f.write(f"Metode: {method_used}\n")
f.write(f"Dato: {sys.exc_info()}\n")
f.write("\n=== TEKST ===\n")
f.write(text)
f.write(f"\n\n=== ANALYSE ===\n")
f.write(json.dumps(analysis, ensure_ascii=False, indent=2))
print(f"💾 Resultat gemt til: {output_file}")
return {
"text": text,
"method": method_used,
"analysis": analysis,
"success": True
}
else:
print("❌ Kunne ikke udtrække tekst fra PDF")
return {"success": False, "error": "Ingen tekst fundet"}
def main():
"""Hovedprogram"""
if len(sys.argv) < 2:
print("Brug: python pdf_extractor.py <pdf_fil> [output_fil]")
sys.exit(1)
pdf_file = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else None
result = extract_pdf_text(pdf_file, output_file)
if result and result.get("success"):
print("\n" + "="*50)
print("UDTRUKKET TEKST (første 1000 tegn):")
print("="*50)
print(result["text"][:1000] + ("..." if len(result["text"]) > 1000 else ""))
if result["analysis"]["found"]:
print("\n" + "="*50)
print("FUNDET KATEGORIER:")
print("="*50)
for cat in result["analysis"]["categories"]:
print(f"{cat['category']}: {', '.join(cat['matches'])}")
return result
if __name__ == "__main__":
main()