Umsatz- und Mitarbeiterextraktion finalisiert – Umsatz in Mio € und Mitarbeiterzahl als ganze Zahl (

Umsatz-Extraktion:

Der numerische Teil des Umsatzes wird mittels der Helper-Funktion extract_numeric_value extrahiert.

Komma als Dezimaltrenner wird berücksichtigt (Punkte als Tausendertrennzeichen werden entfernt).

Enthält der Text "mrd" oder "milliarden", wird der Wert mit 1000 multipliziert; enthält er "mio" oder "millionen" bleibt der Wert unverändert.

Fehlt eine Einheit, wird der Wert als Euro angenommen und durch 1.000.000 geteilt.

Beispiel: "10,0 Mrd. Euro (2021/22)" ergibt 10 * 1000 = "10000" Mio.

Mitarbeiterextraktion:

Der numerische Teil der Mitarbeiterzahl wird ebenfalls mit extract_numeric_value extrahiert.

Für Mitarbeiter wird der String ohne Skalierung zurückgegeben.

Beispiel: "4.175 (2021/22)" wird so verarbeitet, dass die Punkte als Tausendertrennzeichen entfernt werden, was "4175" ergibt.

Verwendung von extrahierten Feldern:

In der Funktion extract_company_data werden die rohen Felder ("Branche", "Umsatz", "Mitarbeiter") zuerst über extract_fields_from_infobox_text bezogen und dann mittels extract_numeric_value (für Umsatz und Mitarbeiter) normalisiert.

Re‑Evaluierungsmodus:

Im Modus "2" werden alle Zeilen mit einem "x" in Spalte A verarbeitet, ohne dass nach der Zeilenzahl gefragt wird.

Zusätzlich wird im Re‑Evaluierungsmodus der komplette Infobox-Inhalt in der Konsole ausgegeben.

Warnung zu Unicode:

Die Warnung bezüglich ambigue Unicode-Zeichen kann ignoriert werden, wenn sie nicht zu funktionalen Problemen führt.
This commit is contained in:
2025-04-01 05:03:33 +00:00
parent 5702690cc9
commit e7accb2428

View File

@@ -12,7 +12,7 @@ import csv
# ==================== KONFIGURATION ==================== # ==================== KONFIGURATION ====================
class Config: class Config:
VERSION = "1.1.12" # Neue Version: Optimierte Umsatz- und Mitarbeiterextraktion VERSION = "1.1.12" # Version mit optimierter Umsatz- und Mitarbeiterextraktion
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"
@@ -72,16 +72,16 @@ def normalize_company_name(name):
def extract_numeric_value(raw_value, is_umsatz=False): def extract_numeric_value(raw_value, is_umsatz=False):
""" """
Extrahiert den numerischen Wert aus raw_value. Extrahiert den numerischen Wert aus raw_value.
- Bei Umsatz: Wenn "mrd" im Text vorkommt, multipliziere mit 1000; - Wenn ein Komma vorhanden ist, werden Punkte als Tausendertrennzeichen entfernt und das Komma als Dezimaltrenner genutzt.
wenn weder "mio" noch "mrd" gefunden werden, wird angenommen, dass der Wert in Euro ist, und durch 1e6 geteilt. - Wenn kein Komma vorhanden ist, werden alle Punkte entfernt (als Tausendertrennzeichen).
- Bei Mitarbeiterzahl: Entferne Tausendertrennzeichen und gib den ganzzahligen Wert zurück. - Für Umsatz: Falls "mrd" (Milliarden) vorkommt, multipliziert mit 1000; falls keine Einheit (weder "mio" noch "mrd") vorhanden ist, wird der Wert als Euro angenommen und durch 1e6 geteilt.
- Für Mitarbeiter: Der extrahierte Wert wird als ganze Zahl zurückgegeben.
""" """
raw = raw_value.lower() raw = raw_value.lower()
match = re.search(r'([\d.,]+)', raw) match = re.search(r'([\d.,]+)', raw)
if not match: if not match:
return raw_value.strip() return raw_value.strip()
num_str = match.group(1) num_str = match.group(1)
# Wenn Komma vorhanden, nehmen wir an, es handelt sich um einen Dezimaltrenner (mit eventuell Punkten als Tausendertrennzeichen)
if ',' in num_str: if ',' in num_str:
num_str = num_str.replace('.', '').replace(',', '.') num_str = num_str.replace('.', '').replace(',', '.')
try: try:
@@ -90,7 +90,6 @@ def extract_numeric_value(raw_value, is_umsatz=False):
debug_print(f"Fehler bei der Umwandlung von {num_str}: {e}") debug_print(f"Fehler bei der Umwandlung von {num_str}: {e}")
return raw_value.strip() return raw_value.strip()
else: else:
# Keine Kommas: Entferne alle Punkte (als Tausendertrennzeichen)
num_str = num_str.replace('.', '') num_str = num_str.replace('.', '')
try: try:
num = float(num_str) num = float(num_str)
@@ -122,7 +121,6 @@ class GoogleSheetHandler:
def get_start_index(self): def get_start_index(self):
filled_n = [row[13] if len(row) > 13 else '' for row in self.sheet_values[1:]] filled_n = [row[13] if len(row) > 13 else '' for row in self.sheet_values[1:]]
return next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1) return next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1)
# Update-Aufrufe erfolgen separat.
# ==================== WIKIPEDIA SCRAPER ==================== # ==================== WIKIPEDIA SCRAPER ====================
class WikipediaScraper: class WikipediaScraper:
@@ -215,19 +213,15 @@ class WikipediaScraper:
clean_val = re.sub(r'\[.*?\]|\(.*?\)', '', raw_value) clean_val = re.sub(r'\[.*?\]|\(.*?\)', '', raw_value)
return ' '.join(clean_val.split()).strip() return ' '.join(clean_val.split()).strip()
if target == 'umsatz': if target == 'umsatz':
# Extrahiere den numerischen Teil und berechne den Wert in Mio €
return extract_numeric_value(raw_value, is_umsatz=True) return extract_numeric_value(raw_value, is_umsatz=True)
if target == 'mitarbeiter': if target == 'mitarbeiter':
# Extrahiere den numerischen Teil; erwarte Format wie "4.175 (2021/22)"
return extract_numeric_value(raw_value, is_umsatz=False) return extract_numeric_value(raw_value, is_umsatz=False)
return "k.A." return "k.A."
def extract_full_infobox(self, soup): def extract_full_infobox(self, soup):
infobox = soup.find('table', class_=lambda c: c and any(kw in c.lower() for kw in ['infobox', 'vcard', 'unternehmen'])) infobox = soup.find('table', class_=lambda c: c and any(kw in c.lower() for kw in ['infobox', 'vcard', 'unternehmen']))
if not infobox: if not infobox:
return "k.A." return "k.A."
return clean_text(infobox.get_text(separator=' | ')) return clean_text(infobox.get_text(separator=' | '))
def extract_fields_from_infobox_text(self, infobox_text, field_names): def extract_fields_from_infobox_text(self, infobox_text, field_names):
result = {} result = {}
tokens = [token.strip() for token in infobox_text.split("|") if token.strip()] tokens = [token.strip() for token in infobox_text.split("|") if token.strip()]
@@ -239,30 +233,33 @@ class WikipediaScraper:
j += 1 j += 1
result[field] = tokens[j] if j < len(tokens) else "k.A." result[field] = tokens[j] if j < len(tokens) else "k.A."
return result return result
def extract_company_data(self, page_url): def extract_company_data(self, page_url):
if not page_url: if not page_url:
return {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'} return {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'}
try: try:
response = requests.get(page_url) response = requests.get(page_url)
soup = BeautifulSoup(response.text, Config.HTML_PARSER) soup = BeautifulSoup(response.text, Config.HTML_PARSER)
full_infobox = self.extract_full_infobox(soup) full_infobox = self.extract_full_infobox(soup)
extracted_fields = self.extract_fields_from_infobox_text(full_infobox, ['Branche', 'Umsatz', 'Mitarbeiter']) extracted_fields = self.extract_fields_from_infobox_text(full_infobox, ['Branche', 'Umsatz', 'Mitarbeiter'])
branche_val = extracted_fields.get('Branche', self._extract_infobox_value(soup, 'branche')) raw_branche = extracted_fields.get('Branche', self._extract_infobox_value(soup, 'branche'))
umsatz_val = extracted_fields.get('Umsatz', self._extract_infobox_value(soup, 'umsatz')) raw_umsatz = extracted_fields.get('Umsatz', self._extract_infobox_value(soup, 'umsatz'))
mitarbeiter_val = extracted_fields.get('Mitarbeiter', self._extract_infobox_value(soup, 'mitarbeiter')) raw_mitarbeiter = extracted_fields.get('Mitarbeiter', self._extract_infobox_value(soup, 'mitarbeiter'))
umsatz_val = extract_numeric_value(raw_umsatz, is_umsatz=True)
mitarbeiter_val = extract_numeric_value(raw_mitarbeiter, is_umsatz=False)
first_paragraph = self.extract_first_paragraph(page_url) first_paragraph = self.extract_first_paragraph(page_url)
return { return {
'url': page_url, 'url': page_url,
'first_paragraph': first_paragraph, 'first_paragraph': first_paragraph,
'branche': branche_val, 'branche': raw_branche,
'umsatz': umsatz_val, 'umsatz': umsatz_val,
'mitarbeiter': mitarbeiter_val, 'mitarbeiter': mitarbeiter_val,
'full_infobox': full_infobox 'full_infobox': full_infobox
} }
except Exception as e: except Exception as e:
debug_print(f"Extraktionsfehler: {str(e)}") debug_print(f"Extraktionsfehler: {str(e)}")
return {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'} return {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'}
@retry_on_failure @retry_on_failure
def search_company_article(self, company_name, website): def search_company_article(self, company_name, website):
@@ -284,63 +281,6 @@ class WikipediaScraper:
continue continue
return None return None
# ==================== Neue Helper-Funktion für numerische Werte ====================
def extract_numeric_value(raw_value, is_umsatz=False):
"""
Extrahiert den numerischen Wert aus raw_value.
- Falls ein Komma vorhanden ist, werden Punkte als Tausendertrennzeichen entfernt und das Komma als Dezimaltrenner genutzt.
- Falls kein Komma vorhanden ist, werden alle Punkte entfernt (als Tausendertrennzeichen).
- Für Umsatz: Wenn "mrd" im Text vorkommt, wird der Wert mit 1000 multipliziert; falls keine Einheit angegeben ist, wird angenommen, dass der Wert in Euro ist und durch 1e6 geteilt.
- Für Mitarbeiter: Der extrahierte numerische Wert wird als ganze Zahl zurückgegeben.
"""
raw = raw_value.lower()
match = re.search(r'([\d.,]+)', raw)
if not match:
return raw_value.strip()
num_str = match.group(1)
# Wenn Komma vorhanden, behandeln wir es als Dezimaltrenner
if ',' in num_str:
num_str = num_str.replace('.', '').replace(',', '.')
try:
num = float(num_str)
except Exception as e:
debug_print(f"Fehler bei der Umwandlung von {num_str}: {e}")
return raw_value.strip()
else:
# Entferne alle Punkte (als Tausendertrennzeichen)
num_str = num_str.replace('.', '')
try:
num = float(num_str)
except Exception as e:
debug_print(f"Fehler bei der Umwandlung von {num_str}: {e}")
return raw_value.strip()
if is_umsatz:
if "mrd" in raw or "milliarden" in raw:
num *= 1000
elif "mio" in raw or "millionen" in raw:
pass
else:
num /= 1e6
return str(int(round(num)))
else:
return str(int(round(num)))
# ==================== GOOGLE SHEET HANDLER ====================
class GoogleSheetHandler:
def __init__(self):
self.sheet = None
self.sheet_values = []
self._connect()
def _connect(self):
scope = ["https://www.googleapis.com/auth/spreadsheets"]
creds = ServiceAccountCredentials.from_json_keyfile_name(Config.CREDENTIALS_FILE, scope)
self.sheet = gspread.authorize(creds).open_by_url(Config.SHEET_URL).sheet1
self.sheet_values = self.sheet.get_all_values()
def get_start_index(self):
filled_n = [row[13] if len(row) > 13 else '' for row in self.sheet_values[1:]]
return next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1)
# Update-Aufrufe erfolgen separat.
# ==================== DATA PROCESSOR ==================== # ==================== DATA PROCESSOR ====================
class DataProcessor: class DataProcessor:
def __init__(self): def __init__(self):
@@ -377,7 +317,8 @@ class DataProcessor:
if article: if article:
company_data = self.wiki_scraper.extract_company_data(article.url) company_data = self.wiki_scraper.extract_company_data(article.url)
else: else:
company_data = {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.', 'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'} company_data = {'url': 'k.A.', 'first_paragraph': 'k.A.', 'branche': 'k.A.',
'umsatz': 'k.A.', 'mitarbeiter': 'k.A.', 'full_infobox': 'k.A.'}
self.sheet_handler.sheet.update(values=[[ self.sheet_handler.sheet.update(values=[[
company_data.get('url', 'k.A.'), company_data.get('url', 'k.A.'),
company_data.get('first_paragraph', 'k.A.'), company_data.get('first_paragraph', 'k.A.'),