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:
@@ -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.'),
|
||||||
|
|||||||
Reference in New Issue
Block a user