domain priorisierung

Vollständige Domain-Extraktion:
Implementiert über die neue Methode _get_full_domain, die nun den kompletten Domainnamen (inklusive TLD) liefert (z. B. "heimbach.com").

Normalisierung der Firmennamen:
Einführung der Funktion normalize_company_name, welche gängige Firmierungsformen (z. B. GmbH, AG, Aktiengesellschaft, Co. KG, mbH, & Co. KG, e.V., Limited, Ltd, Inc, Corp, Corporation, Gruppe) entfernt. Dies führt zu einem konsistenten Vergleich zwischen den Unternehmensdaten und Wikipedia-Titeln.

Verbesserte Artikelvalidierung:
In _validate_article werden nun:

Infobox-Links sowie externe Links geprüft, ob sie den vollständigen Domainnamen enthalten (ohne Dateilinks).

Der Vergleich der Wikipedia-Titel und des Firmennamens erfolgt auf Basis der normalisierten Namen.

Ein dynamischer Schwellenwert wird verwendet (0.60 statt 0.65), wenn ein definitiver Link-Match gefunden wurde.
This commit is contained in:
2025-03-31 19:18:07 +00:00
parent 37034e778e
commit 3f30c63adc

View File

@@ -12,7 +12,7 @@ import csv
# ==================== KONFIGURATION ====================
class Config:
VERSION = "1.0.14"
VERSION = "1.1.3"
LANG = "de"
CREDENTIALS_FILE = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
@@ -51,6 +51,17 @@ def clean_text(text):
text = re.sub(r'\s+', ' ', text).strip()
return text if text else "k.A."
def normalize_company_name(name):
"""Entfernt gängige Firmierungsformen und normalisiert den Namen."""
if not name:
return ""
# Liste gängiger Firmierungsformen, inklusive "Gruppe"
pattern = r'\b(gmbh|ag|aktiengesellschaft|co\.?\s*kg|mbh|&\s*co\.?\s*kg|e\.v\.|limited|ltd|inc|corp|corporation|gruppe)\b'
normalized = re.sub(pattern, '', name, flags=re.IGNORECASE)
normalized = re.sub(r'[\-]', ' ', normalized) # Ersetze Bindestriche durch Leerzeichen
normalized = re.sub(r'\s+', ' ', normalized).strip()
return normalized.lower()
# ==================== GOOGLE SHEET HANDLER ====================
class GoogleSheetHandler:
"""Handhabung der Google Sheets Interaktion"""
@@ -83,72 +94,86 @@ class WikipediaScraper:
def __init__(self):
wikipedia.set_lang(Config.LANG)
def _get_domain_key(self, website):
"""Extrahiert den Domain-Key aus der URL (ohne Protokoll und www)"""
def _get_full_domain(self, website):
"""Extrahiert den vollständigen Domainnamen (inklusive Topleveldomain) aus der URL."""
if not website:
return ""
website = website.lower().strip()
website = re.sub(r'^https?:\/\/', '', website)
website = re.sub(r'^www\.', '', website)
parts = website.split(".")
if len(parts) > 1:
return parts[0]
website = website.split('/')[0]
return website
def _generate_search_terms(self, company_name, website):
"""
Generiert Suchbegriffe in folgender Reihenfolge:
1. Domain-Key (falls vorhanden)
2. Die ersten zwei Wörter des Firmennamens
3. Der vollständige Firmenname
1. Vollständiger Domainname (z.B. "heimbach.com")
2. Die ersten zwei Wörter des normalisierten Firmennamens
3. Der vollständige, normalisierte Firmenname
"""
terms = []
domain_key = self._get_domain_key(website)
if domain_key:
terms.append(domain_key)
candidate = " ".join(company_name.split()[:2]).strip()
full_domain = self._get_full_domain(website)
if full_domain:
terms.append(full_domain)
normalized_name = normalize_company_name(company_name)
candidate = " ".join(normalized_name.split()[:2]).strip()
if candidate and candidate not in terms:
terms.append(candidate)
original = company_name.strip()
if original and original not in terms:
terms.append(original)
if normalized_name and normalized_name not in terms:
terms.append(normalized_name)
debug_print(f"Generierte Suchbegriffe: {terms}")
return terms
def _validate_article(self, page, company_name, domain_key):
def _validate_article(self, page, company_name, website):
"""
Validiert den Artikel:
- Sucht in der Infobox nach externen Links, die den Domain-Key enthalten.
Wird der Domain-Key gefunden, wird der Artikel als definitiv korrekt akzeptiert.
- Andernfalls wird der Wikipedia-Titel mit dem Firmennamen mittels Ähnlichkeitsvergleich geprüft.
- Sucht in der Infobox und in den externen Links nach Links, die den vollständigen Domainnamen enthalten.
Wird ein solcher Link gefunden, wird ein niedrigerer Schwellenwert (0.60) angewendet.
- Andernfalls werden der normalisierte Wikipedia-Titel und der normalisierte Firmenname verglichen.
"""
# Zuerst: Prüfe, ob der Domain-Key in den Links der Infobox vorkommt.
if domain_key:
full_domain = self._get_full_domain(website)
domain_found = False
if full_domain:
try:
html_raw = requests.get(page.url).text
soup = BeautifulSoup(html_raw, Config.HTML_PARSER)
# Suche in der Infobox
infobox = soup.find('table', class_=lambda c: c and 'infobox' in c.lower())
if infobox:
links = infobox.find_all('a', href=True)
for link in links:
href = link['href'].lower()
if domain_key in href:
href = link.get('href').lower()
# Überspringe Dateilinks
if href.startswith('/wiki/datei:'):
continue
if full_domain in href:
debug_print(f"Definitiver Link-Match in Infobox gefunden: {href}")
return True
domain_found = True
break
# Suche in den externen Links, falls vorhanden
if not domain_found and hasattr(page, 'externallinks'):
for ext_link in page.externallinks:
ext_link = ext_link.lower()
if full_domain in ext_link:
debug_print(f"Definitiver Link-Match in externen Links gefunden: {ext_link}")
domain_found = True
break
except Exception as e:
debug_print(f"Fehler beim Extrahieren des Infobox-Links: {str(e)}")
# Falls kein definitiver Link-Match, mache den Ähnlichkeitsvergleich:
clean_title = re.sub(r'\(.*?\)', '', page.title).lower()
clean_company = company_name.lower().strip()
similarity = SequenceMatcher(None, clean_title, clean_company).ratio()
debug_print(f"Ähnlichkeit: {similarity:.2f} ({clean_title} vs {clean_company})")
return similarity >= Config.SIMILARITY_THRESHOLD
debug_print(f"Fehler beim Extrahieren von Links: {str(e)}")
# Normalisierte Namen für Vergleich
normalized_title = normalize_company_name(page.title)
normalized_company = normalize_company_name(company_name)
similarity = SequenceMatcher(None, normalized_title, normalized_company).ratio()
debug_print(f"Ähnlichkeit (normalisiert): {similarity:.2f} ({normalized_title} vs {normalized_company})")
threshold = 0.60 if domain_found else Config.SIMILARITY_THRESHOLD
return similarity >= threshold
@retry_on_failure
def search_company_article(self, company_name, website):
"""Sucht mit optimierten Suchbegriffen (Domain-Key, Candidate, Name) nach dem Wikipedia-Artikel."""
"""Sucht mit optimierten Suchbegriffen (vollständiger Domainname, Candidate, normalisierter Name) nach dem Wikipedia-Artikel."""
search_terms = self._generate_search_terms(company_name, website)
domain_key = self._get_domain_key(website)
for term in search_terms:
try:
results = wikipedia.search(term, results=Config.WIKIPEDIA_SEARCH_RESULTS)
@@ -156,7 +181,7 @@ class WikipediaScraper:
for title in results:
try:
page = wikipedia.page(title, auto_suggest=False)
if self._validate_article(page, company_name, domain_key):
if self._validate_article(page, company_name, website):
return page
except (wikipedia.exceptions.DisambiguationError, wikipedia.exceptions.PageError) as e:
debug_print(f"Seitenfehler: {str(e)}")