Code in zwei Bereiche aufgeteilt

Aufteilung des Codes in zwei unabhängige Verarbeitungsschritte.

Wikipedia-Branche und Umsatz werden nur geschrieben, wenn Wikipedia-URL vorhanden ist.

GPT-Aufruf überarbeitet (inkl. Timeout und Retry-Logik).

gpt_antworten_log.csv wird mit Zeitstempel aktualisiert.
This commit is contained in:
2025-03-30 17:42:46 +00:00
parent 28983aec43
commit 80a70fcf3b

View File

@@ -1,209 +1,192 @@
# Schritt 1: Nur Wikipedia-Daten extrahieren und in Google Sheet schreiben
import os import os
import time import time
import csv
import re import re
import gspread import gspread
import openai
import wikipedia import wikipedia
from bs4 import BeautifulSoup
import requests import requests
import openai
import csv
from bs4 import BeautifulSoup
from oauth2client.service_account import ServiceAccountCredentials from oauth2client.service_account import ServiceAccountCredentials
from datetime import datetime from datetime import datetime
# === KONFIGURATION === # === KONFIGURATION ===
EXCEL = "Bestandsfirmen.xlsx"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
CREDENTIALS = "service_account.json"
LANG = "de" LANG = "de"
LOG_CSV = "gpt_antworten_log.csv" CREDENTIALS = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
DURCHLÄUFE = int(input("Wieviele Zeilen sollen überprüft werden? ")) DURCHLÄUFE = int(input("Wieviele Zeilen sollen überprüft werden? "))
MAX_RETRIES = 3 MAX_RETRIES = 3
RETRY_DELAY = 5 RETRY_DELAY = 5
LOG_CSV = "gpt_antworten_log.csv"
# === OpenAI INIT === # === OpenAI API-KEY LADEN ===
with open("api_key.txt", "r") as f: with open("api_key.txt", "r") as f:
openai.api_key = f.read().strip() openai.api_key = f.read().strip()
# === GOOGLE SHEETS === # === GOOGLE SHEET VERBINDUNG ===
scope = ["https://www.googleapis.com/auth/spreadsheets"] scope = ["https://www.googleapis.com/auth/spreadsheets"]
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope) creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope)
client = gspread.authorize(creds) sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1
sheet = client.open_by_url(SHEET_URL).sheet1
sheet_values = sheet.get_all_values() sheet_values = sheet.get_all_values()
# === WIKIPEDIA KONFIG === # === STARTINDEX SUCHEN (Spalte N = Index 13) ===
filled_n = [row[13] if len(row) > 13 else '' for row in sheet_values[1:]]
start = next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip()), len(filled_n) + 1)
print(f"Starte bei Zeile {start+1}")
wikipedia.set_lang(LANG) wikipedia.set_lang(LANG)
# === DOMAIN SCHLÜSSEL ===
def extract_domain_key(url):
if not url:
return ""
clean_url = url.replace("https://", "").replace("http://", "").split("/")[0]
parts = clean_url.split(".")
return parts[0] if len(parts) > 1 else ""
# === INFOBOX-PARSING ===
def parse_infobox(soup):
infobox = soup.find("table", class_=["infobox", "infobox vcard"])
branche = umsatz = ""
if infobox:
for row in infobox.find_all("tr"):
th, td = row.find("th"), row.find("td")
if not th or not td:
continue
if "branche" in th.text.lower():
branche = td.get_text(separator=" ", strip=True)
if "umsatz" in th.text.lower():
umsatz_text = td.get_text(strip=True)
if "Mio" in umsatz_text:
match = re.search(r"(\d+[\d.,]*)\s*Mio", umsatz_text)
if match:
umsatz = match.group(1).replace(",", ".")
return branche, umsatz
# === WIKIPEDIA DATEN ===
WHITELIST_KATEGORIEN = [ WHITELIST_KATEGORIEN = [
"unternehmen", "hersteller", "produktion", "industrie", "unternehmen", "hersteller", "produktion", "industrie",
"maschinenbau", "technik", "dienstleistung", "chemie", "maschinenbau", "technik", "dienstleistung", "chemie",
"pharma", "elektro", "medizin", "bau", "energie", "pharma", "elektro", "medizin", "bau", "energie",
"logistik", "automobil", "handel", "textil", "klima" "logistik", "automobil"
] ]
# === HELFERFUNKTIONEN === def get_wikipedia_data(name, website_hint=""):
def extract_domain(url): domain_key = extract_domain_key(website_hint)
"""Extrahiert den Domain-Schlüssel aus der URL""" search_terms = [name, domain_key] if domain_key else [name]
if not url or not isinstance(url, str): for term in search_terms:
return "" if not term:
if not url.startswith("http"):
url = f"https://{url}"
return url.split("//")[-1].split("/")[0].split(".")[0]
def validate_wikipedia_content(content, name, domain):
"""Prüft ob der Artikel zum Unternehmen gehört"""
if not content or not name:
return False
name_fragments = name.lower().split()[:2]
domain_check = domain and domain.lower() in content.lower()
name_check = any(frag in content.lower() for frag in name_fragments)
return domain_check or name_check
def parse_infobox(soup):
"""Extrahiert Branche und Umsatz aus der Infobox"""
branche = umsatz = ""
if not soup:
return branche, umsatz
for row in soup.find_all("tr"):
th = row.find("th")
td = row.find("td")
if not th or not td:
continue continue
for attempt in range(MAX_RETRIES):
header = th.get_text(strip=True).lower() try:
value = td.get_text(separator=" ", strip=True) results = wikipedia.search(term, results=3)
for title in results:
# Branche erkennen try:
if any(s in header for s in ["branche", "industrie", "tätigkeitsfeld"]): page = wikipedia.page(title, auto_suggest=False)
branche = value html = requests.get(page.url, timeout=10).text
if name.split()[0].lower() in page.content.lower() or (domain_key and domain_key.lower() in html.lower()):
# Umsatz erkennen soup = BeautifulSoup(html, "html.parser")
if "umsatz" in header: branche, umsatz = parse_infobox(soup)
if "mio" in value.lower(): if not branche:
match = re.search(r"(\d{1,3}(?:[.,]\d{3})*(?:[.,]\d+)?)", value) for category in page.categories:
if match: if any(kw in category.lower() for kw in WHITELIST_KATEGORIEN):
umsatz = match.group(1).replace(",", ".") branche = category
break
return branche, umsatz return page.url, branche or "k.A.", umsatz or "k.A."
except:
def get_wikipedia_data(name, website):
"""Holt validierte Wikipedia-Daten"""
if not name:
return "", "k.A.", "k.A."
domain = extract_domain(website) if website else ""
for attempt in range(MAX_RETRIES):
try:
results = wikipedia.search(name, results=3)
for title in results:
try:
page = wikipedia.page(title, auto_suggest=False)
if not validate_wikipedia_content(page.content, name, domain):
continue continue
except Exception as e:
soup = BeautifulSoup(requests.get(page.url).text, "html.parser") print(f"⚠️ Wikipedia-Fehler ({term}, Versuch {attempt+1}): {str(e)[:100]}")
branche, umsatz = parse_infobox(soup) time.sleep(RETRY_DELAY)
# Fallback auf Kategorien
if not branche:
for cat in page.categories:
if any(kw in cat.lower() for kw in WHITELIST_KATEGORIEN):
branche = cat
break
return page.url, branche or "k.A.", umsatz or "k.A."
except (wikipedia.exceptions.PageError, wikipedia.exceptions.DisambiguationError):
continue
except Exception as e:
print(f"⚠️ Wikipedia-Fehler ({name}): {str(e)[:100]}")
time.sleep(RETRY_DELAY)
return "", "k.A.", "k.A." return "", "k.A.", "k.A."
def query_gpt(row, wiki_url): # === SCHRITT 1: WIKIPEDIA VERARBEITUNG ===
"""Verarbeitet die GPT-Abfrage mit verbessertem Error-Handling""" for i in range(start, min(start + DURCHLÄUFE, len(sheet_values))):
if not row or len(row) < 6: row = sheet_values[i]
return "k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A." print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Verarbeite Zeile {i+1}: {row[0]}")
url, wiki_branche, umsatz = get_wikipedia_data(row[0], row[1])
user_content = f"{row[0]};{row[1]};{row[2]};{row[4]};{row[5]}\nWikipedia: {wiki_url}" wiki_final = wiki_branche if url else "k.A."
umsatz_final = umsatz if url else "k.A."
system_prompt = { values = [
"role": "system", wiki_final,
"content": ( "k.A.", # LinkedIn-Branche leer
"Du bist ein Experte für Brancheneinstufung. Beantworte ausschließlich " umsatz_final,
"basierend auf den gegebenen Unternehmensdaten. Format: " "k.A.", "k.A.", "k.A.",
"Wikipedia-Branche;LinkedIn-Branche;Umsatz (Mio €);Empfohlene Neueinstufung;" url,
"Begründung;FSM-Relevanz;Techniker-Einschätzung;Techniker-Begründung" datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
) "k.A.", "k.A."
]
sheet.update(range_name=f"G{i+1}:P{i+1}", values=[values])
print(f"✅ Aktualisiert: {values[:3]}...")
time.sleep(RETRY_DELAY)
# === SCHRITT 2: GPT-BEWERTUNG ===
def classify_company(row, wikipedia_url=""):
user_prompt = {
"role": "user",
"content": f"{row[0]};{row[1]};{row[2]};{row[4]};{row[5]}\nWikipedia-Link: {wikipedia_url}"
} }
for attempt in range(MAX_RETRIES): for attempt in range(MAX_RETRIES):
try: try:
response = openai.chat.completions.create( response = openai.chat.completions.create(
model="gpt-3.5-turbo", model="gpt-3.5-turbo",
messages=[system_prompt, {"role": "user", "content": user_content}], messages=[
{
"role": "system",
"content": (
"Du bist ein Experte für Brancheneinstufung und FSM-Potenzialbewertung.\n"
"Bitte beziehe dich ausschließlich auf das konkret genannte Unternehmen.\n"
"FSM steht für Field Service Management. Ziel ist es, Unternehmen mit >50 Technikern im Außendienst zu identifizieren.\n\n"
"Struktur: Firmenname; Website; Ort; Aktuelle Einstufung; Beschreibung der Branche Extern\n\n"
"Gib deine Antwort im CSV-Format zurück (1 Zeile, 8 Spalten):\n"
"Wikipedia-Branche;LinkedIn-Branche;Umsatz (Mio €);Empfohlene Neueinstufung;Begründung;FSM-Relevanz;Techniker-Einschätzung;Techniker-Begründung"
)
},
user_prompt
],
temperature=0, temperature=0,
timeout=15 timeout=15
) )
return response.choices[0].message.content.strip() full_text = response.choices[0].message.content.strip()
break
except Exception as e: except Exception as e:
print(f"⚠️ GPT-Fehler (Versuch {attempt+1}): {str(e)[:100]}") print(f"⚠️ GPT-Fehler (Versuch {attempt+1}): {str(e)[:100]}")
time.sleep(RETRY_DELAY) time.sleep(RETRY_DELAY)
else:
print("❌ GPT-Abfrage fehlgeschlagen") print("❌ GPT 3x fehlgeschlagen Standardwerte")
return "k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A." full_text = "k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A.;k.A."
# === HAUPTLOGIK === lines = full_text.splitlines()
try: csv_line = next((l for l in lines if ";" in l), "")
start_index = next((i for i, row in enumerate(sheet_values[1:], start=1) if len(row) > 13 and not row[13].strip()), 1) parts = [v.strip() for v in csv_line.split(";")] if csv_line else ["k.A."] * 8
except StopIteration:
start_index = 1
for i in range(start_index, min(start_index + DURCHLÄUFE, len(sheet_values))): with open(LOG_CSV, "a", newline="", encoding="utf-8") as log:
if i >= len(sheet_values): writer = csv.writer(log, delimiter=";")
break writer.writerow([datetime.now().strftime("%Y-%m-%d %H:%M:%S"), row[0], *parts, full_text])
return parts
# === SCHRITT 2 DURCHFÜHREN ===
for i in range(start, min(start + DURCHLÄUFE, len(sheet_values))):
row = sheet_values[i] row = sheet_values[i]
print(f"\n[{datetime.now().strftime('%H:%M:%S')}] Verarbeite Zeile {i+1}: {row[0]}") print(f"\n[{datetime.now().strftime('%H:%M:%S')}] GPT-Bewertung für Zeile {i+1}: {row[0]}")
wiki_url = row[12] if len(row) > 12 else ""
# Wikipedia-Daten holen wiki, linkedin, umsatz_chat, new_cat, reason, fsm, techniker, techniker_reason = classify_company(row, wikipedia_url=wiki_url)
wiki_url, wiki_branche, wiki_umsatz = get_wikipedia_data(row[0], row[1] if len(row) > 1 else "") values = [
wiki,
# GPT-Abfrage linkedin,
gpt_response = query_gpt(row, wiki_url) umsatz_chat,
gpt_data = [x.strip('"').strip() for x in gpt_response.split(";")] new_cat,
gpt_data += ["k.A."] * (8 - len(gpt_data)) # Sicherstellen dass wir 8 Werte haben reason,
fsm,
# Finale Werte wiki_url,
final_branche = wiki_branche if wiki_url else "k.A."
final_umsatz = wiki_umsatz if wiki_url else "k.A."
# Google Sheet aktualisieren
update_values = [
final_branche, # G: Wikipedia-Branche
gpt_data[1], # H: LinkedIn-Branche
final_umsatz, # I: Umsatz
gpt_data[3], # J: Neueinstufung
gpt_data[4], # K: Begründung
gpt_data[5], # L: FSM-Relevanz
wiki_url, # M: Wikipedia-URL
datetime.now().strftime("%Y-%m-%d %H:%M:%S"), datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
gpt_data[6], # O: Techniker-Einschätzung techniker,
gpt_data[7] # P: Techniker-Begründung techniker_reason
] ]
sheet.update(range_name=f"G{i+1}:P{i+1}", values=[values])
try:
sheet.update(
range_name=f"G{i+1}:P{i+1}",
values=[update_values]
)
print(f"✅ Aktualisiert: {update_values[:3]}...")
except Exception as e:
print(f"⚠️ Google Sheets Update fehlgeschlagen: {str(e)[:100]}")
time.sleep(RETRY_DELAY) time.sleep(RETRY_DELAY)
print("\nProzess erfolgreich abgeschlossen") print("\nGPT-Bewertung abgeschlossen")