import os import time import pandas as pd import gspread import openai import wikipedia from bs4 import BeautifulSoup import requests from oauth2client.service_account import ServiceAccountCredentials from datetime import datetime # === CONFIG === EXCEL = "Bestandsfirmen.xlsx" SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" CREDENTIALS = "service_account.json" CHUNK = 10 LANG = "de" # === AUTHENTICATION === scope = ["https://www.googleapis.com/auth/spreadsheets"] creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope) sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1 # OpenAI API-Key aus externer Datei laden with open("api_key.txt", "r") as f: openai.api_key = f.read().strip() # === LOAD DATA === df = pd.read_excel(EXCEL) for col in ["Wikipedia-URL", "Wikipedia-Branche", "LinkedIn-Branche", "Umsatz (Mio €)", "Empfohlene Neueinstufung", "Begründung Neueinstufung", "FSM-Relevanz", "Letzte Prüfung", "Techniker-Einschätzung (Auto)", "Techniker-Einschätzung (Begründung)", "Techniker-Einschätzung (Manuell)"]: if col not in df.columns: df[col] = "" # === STARTE BEI ERSTER LEERER ZEILE IN SPALTE 'Letzte Prüfung' (Spalte N) === sheet_values = sheet.get_all_values() 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() or str(v).lower() == 'nan'), len(filled_n) + 1) print(f"Starte bei Zeile {start+1} (erste leere Zeile in Spalte N)") # === ANZAHL ABFRAGEN ERMITTELN === try: limit = int(input("Wieviele Firmen sollen analysiert werden? (z.B. 1000): ").strip()) except: print("Ungültige Eingabe, verwende alle verbleibenden Firmen.") limit = len(df) - (start - 1) wikipedia.set_lang(LANG) # === SYSTEMPROMPT === SYSTEM_PROMPT = ( "Du bist ein Klassifizierungs-Experte für Unternehmensbranchen. " "Ordne jedes Unternehmen genau einer der folgenden Kategorien zu (nur eine):\n\n" "1. Hersteller / Produzenten > Maschinenbau\n" "2. Hersteller / Produzenten > Automobil\n" "3. Hersteller / Produzenten > Anlagenbau\n" "4. Hersteller / Produzenten > Medizintechnik\n" "5. Hersteller / Produzenten > Chemie & Pharma\n" "6. Hersteller / Produzenten > Elektrotechnik\n" "7. Hersteller / Produzenten > Lebensmittelproduktion\n" "8. Hersteller / Produzenten > IT / Telekommunikation\n" "9. Hersteller / Produzenten > Bürotechnik\n" "10. Hersteller / Produzenten > Automaten (Vending, Slot)\n" "11. Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima\n" "12. Hersteller / Produzenten > Gebäudetechnik Allgemein\n" "13. Hersteller / Produzenten > Schädlingsbekämpfung\n" "14. Hersteller / Produzenten > Fertigung\n" "15. Hersteller / Produzenten > Braune & Weiße Ware\n" "16. Versorger > Stadtwerk\n" "17. Versorger > Verteilnetzbetreiber\n" "18. Versorger > Telekommunikation\n" "19. Dienstleister > Messdienstleister\n" "20. Dienstleister > Facility Management\n" "21. Dienstleister > Healthcare/Pflegedienste\n" "22. Dienstleister > Servicedienstleister / Reparatur ohne Produktion\n" "23. Handel & Logistik > Auslieferdienste\n" "24. Handel & Logistik > Energie (Brennstoffe)\n" "25. Handel & Logistik > Großhandel\n" "26. Handel & Logistik > Einzelhandel\n" "27. Handel & Logistik > Logistik Sonstige\n" "28. Sonstige > Unternehmensberatung (old)\n" "29. Sonstige > Sonstige\n" "30. Sonstige > Agrar, Pellets (old)\n" "31. Sonstige > Sonstiger Service (old)\n" "32. Sonstige > IT Beratung\n" "33. Sonstige > Engineering\n" "34. Baubranche > Baustoffhandel\n" "35. Baubranche > Baustoffindustrie\n" "36. Baubranche > Logistiker Baustoffe\n" "37. Baubranche > Bauunternehmen\n" "38. Gutachter / Versicherungen > Versicherungsgutachten\n" "39. Gutachter / Versicherungen > Technische Gutachter\n" "40. Gutachter / Versicherungen > Medizinische Gutachten\n\n" "Antwortformat: Wikipedia-Branche; LinkedIn-Branche; Umsatz (Mio €); Empfohlene Neueinstufung; Begründung; FSM-Relevanz; Techniker-Einschätzung (Auto); Techniker-Einschätzung (Begründung)" ) system_prompt = {"role": "system", "content": SYSTEM_PROMPT} # === WIKIPEDIA LOOKUP === def get_wikipedia_data(firmenname): suchbegriffe = [firmenname.strip(), " ".join(firmenname.split()[:2])] for suchbegriff in suchbegriffe: try: page = wikipedia.page(suchbegriff, auto_suggest=False) url = page.url html = requests.get(url).text soup = BeautifulSoup(html, 'html.parser') infobox = soup.find("table", {"class": "infobox"}) branche = "" umsatz = "" if infobox: for row in infobox.find_all("tr"): header = row.find("th") data = row.find("td") if not header or not data: continue if "Branche" in header.text: branche = data.text.strip() if "Umsatz" in header.text: umsatz = data.text.strip() if not branche: cats = page.categories branche = cats[0] if cats else "" return url, branche, umsatz except: continue return "", "", "" # === KLASSIFIZIERUNG === def classify_company(row): content = ( f"Beschreibung: {row['Beschreibung des Unternehmens'] or ''}\n" f"Einstufung: {row['Aktuelle Einstufung'] or ''}\n" f"Website: {row['Website'] or ''}" ) try: resp = openai.chat.completions.create( model="gpt-4", messages=[system_prompt, {"role": "user", "content": content}], temperature=0 ) result = resp.choices[0].message.content.strip() parts = [v.strip().strip('"') if v.strip() else "k.A." for v in result.split(";", 7)] while len(parts) < 8: parts.append("k.A.") return parts except Exception as e: print(f"⚠️ Fehler bei Zeile: {row['Firmenname']} → {e}") return ["k.A."] * 8 # === LOOP === count = 0 for df_idx in range(start - 1, len(df)): if count >= limit: break row = df.iloc[df_idx] if str(row.get("Letzte Prüfung", "")).strip(): continue print(f"[{time.strftime('%H:%M:%S')}] Verarbeite Zeile {df_idx+1}: {row['Firmenname']}") count += 1 url, wiki_branche, umsatz = get_wikipedia_data(row['Firmenname']) df.at[df_idx, "Wikipedia-URL"] = url or "k.A." df.at[df_idx, "Wikipedia-Branche"] = wiki_branche.strip('"') or "k.A." if not df.at[df_idx, "Umsatz (Mio €)"]: df.at[df_idx, "Umsatz (Mio €)"] = umsatz or "k.A." wiki, linkedin, umsatz_chat, new_cat, reason, fsm_relevant, techniker, techniker_reason = classify_company(row) df.at[df_idx, "Wikipedia-Branche"] = wiki or wiki_branche or "k.A." df.at[df_idx, "LinkedIn-Branche"] = linkedin or "k.A." if not df.at[df_idx, "Umsatz (Mio €)"] or df.at[df_idx, "Umsatz (Mio €)"] == "k.A.": df.at[df_idx, "Umsatz (Mio €)"] = umsatz_chat or "k.A." df.at[df_idx, "Empfohlene Neueinstufung"] = new_cat or "k.A." current_cat = str(row.get("Aktuelle Einstufung") or "").strip().strip('"') if new_cat != current_cat: df.at[df_idx, "Begründung Neueinstufung"] = reason or "k.A." else: df.at[df_idx, "Begründung Neueinstufung"] = "" df.at[df_idx, "FSM-Relevanz"] = fsm_relevant or "k.A." df.at[df_idx, "Techniker-Einschätzung (Auto)"] = techniker or "k.A." df.at[df_idx, "Techniker-Einschätzung (Begründung)"] = techniker_reason or "k.A." now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") df.at[df_idx, "Letzte Prüfung"] = now sheet.update( values=[df.loc[df_idx, [ "Wikipedia-Branche", "LinkedIn-Branche", "Umsatz (Mio €)", "Empfohlene Neueinstufung", "Begründung Neueinstufung", "FSM-Relevanz", "Wikipedia-URL", "Letzte Prüfung", "Techniker-Einschätzung (Auto)", "Techniker-Einschätzung (Begründung)" ]].tolist()], range_name=f"G{df_idx+2}:Q{df_idx+2}" ) time.sleep(5) print("✅ Fertig!")