From dda14ccbf788017ff94738398614e2fc6be2a56c Mon Sep 17 00:00:00 2001 From: Floke Date: Wed, 9 Apr 2025 06:36:35 +0000 Subject: [PATCH] v1.4.6 Erweiterte Modi: Neuer Modus 23 Website-Detail Extraction + SERP Lookup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Neuer Modus 23 implementiert: Website Detail Extraction für Zeilen mit "x" in Spalte A. - scrape_website_details() extrahiert Seitentitel, Meta-Description und h1/h2/h3 aus der Startseite. - SERP-API Website Lookup (Modus 22) integriert: Fehlt in Spalte D eine Website, wird diese ermittelt und normalisiert. - Alignment Demo bleibt unverändert; neue Spalten AR (Website Rohtext) und AS (Website Zusammenfassung) werden beibehalten. - Main-Funktion und DataProcessor entsprechend der neuen Betriebsmodi angepasst. --- brancheneinstufung.py | 121 ++++++++++++++++++++++++++++-------------- 1 file changed, 80 insertions(+), 41 deletions(-) diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 34d08e5c..6160c11b 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1,14 +1,17 @@ #!/usr/bin/env python3 """ -Version: v1.4.5 +Version: v1.4.6 Datum: {aktuelles Datum} Git-Überschrift (max. 100 Zeichen): -v1.4.5 Timestamp-Skip und URL-Scheme ergänzt, Website-Fallback bleibt, Alignment Demo vollständig beibehalten +v1.4.6 Erweiterte Modi: Neuer Modus 23 Website-Detail Extraction + SERP Lookup Git-Änderungsbeschreibung: -- In _process_single_row() und process_verification_only() wird nun geprüft, ob in Spalte AO bereits ein Timestamp steht – in diesem Fall wird die Zeile übersprungen. -- In get_website_raw() wird vor dem Abruf geprüft, ob der URL mit "http" beginnt; andernfalls wird "https://" vorangestellt. -- Ansonsten bleibt die bisherige Logik (inklusive Website-Extraktion und -Zusammenfassung, Fallback in evaluate_branche_chatgpt und vollständige Alignment Demo) unverändert. +- Neuer Modus 23 implementiert: Website Detail Extraction für Zeilen mit "x" in Spalte A. +- scrape_website_details() extrahiert Seitentitel, Meta-Description und h1/h2/h3 aus der Startseite. +- SERP-API Website Lookup (Modus 22) integriert: Fehlt in Spalte D eine Website, wird diese ermittelt und normalisiert. +- Alignment Demo bleibt unverändert; neue Spalten AR (Website Rohtext) und AS (Website Zusammenfassung) werden beibehalten. +- Main-Funktion und DataProcessor entsprechend der neuen Betriebsmodi angepasst. + """ import os @@ -493,14 +496,19 @@ def process_verification_only(): # ==================== List Metatitel, Description und Überschriften aus Websiten aus ==================== def scrape_website_details(url): """ - Ruft die Website ab und extrahiert: - - den Seitentitel (aus im Kopfbereich), - - die Meta-Description (<meta name="description">), - - alle Überschriften h1, h2, h3 als zusammengefassten Text. - + Ruft die Website ab und extrahiert folgende Informationen: + - Seitentitel (<title>) + - Meta-Description (<meta name="description">) + - Alle Überschriften h1, h2, h3 (als kommaseparierte Listen) + + Die extrahierten Informationen werden in folgender Form kombiniert: + "Title: [Seitentitel] Description: [Meta-Description] H1: [h1-Überschriften] H2: [h2-Überschriften] H3: [h3-Überschriften]" + + Args: + url (str): Die URL der Website. + Returns: - Ein Dictionary mit den Schlüsseln "title", "description" und "headers". - Falls ein Element nicht gefunden wird, wird "k.A." zurückgegeben. + str: Die formatierte Zusammenfassung oder "k.A." bei Fehlern. """ # Falls URL kein Schema besitzt, ergänze "https://" if not url.lower().startswith("http"): @@ -509,32 +517,34 @@ def scrape_website_details(url): response = requests.get(url, timeout=10) soup = BeautifulSoup(response.text, Config.HTML_PARSER) - # Seitentitel + # Seitentitel extrahieren title_tag = soup.find("title") - title = title_tag.get_text().strip() if title_tag else "k.A." + title = title_tag.get_text().strip() if title_tag and title_tag.get_text() else "k.A." - # Meta-Description - meta_desc = soup.find("meta", attrs={"name": "description"}) - description = meta_desc["content"].strip() if meta_desc and meta_desc.get("content") else "k.A." + # Meta-Description extrahieren + meta_tag = soup.find("meta", attrs={"name": "description"}) + description = meta_tag["content"].strip() if meta_tag and meta_tag.get("content") else "k.A." - # Überschriften h1, h2, h3 - headers = [] - for tag in ['h1', 'h2', 'h3']: - for header in soup.find_all(tag): - header_text = header.get_text().strip() - if header_text: - headers.append(f"{tag.upper()}: {header_text}") - headers_text = "\n".join(headers) if headers else "k.A." + # Überschriften h1, h2, h3 extrahieren und kommasepariert zusammenfassen + headers = {} + for tag in ["h1", "h2", "h3"]: + elements = soup.find_all(tag) + # Extrahiere den Text und filtere leere Ergebnisse + header_texts = [el.get_text().strip() for el in elements if el.get_text().strip()] + headers[tag] = ", ".join(header_texts) if header_texts else "k.A." - # Rückgabe als Dictionary - return { - "title": title, - "description": description, - "headers": headers_text - } + # Kombiniere alle extrahierten Daten in einen String + combined = ( + f"Title: {title} " + f"Description: {description} " + f"H1: {headers['h1']} " + f"H2: {headers['h2']} " + f"H3: {headers['h3']}" + ) + return combined except Exception as e: - debug_print(f"Fehler beim Scrapen der Website {url}: {e}") - return {"title": "k.A.", "description": "k.A.", "headers": "k.A."} + debug_print(f"Fehler beim Auslesen der Website {url}: {e}") + return "k.A." # ==================== ALIGNMENT DEMO (Hauptblatt) ==================== def alignment_demo(sheet): @@ -1353,11 +1363,10 @@ class DataProcessor: def __init__(self): self.sheet_handler = GoogleSheetHandler() self.wiki_scraper = WikipediaScraper() - + def process_serp_website_lookup(self): debug_print("Starte SERP-API Website Lookup für alle Zeilen ohne CRM-Website (Spalte D).") for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): - # Prüfe, ob in Spalte D (Index 3) bereits ein Website-Wert vorhanden ist current_website = row[3] if len(row) > 3 else "" if current_website.strip() == "": company_name = row[1] if len(row) > 1 else "" @@ -1371,10 +1380,34 @@ class DataProcessor: else: debug_print(f"Zeile {i}: CRM-Website bereits vorhanden, überspringe.") + def process_website_details(self): + """ + Neuer Modus 23: + Für alle Zeilen, in denen das Re-Evaluation-Flag in Spalte A "x" steht + und ein gültiger Website-URL in Spalte D vorhanden ist, wird die Funktion + scrape_website_details(url) aufgerufen. Das kombinierte Ergebnis (Title, Meta-Description, + h1, h2, h3) wird in Spalte AR geschrieben. + """ + debug_print("Starte Modus 23: Website Detail Extraction für Zeilen mit 'x' in Spalte A.") + for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): + # Hier verarbeiten wir nur die Zeilen, die gezielt zur Re-Evaluation markiert sind: + if row[0].strip().lower() != "x": + continue + website_url = row[3] if len(row) > 3 else "" + if website_url.strip() == "" or website_url.strip().lower() == "k.a.": + debug_print(f"Zeile {i}: Keine gültige Website in Spalte D vorhanden, überspringe.") + continue + details = scrape_website_details(website_url) + # Speichere das Detail-Ergebnis in Spalte AR (Website Rohtext) + self.sheet_handler.sheet.update(values=[[details]], range_name=f"AR{i}") + debug_print(f"Zeile {i}: Website Detail Extraction abgeschlossen, Ergebnis in Spalte AR geschrieben.") + time.sleep(Config.RETRY_DELAY) + def process_rows(self, num_rows=None): global MODE if MODE == "1": - self.process_rows_complete() # Vollständige Verarbeitung (sofern implementiert) + # Vollständige Verarbeitung (alle Funktionen) + self.process_rows_complete() # Falls diese Methode bereits implementiert ist. elif MODE == "11": # Re-Evaluation markierter Zeilen (nur "x" in Spalte A) for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): @@ -1385,10 +1418,13 @@ class DataProcessor: for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): self._process_single_row(i, row, process_wiki=False, process_chatgpt=False) elif MODE == "22": - # SERP-API Website Lookup: Füllt Spalte D, wenn leer, mit dem SERP-Ergebnis + # SERP-API Website Lookup: Füllt Spalte D, falls leer self.process_serp_website_lookup() + elif MODE == "23": + # Neuer Modus 23: Detaillierte Website-Auswertung (nur für Zeilen mit "x" in Spalte A) + self.process_website_details() elif MODE == "31": - # Nur ChatGPT-Auswertung: Alle ChatGPT-Routinen werden ausgeführt (ohne Wiki und Website) + # Nur ChatGPT-Auswertung: Alle ChatGPT-Routinen (ohne Wikipedia und Website) for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2): self._process_single_row(i, row, process_wiki=False, process_chatgpt=True) elif MODE == "41": @@ -1402,6 +1438,7 @@ class DataProcessor: elif MODE == "8": process_batch_token_count() else: + # Falls ein unbekannter Modus gewählt wird start_index = self.sheet_handler.get_start_index() print(f"Starte bei Zeile {start_index+1}") rows_processed = 0 @@ -1421,6 +1458,7 @@ def main(): print("11: Re-Evaluation markierter Zeilen (nur 'x' in Spalte A)") print("21: Website-Scraping Testmodus (nur Website-Rohtext & Zusammenfassung)") print("22: SERP-API Website Lookup (nur Website-Daten ermitteln)") + print("23: Website Detail Extraction (nur für Zeilen mit 'x')") print("31: Nur ChatGPT-Auswertung (alle ChatGPT-Routinen)") print("41: Nur Wikipedia-Scraping") print("51: Batch-Verifizierung (alte Nummerierung beibehalten)") @@ -1433,14 +1471,13 @@ def main(): LOG_FILE = create_log_filename(MODE) debug_print(f"Start Betriebsmodus {MODE}") - # Anzeigen der Prompt-Übersicht for entry in prompt_overview()[1:]: debug_print(f"{entry[0]}: {entry[1]}") dp = DataProcessor() if MODE == "1": - dp.process_rows() # Vollständige Verarbeitung + dp.process_rows() elif MODE == "11": for i, row in enumerate(dp.sheet_handler.sheet_values[1:], start=2): if row[0].strip().lower() == "x": @@ -1450,6 +1487,8 @@ def main(): dp._process_single_row(i, row, process_wiki=False, process_chatgpt=False) elif MODE == "22": dp.process_serp_website_lookup() + elif MODE == "23": + dp.process_website_details() elif MODE == "31": for i, row in enumerate(dp.sheet_handler.sheet_values[1:], start=2): dp._process_single_row(i, row, process_wiki=False, process_chatgpt=True) @@ -1477,4 +1516,4 @@ def main(): print(f"Verarbeitung abgeschlossen. Logfile: {LOG_FILE}") if __name__ == '__main__': - main() + main() \ No newline at end of file