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