v1.4.6 Erweiterte Modi: Neuer Modus 23 Website-Detail Extraction + SERP Lookup
- 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.
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
Version: v1.4.5
|
Version: v1.4.6
|
||||||
Datum: {aktuelles Datum}
|
Datum: {aktuelles Datum}
|
||||||
Git-Überschrift (max. 100 Zeichen):
|
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:
|
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.
|
- Neuer Modus 23 implementiert: Website Detail Extraction für Zeilen mit "x" in Spalte A.
|
||||||
- In get_website_raw() wird vor dem Abruf geprüft, ob der URL mit "http" beginnt; andernfalls wird "https://" vorangestellt.
|
- scrape_website_details() extrahiert Seitentitel, Meta-Description und h1/h2/h3 aus der Startseite.
|
||||||
- Ansonsten bleibt die bisherige Logik (inklusive Website-Extraktion und -Zusammenfassung, Fallback in evaluate_branche_chatgpt und vollständige Alignment Demo) unverändert.
|
- 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
|
import os
|
||||||
@@ -493,14 +496,19 @@ def process_verification_only():
|
|||||||
# ==================== List Metatitel, Description und Überschriften aus Websiten aus ====================
|
# ==================== List Metatitel, Description und Überschriften aus Websiten aus ====================
|
||||||
def scrape_website_details(url):
|
def scrape_website_details(url):
|
||||||
"""
|
"""
|
||||||
Ruft die Website ab und extrahiert:
|
Ruft die Website ab und extrahiert folgende Informationen:
|
||||||
- den Seitentitel (aus <title> im Kopfbereich),
|
- Seitentitel (<title>)
|
||||||
- die Meta-Description (<meta name="description">),
|
- Meta-Description (<meta name="description">)
|
||||||
- alle Überschriften h1, h2, h3 als zusammengefassten Text.
|
- 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:
|
Returns:
|
||||||
Ein Dictionary mit den Schlüsseln "title", "description" und "headers".
|
str: Die formatierte Zusammenfassung oder "k.A." bei Fehlern.
|
||||||
Falls ein Element nicht gefunden wird, wird "k.A." zurückgegeben.
|
|
||||||
"""
|
"""
|
||||||
# Falls URL kein Schema besitzt, ergänze "https://"
|
# Falls URL kein Schema besitzt, ergänze "https://"
|
||||||
if not url.lower().startswith("http"):
|
if not url.lower().startswith("http"):
|
||||||
@@ -509,32 +517,34 @@ def scrape_website_details(url):
|
|||||||
response = requests.get(url, timeout=10)
|
response = requests.get(url, timeout=10)
|
||||||
soup = BeautifulSoup(response.text, Config.HTML_PARSER)
|
soup = BeautifulSoup(response.text, Config.HTML_PARSER)
|
||||||
|
|
||||||
# Seitentitel
|
# Seitentitel extrahieren
|
||||||
title_tag = soup.find("title")
|
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-Description extrahieren
|
||||||
meta_desc = soup.find("meta", attrs={"name": "description"})
|
meta_tag = soup.find("meta", attrs={"name": "description"})
|
||||||
description = meta_desc["content"].strip() if meta_desc and meta_desc.get("content") else "k.A."
|
description = meta_tag["content"].strip() if meta_tag and meta_tag.get("content") else "k.A."
|
||||||
|
|
||||||
# Überschriften h1, h2, h3
|
# Überschriften h1, h2, h3 extrahieren und kommasepariert zusammenfassen
|
||||||
headers = []
|
headers = {}
|
||||||
for tag in ['h1', 'h2', 'h3']:
|
for tag in ["h1", "h2", "h3"]:
|
||||||
for header in soup.find_all(tag):
|
elements = soup.find_all(tag)
|
||||||
header_text = header.get_text().strip()
|
# Extrahiere den Text und filtere leere Ergebnisse
|
||||||
if header_text:
|
header_texts = [el.get_text().strip() for el in elements if el.get_text().strip()]
|
||||||
headers.append(f"{tag.upper()}: {header_text}")
|
headers[tag] = ", ".join(header_texts) if header_texts else "k.A."
|
||||||
headers_text = "\n".join(headers) if headers else "k.A."
|
|
||||||
|
|
||||||
# Rückgabe als Dictionary
|
# Kombiniere alle extrahierten Daten in einen String
|
||||||
return {
|
combined = (
|
||||||
"title": title,
|
f"Title: {title} "
|
||||||
"description": description,
|
f"Description: {description} "
|
||||||
"headers": headers_text
|
f"H1: {headers['h1']} "
|
||||||
}
|
f"H2: {headers['h2']} "
|
||||||
|
f"H3: {headers['h3']}"
|
||||||
|
)
|
||||||
|
return combined
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
debug_print(f"Fehler beim Scrapen der Website {url}: {e}")
|
debug_print(f"Fehler beim Auslesen der Website {url}: {e}")
|
||||||
return {"title": "k.A.", "description": "k.A.", "headers": "k.A."}
|
return "k.A."
|
||||||
|
|
||||||
# ==================== ALIGNMENT DEMO (Hauptblatt) ====================
|
# ==================== ALIGNMENT DEMO (Hauptblatt) ====================
|
||||||
def alignment_demo(sheet):
|
def alignment_demo(sheet):
|
||||||
@@ -1353,11 +1363,10 @@ class DataProcessor:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.sheet_handler = GoogleSheetHandler()
|
self.sheet_handler = GoogleSheetHandler()
|
||||||
self.wiki_scraper = WikipediaScraper()
|
self.wiki_scraper = WikipediaScraper()
|
||||||
|
|
||||||
def process_serp_website_lookup(self):
|
def process_serp_website_lookup(self):
|
||||||
debug_print("Starte SERP-API Website Lookup für alle Zeilen ohne CRM-Website (Spalte D).")
|
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):
|
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 ""
|
current_website = row[3] if len(row) > 3 else ""
|
||||||
if current_website.strip() == "":
|
if current_website.strip() == "":
|
||||||
company_name = row[1] if len(row) > 1 else ""
|
company_name = row[1] if len(row) > 1 else ""
|
||||||
@@ -1371,10 +1380,34 @@ class DataProcessor:
|
|||||||
else:
|
else:
|
||||||
debug_print(f"Zeile {i}: CRM-Website bereits vorhanden, überspringe.")
|
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):
|
def process_rows(self, num_rows=None):
|
||||||
global MODE
|
global MODE
|
||||||
if MODE == "1":
|
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":
|
elif MODE == "11":
|
||||||
# Re-Evaluation markierter Zeilen (nur "x" in Spalte A)
|
# Re-Evaluation markierter Zeilen (nur "x" in Spalte A)
|
||||||
for i, row in enumerate(self.sheet_handler.sheet_values[1:], start=2):
|
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):
|
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)
|
self._process_single_row(i, row, process_wiki=False, process_chatgpt=False)
|
||||||
elif MODE == "22":
|
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()
|
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":
|
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):
|
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)
|
self._process_single_row(i, row, process_wiki=False, process_chatgpt=True)
|
||||||
elif MODE == "41":
|
elif MODE == "41":
|
||||||
@@ -1402,6 +1438,7 @@ class DataProcessor:
|
|||||||
elif MODE == "8":
|
elif MODE == "8":
|
||||||
process_batch_token_count()
|
process_batch_token_count()
|
||||||
else:
|
else:
|
||||||
|
# Falls ein unbekannter Modus gewählt wird
|
||||||
start_index = self.sheet_handler.get_start_index()
|
start_index = self.sheet_handler.get_start_index()
|
||||||
print(f"Starte bei Zeile {start_index+1}")
|
print(f"Starte bei Zeile {start_index+1}")
|
||||||
rows_processed = 0
|
rows_processed = 0
|
||||||
@@ -1421,6 +1458,7 @@ def main():
|
|||||||
print("11: Re-Evaluation markierter Zeilen (nur 'x' in Spalte A)")
|
print("11: Re-Evaluation markierter Zeilen (nur 'x' in Spalte A)")
|
||||||
print("21: Website-Scraping Testmodus (nur Website-Rohtext & Zusammenfassung)")
|
print("21: Website-Scraping Testmodus (nur Website-Rohtext & Zusammenfassung)")
|
||||||
print("22: SERP-API Website Lookup (nur Website-Daten ermitteln)")
|
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("31: Nur ChatGPT-Auswertung (alle ChatGPT-Routinen)")
|
||||||
print("41: Nur Wikipedia-Scraping")
|
print("41: Nur Wikipedia-Scraping")
|
||||||
print("51: Batch-Verifizierung (alte Nummerierung beibehalten)")
|
print("51: Batch-Verifizierung (alte Nummerierung beibehalten)")
|
||||||
@@ -1433,14 +1471,13 @@ def main():
|
|||||||
LOG_FILE = create_log_filename(MODE)
|
LOG_FILE = create_log_filename(MODE)
|
||||||
debug_print(f"Start Betriebsmodus {MODE}")
|
debug_print(f"Start Betriebsmodus {MODE}")
|
||||||
|
|
||||||
# Anzeigen der Prompt-Übersicht
|
|
||||||
for entry in prompt_overview()[1:]:
|
for entry in prompt_overview()[1:]:
|
||||||
debug_print(f"{entry[0]}: {entry[1]}")
|
debug_print(f"{entry[0]}: {entry[1]}")
|
||||||
|
|
||||||
dp = DataProcessor()
|
dp = DataProcessor()
|
||||||
|
|
||||||
if MODE == "1":
|
if MODE == "1":
|
||||||
dp.process_rows() # Vollständige Verarbeitung
|
dp.process_rows()
|
||||||
elif MODE == "11":
|
elif MODE == "11":
|
||||||
for i, row in enumerate(dp.sheet_handler.sheet_values[1:], start=2):
|
for i, row in enumerate(dp.sheet_handler.sheet_values[1:], start=2):
|
||||||
if row[0].strip().lower() == "x":
|
if row[0].strip().lower() == "x":
|
||||||
@@ -1450,6 +1487,8 @@ def main():
|
|||||||
dp._process_single_row(i, row, process_wiki=False, process_chatgpt=False)
|
dp._process_single_row(i, row, process_wiki=False, process_chatgpt=False)
|
||||||
elif MODE == "22":
|
elif MODE == "22":
|
||||||
dp.process_serp_website_lookup()
|
dp.process_serp_website_lookup()
|
||||||
|
elif MODE == "23":
|
||||||
|
dp.process_website_details()
|
||||||
elif MODE == "31":
|
elif MODE == "31":
|
||||||
for i, row in enumerate(dp.sheet_handler.sheet_values[1:], start=2):
|
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)
|
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}")
|
print(f"Verarbeitung abgeschlossen. Logfile: {LOG_FILE}")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user