komplett neue Version mit optimiertem Prompt
This commit is contained in:
@@ -1,212 +1,164 @@
|
|||||||
import os
|
# Neue Version mit Token-Optimierung, festem Prompt und Begrenzung der Durchläufe
|
||||||
import time
|
|
||||||
import pandas as pd
|
import os
|
||||||
import gspread
|
import time
|
||||||
import openai
|
import pandas as pd
|
||||||
import wikipedia
|
import gspread
|
||||||
from bs4 import BeautifulSoup
|
import openai
|
||||||
import requests
|
import wikipedia
|
||||||
from oauth2client.service_account import ServiceAccountCredentials
|
from bs4 import BeautifulSoup
|
||||||
from datetime import datetime
|
import requests
|
||||||
|
from oauth2client.service_account import ServiceAccountCredentials
|
||||||
# === CONFIG ===
|
from datetime import datetime
|
||||||
EXCEL = "Bestandsfirmen.xlsx"
|
|
||||||
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
# === KONFIGURATION ===
|
||||||
CREDENTIALS = "service_account.json"
|
EXCEL = "Bestandsfirmen.xlsx" # optional, falls du später exportieren willst
|
||||||
CHUNK = 10
|
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
||||||
LANG = "de"
|
CREDENTIALS = "service_account.json"
|
||||||
|
LANG = "de"
|
||||||
# === AUTHENTICATION ===
|
DURCHLÄUFE = int(input("Wieviele Zeilen sollen überprüft werden? "))
|
||||||
scope = ["https://www.googleapis.com/auth/spreadsheets"]
|
|
||||||
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope)
|
# === GOOGLE SHEET VERBINDUNG ===
|
||||||
sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1
|
scope = ["https://www.googleapis.com/auth/spreadsheets"]
|
||||||
|
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope)
|
||||||
# OpenAI API-Key aus externer Datei laden
|
sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1
|
||||||
with open("api_key.txt", "r") as f:
|
sheet_values = sheet.get_all_values()
|
||||||
openai.api_key = f.read().strip()
|
|
||||||
|
# === WIKIPEDIA KONFIG ===
|
||||||
# === LOAD DATA ===
|
wikipedia.set_lang(LANG)
|
||||||
df = pd.read_excel(EXCEL)
|
|
||||||
for col in ["Wikipedia-URL", "Wikipedia-Branche", "LinkedIn-Branche", "Umsatz (Mio €)",
|
# === SYSTEM PROMPT ===
|
||||||
"Empfohlene Neueinstufung", "Begründung Neueinstufung", "FSM-Relevanz", "Letzte Prüfung",
|
branches = [
|
||||||
"Techniker-Einschätzung (Auto)", "Techniker-Einschätzung (Begründung)", "Techniker-Einschätzung (Manuell)"]:
|
"Hersteller / Produzenten > Maschinenbau",
|
||||||
if col not in df.columns:
|
"Hersteller / Produzenten > Automobil",
|
||||||
df[col] = ""
|
"Hersteller / Produzenten > Anlagenbau",
|
||||||
|
"Hersteller / Produzenten > Medizintechnik",
|
||||||
# === STARTE BEI ERSTER LEERER ZEILE IN SPALTE 'Letzte Prüfung' (Spalte N) ===
|
"Hersteller / Produzenten > Chemie & Pharma",
|
||||||
sheet_values = sheet.get_all_values()
|
"Hersteller / Produzenten > Elektrotechnik",
|
||||||
filled_n = [row[13] if len(row) > 13 else '' for row in sheet_values[1:]]
|
"Hersteller / Produzenten > Lebensmittelproduktion",
|
||||||
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)
|
"Hersteller / Produzenten > IT / Telekommunikation",
|
||||||
print(f"Starte bei Zeile {start+1} (erste leere Zeile in Spalte N)")
|
"Hersteller / Produzenten > Bürotechnik",
|
||||||
|
"Hersteller / Produzenten > Automaten (Vending, Slot)",
|
||||||
mapping_dict = {}
|
"Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima",
|
||||||
wikipedia.set_lang(LANG)
|
"Hersteller / Produzenten > Gebäudetechnik Allgemein",
|
||||||
|
"Hersteller / Produzenten > Schädlingsbekämpfung",
|
||||||
# === ÜBERSETZUNGSTABELLE VORBEREITEN ===
|
"Hersteller / Produzenten > Fertigung",
|
||||||
sheet_trans_title = "Branchen-Mapping"
|
"Hersteller / Produzenten > Braune & Weiße Ware",
|
||||||
try:
|
"Versorger > Stadtwerk",
|
||||||
sheet_trans = sheet.spreadsheet.worksheet(sheet_trans_title)
|
"Versorger > Verteilnetzbetreiber",
|
||||||
except gspread.exceptions.WorksheetNotFound:
|
"Versorger > Telekommunikation",
|
||||||
sheet_trans = sheet.spreadsheet.add_worksheet(title=sheet_trans_title, rows="100", cols="3")
|
"Dienstleister > Messdienstleister",
|
||||||
sheet_trans.clear()
|
"Dienstleister > Facility Management",
|
||||||
sheet_trans.update(range_name="A1:B1", values=[["Wikipedia-Branche", "Ziel-Branchenschema"]])
|
"Dienstleister > Healthcare/Pflegedienste",
|
||||||
|
"Dienstleister > Servicedienstleister / Reparatur ohne Produktion",
|
||||||
# === BRANCHENSCHEMA ===
|
"Handel & Logistik > Auslieferdienste",
|
||||||
branches = [
|
"Handel & Logistik > Energie (Brennstoffe)",
|
||||||
"Hersteller / Produzenten > Maschinenbau",
|
"Handel & Logistik > Großhandel",
|
||||||
"Hersteller / Produzenten > Automobil",
|
"Handel & Logistik > Einzelhandel",
|
||||||
"Hersteller / Produzenten > Anlagenbau",
|
"Handel & Logistik > Logistik Sonstige",
|
||||||
"Hersteller / Produzenten > Medizintechnik",
|
"Sonstige > Unternehmensberatung (old)",
|
||||||
"Hersteller / Produzenten > Chemie & Pharma",
|
"Sonstige > Sonstige",
|
||||||
"Hersteller / Produzenten > Elektrotechnik",
|
"Sonstige > Agrar, Pellets (old)",
|
||||||
"Hersteller / Produzenten > Lebensmittelproduktion",
|
"Sonstige > Sonstiger Service (old)",
|
||||||
"Hersteller / Produzenten > IT / Telekommunikation",
|
"Sonstige > IT Beratung",
|
||||||
"Hersteller / Produzenten > Bürotechnik",
|
"Sonstige > Engineering",
|
||||||
"Hersteller / Produzenten > Automaten (Vending, Slot)",
|
"Baubranche > Baustoffhandel",
|
||||||
"Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima",
|
"Baubranche > Baustoffindustrie",
|
||||||
"Hersteller / Produzenten > Gebäudetechnik Allgemein",
|
"Baubranche > Logistiker Baustoffe",
|
||||||
"Hersteller / Produzenten > Schädlingsbekämpfung",
|
"Baubranche > Bauunternehmen",
|
||||||
"Hersteller / Produzenten > Fertigung",
|
"Gutachter / Versicherungen > Versicherungsgutachten",
|
||||||
"Hersteller / Produzenten > Braune & Weiße Ware",
|
"Gutachter / Versicherungen > Technische Gutachter",
|
||||||
"Versorger > Stadtwerk",
|
"Gutachter / Versicherungen > Medizinische Gutachten"
|
||||||
"Versorger > Verteilnetzbetreiber",
|
]
|
||||||
"Versorger > Telekommunikation",
|
|
||||||
"Dienstleister > Messdienstleister",
|
system_prompt = {
|
||||||
"Dienstleister > Facility Management",
|
"role": "system",
|
||||||
"Dienstleister > Healthcare/Pflegedienste",
|
"content": (
|
||||||
"Dienstleister > Servicedienstleister / Reparatur ohne Produktion",
|
"Du bist ein Experte für Brancheneinstufung und FSM-Potenzialbewertung. "
|
||||||
"Handel & Logistik > Auslieferdienste",
|
"FSM steht für Field Service Management – Software zur Planung und Unterstützung mobiler Techniker. "
|
||||||
"Handel & Logistik > Energie (Brennstoffe)",
|
"Ziel ist es, Unternehmen zu identifizieren, die mehr als 50 Techniker im Außeneinsatz beschäftigen (z. B. Servicetechniker, Instandhalter, Medizintechniker etc.).\n\n"
|
||||||
"Handel & Logistik > Großhandel",
|
"Dir liegt pro Unternehmen eine strukturierte Eingabezeile vor, bestehend aus:\n"
|
||||||
"Handel & Logistik > Einzelhandel",
|
"Firmenname; Website; Ort; Aktuelle Einstufung; Beschreibung der Branche Extern\n\n"
|
||||||
"Handel & Logistik > Logistik Sonstige",
|
"Bitte führe für jede Firma eine fundierte Bewertung durch:\n"
|
||||||
"Sonstige > Unternehmensberatung (old)",
|
"- Nimm eine Brancheneinstufung anhand des untenstehenden Ziel-Branchenschemas vor.\n"
|
||||||
"Sonstige > Sonstige",
|
"- Berücksichtige dabei alle vorliegenden Informationen (auch externe Beschreibung, Wikipedia, LinkedIn, Website) sowie die bisherige Einstufung.\n"
|
||||||
"Sonstige > Agrar, Pellets (old)",
|
"- Wenn die bisherige Einstufung korrekt ist, bestätige sie – wenn nicht, schlage eine neue Einstufung vor und begründe diese.\n"
|
||||||
"Sonstige > Sonstiger Service (old)",
|
"- Gib zusätzlich an, ob das Unternehmen FSM-relevant ist (Ja / Nein / k.A. mit Begründung).\n"
|
||||||
"Sonstige > IT Beratung",
|
"- Schätze die Anzahl mobiler Techniker anhand öffentlich verfügbarer Infos und gib eine Stufe an: <50 / >50 / >100 / >500, mit Begründung.\n\n"
|
||||||
"Sonstige > Engineering",
|
"Ziel-Branchenschema:\n" + "\n".join(branches)
|
||||||
"Baubranche > Baustoffhandel",
|
)
|
||||||
"Baubranche > Baustoffindustrie",
|
}
|
||||||
"Baubranche > Logistiker Baustoffe",
|
|
||||||
"Baubranche > Bauunternehmen",
|
# === HILFSFUNKTIONEN ===
|
||||||
"Gutachter / Versicherungen > Versicherungsgutachten",
|
def get_wikipedia_data(name):
|
||||||
"Gutachter / Versicherungen > Technische Gutachter",
|
for suchbegriff in [name.strip(), " ".join(name.split()[:2])]:
|
||||||
"Gutachter / Versicherungen > Medizinische Gutachten"
|
try:
|
||||||
]
|
page = wikipedia.page(suchbegriff, auto_suggest=False)
|
||||||
|
url = page.url
|
||||||
system_prompt = {
|
html = requests.get(url).text
|
||||||
"role": "system",
|
soup = BeautifulSoup(html, 'html.parser')
|
||||||
"content": (
|
infobox = soup.find("table", {"class": "infobox"})
|
||||||
"Du bist ein Experte für Brancheneinstufung und FSM-Potenzialbewertung. Nutze das folgende Ziel‑Branchenschema als Referenz:\n\n"
|
branche = umsatz = ""
|
||||||
+ "\n".join(branches)
|
if infobox:
|
||||||
)
|
for row in infobox.find_all("tr"):
|
||||||
}
|
th, td = row.find("th"), row.find("td")
|
||||||
|
if not th or not td:
|
||||||
# === WIKIPEDIA LOOKUP ===
|
continue
|
||||||
def get_wikipedia_data(firmenname):
|
if "Branche" in th.text:
|
||||||
suchbegriffe = [firmenname.strip(), " ".join(firmenname.split()[:2])]
|
branche = td.text.strip()
|
||||||
for suchbegriff in suchbegriffe:
|
if "Umsatz" in th.text:
|
||||||
try:
|
umsatz = td.text.strip()
|
||||||
page = wikipedia.page(suchbegriff, auto_suggest=False)
|
if not branche:
|
||||||
url = page.url
|
cats = page.categories
|
||||||
html = requests.get(url).text
|
branche = cats[0] if cats else ""
|
||||||
soup = BeautifulSoup(html, 'html.parser')
|
return url, branche, umsatz
|
||||||
infobox = soup.find("table", {"class": "infobox"})
|
except:
|
||||||
branche = ""
|
continue
|
||||||
umsatz = ""
|
return "", "", ""
|
||||||
if infobox:
|
|
||||||
for row in infobox.find_all("tr"):
|
def classify_company(row):
|
||||||
header = row.find("th")
|
user_prompt = {
|
||||||
data = row.find("td")
|
"role": "user",
|
||||||
if not header or not data:
|
"content": f"{row[0]};{row[1]};{row[2]};{row[4]};{row[5]}"
|
||||||
continue
|
}
|
||||||
if "Branche" in header.text:
|
response = openai.chat.completions.create(
|
||||||
branche = data.text.strip()
|
model="gpt-3.5-turbo",
|
||||||
if "Umsatz" in header.text:
|
messages=[system_prompt, user_prompt],
|
||||||
umsatz = data.text.strip()
|
temperature=0
|
||||||
if not branche:
|
)
|
||||||
cats = page.categories
|
parts = [v.strip().strip('"') for v in response.choices[0].message.content.strip().split(";", 7)]
|
||||||
branche = cats[0] if cats else ""
|
while len(parts) < 8:
|
||||||
return url, branche, umsatz
|
parts.append("k.A.")
|
||||||
except:
|
return parts
|
||||||
continue
|
|
||||||
return "", "", ""
|
# === STARTINDEX SUCHEN ===
|
||||||
|
filled = [row[11] if len(row) > 11 else '' for row in sheet_values[1:]]
|
||||||
# === KLASSIFIZIERUNG ===
|
start = next((i + 1 for i, v in enumerate(filled, start=1) if not v.strip()), len(filled) + 1)
|
||||||
def classify_company(row):
|
print(f"Starte bei Zeile {start+1}")
|
||||||
user_prompt = {
|
|
||||||
"role": "user",
|
# === VERARBEITUNG ===
|
||||||
"content": (
|
for i in range(start, min(start + DURCHLÄUFE, len(sheet_values))):
|
||||||
"Bitte prüfe die vorliegenden Informationen zum Unternehmen. Gib die Antwort im CSV-Format zurück:\n"
|
row = sheet_values[i]
|
||||||
"Wikipedia-Branche; LinkedIn-Branche; Umsatz (Mio €); Empfohlene Neueinstufung; Begründung; FSM-Relevanz (Ja/Nein/k.A. mit Begründung); Techniker-Einschätzung (<50/>50/>100/>500); Techniker-Begründung\n\n"
|
print(f"[{time.strftime('%H:%M:%S')}] Verarbeite Zeile {i+1}: {row[0]}")
|
||||||
f"Beschreibung: {row['Beschreibung des Unternehmens'] or ''}\n"
|
|
||||||
f"Aktuelle Einstufung: {row['Aktuelle Einstufung'] or ''}\n"
|
url, wiki_branche, umsatz = get_wikipedia_data(row[0])
|
||||||
f"Externe Branchenbeschreibung: {row['Beschreibung der Branche Extern'] or ''}\n"
|
wiki, linkedin, umsatz_chat, new_cat, reason, fsm, techniker, techniker_reason = classify_company(row)
|
||||||
f"Website: {row['Website'] or ''}"
|
|
||||||
)
|
values = [
|
||||||
}
|
wiki or wiki_branche,
|
||||||
resp = openai.chat.completions.create(
|
linkedin,
|
||||||
model="gpt-4",
|
umsatz_chat or umsatz,
|
||||||
messages=[system_prompt, user_prompt],
|
new_cat,
|
||||||
temperature=0
|
reason,
|
||||||
)
|
fsm,
|
||||||
result = resp.choices[0].message.content.strip()
|
url,
|
||||||
parts = [v.strip().strip('"') for v in result.split(";", 7)]
|
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
while len(parts) < 8:
|
techniker,
|
||||||
parts.append("k.A.")
|
techniker_reason
|
||||||
return parts
|
]
|
||||||
|
|
||||||
# === LOOP ===
|
# Schreibe in die Spalten G bis P (7–16, nullbasiert also 6–15)
|
||||||
for df_idx in range(start - 1, len(df)):
|
sheet.update(range_name=f"G{i+2}:P{i+2}", values=[values])
|
||||||
row = df.iloc[df_idx]
|
time.sleep(5)
|
||||||
if str(row.get("Letzte Prüfung", "")).strip():
|
|
||||||
continue
|
print("✅ Durchläufe abgeschlossen")
|
||||||
|
|
||||||
print(f"[{time.strftime('%H:%M:%S')}] Verarbeite Zeile {df_idx+1}: {row['Firmenname']}")
|
|
||||||
|
|
||||||
url, wiki_branche, umsatz = get_wikipedia_data(row['Firmenname'])
|
|
||||||
df.at[df_idx, "Wikipedia-URL"] = url
|
|
||||||
df.at[df_idx, "Wikipedia-Branche"] = wiki_branche.strip('"')
|
|
||||||
if not df.at[df_idx, "Umsatz (Mio €)"]:
|
|
||||||
df.at[df_idx, "Umsatz (Mio €)"] = umsatz
|
|
||||||
|
|
||||||
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
|
|
||||||
df.at[df_idx, "LinkedIn-Branche"] = linkedin
|
|
||||||
if not df.at[df_idx, "Umsatz (Mio €)"]:
|
|
||||||
df.at[df_idx, "Umsatz (Mio €)"] = umsatz_chat
|
|
||||||
df.at[df_idx, "Empfohlene Neueinstufung"] = new_cat
|
|
||||||
|
|
||||||
current_cat = str(row.get("Aktuelle Einstufung") or "").strip().strip('"')
|
|
||||||
if new_cat != current_cat:
|
|
||||||
df.at[df_idx, "Begründung Neueinstufung"] = reason
|
|
||||||
else:
|
|
||||||
df.at[df_idx, "Begründung Neueinstufung"] = ""
|
|
||||||
|
|
||||||
df.at[df_idx, "FSM-Relevanz"] = fsm_relevant
|
|
||||||
df.at[df_idx, "Techniker-Einschätzung (Auto)"] = techniker
|
|
||||||
df.at[df_idx, "Techniker-Einschätzung (Begründung)"] = techniker_reason
|
|
||||||
|
|
||||||
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
||||||
df.at[df_idx, "Letzte Prüfung"] = now
|
|
||||||
|
|
||||||
key = df.at[df_idx, "Wikipedia-Branche"]
|
|
||||||
val = df.at[df_idx, "Empfohlene Neueinstufung"]
|
|
||||||
if key and val and key not in mapping_dict:
|
|
||||||
mapping_dict[key] = val
|
|
||||||
sheet_trans.update(range_name=f"A{len(mapping_dict)+1}:B{len(mapping_dict)+1}", values=[[key, val]])
|
|
||||||
|
|
||||||
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!")
|
|
||||||
|
|||||||
Reference in New Issue
Block a user