diff --git a/brancheneinstufung.py b/brancheneinstufung.py index 1a99dd5d..babc8d29 100644 --- a/brancheneinstufung.py +++ b/brancheneinstufung.py @@ -1537,41 +1537,52 @@ def process_contacts(): # Weitere Verarbeitung der Kontakte folgt hier ... # ==================== LINKEDIN HELPER ==================== -def search_linkedin_contact(company_name, website, position_query, crm_kurzform): +def search_linkedin_contacts(company_name, website, position_query, crm_kurzform, num_results=100): """ - Sucht über SERPAPI einen einzelnen LinkedIn-Kontakt basierend auf der Positionsbezeichnung und der CRM-Kurzform des Unternehmens. - Es wird nur ein Treffer zurückgegeben, wenn der Titel auch die CRM-Kurzform (als Teilstring) enthält. + Sucht über SERPAPI mehrere LinkedIn-Kontakte basierend auf der Positionsbezeichnung + und der CRM-Kurzform des Unternehmens. Es werden alle Treffer zurückgegeben, bei denen + die CRM-Kurzform (als Teilstring) im Titel auftaucht. Args: company_name (str): Der Firmenname. website (str): Die Website des Unternehmens. position_query (str): Die zu suchende Positionsbezeichnung (z. B. "Serviceleiter"). crm_kurzform (str): Die manuell gepflegte Kurzform des Firmennamens. + num_results (int): Anzahl der abzurufenden Suchergebnisse (hier standardmäßig 100). Returns: - dict oder None: Ein Dictionary mit den Kontaktdaten (Vorname, Nachname, Position, LinkedInURL) oder None, falls kein passender Kontakt gefunden wurde. + list: Eine Liste von Dictionaries mit den Kontaktdaten (Vorname, Nachname, Position, LinkedInURL) + oder eine leere Liste, wenn keine Treffer gefunden wurden. """ try: with open("serpApiKey.txt", "r") as f: serp_key = f.read().strip() except Exception as e: debug_print("Fehler beim Lesen des SerpAPI-Schlüssels: " + str(e)) - return None - + return [] + query = f'site:linkedin.com/in "{position_query}" "{company_name}"' params = { "engine": "google", "q": query, "api_key": serp_key, - "hl": "de" + "hl": "de", + "num": num_results } + + # Logge die vollständige Such-URL + request_url = "https://serpapi.com/search?" + urlencode(params) + debug_print(f"Such-URL: {request_url}") + try: response = requests.get("https://serpapi.com/search", params=params, timeout=10) data = response.json() + contacts = [] if "organic_results" in data and len(data["organic_results"]) > 0: for result in data["organic_results"]: title = result.get("title", "") if crm_kurzform.lower() in title.lower(): + # Aufteilen des Titels in Namens- und Positionsbestandteile if "–" in title: parts = title.split("–") elif "-" in title: @@ -1589,22 +1600,22 @@ def search_linkedin_contact(company_name, website, position_query, crm_kurzform) lastname = "" linkedin_url = result.get("link", "") debug_print(f"Gefundener Kontakt: {firstname} {lastname}, Position: {pos_part}") - return { + contacts.append({ "Firmenname": company_name, "Website": website, "Vorname": firstname, "Nachname": lastname, "Position": pos_part, "LinkedInURL": linkedin_url - } - debug_print("Kein Treffer mit CRM-Kurzform in Titel gefunden.") - return None + }) + if not contacts: + debug_print("Kein Treffer mit CRM-Kurzform in Titel gefunden.") else: debug_print("Keine organic_results für Query gefunden.") - return None + return contacts except Exception as e: - debug_print(f"Fehler bei der SerpAPI-Suche: {e}") - return None + debug_print(f"Fehler bei der SERPAPI-Suche: {e}") + return [] def count_linkedin_contacts(company_name, website, position_query, crm_kurzform): """ @@ -1772,12 +1783,9 @@ def count_linkedin_contacts(company_name, website, position_query, crm_kurzform) def process_contact_research(): """ - Sucht mithilfe der SerpAPI Kontakte für bestimmte Positionen für jedes Unternehmen. - Es werden zunächst die CRM-Daten (insbesondere CRM Kurzform in Spalte C) sowie Firma und Website aus dem Hauptblatt gelesen. - Die gefundenen Kontakte, welche den Filter (CRM Kurzform muss im Titel enthalten sein) erfüllen, werden im Kontakte-Blatt eingetragen. - - Zusätzlich werden die Trefferzahlen (als Summen pro Position) in das Hauptblatt in den Spalten AI, AJ, AK, AL geschrieben - und ein Timestamp in Spalte AM gesetzt. + Sucht mithilfe der SERPAPI Kontakte für bestimmte Positionen für jedes Unternehmen. + Die gefundenen Kontakte werden im Kontakte-Blatt eingetragen – pro Kategorie werden alle + Treffer (die den Filter (CRM-Kurzform muss im Titel enthalten sein) erfüllen) verarbeitet. Im Kontakte-Blatt wird folgende Spaltenstruktur verwendet: A: Firmenname @@ -1791,8 +1799,6 @@ def process_contact_research(): I: E-Mail-Adresse J: LinkedIn-Link K: Timestamp - - Detaillierte Debug-Ausgaben sorgen für Transparenz bei der Ausführung. """ debug_print("Starte Contact Research (Modus 6)...") # Verbinde zum Hauptblatt @@ -1802,20 +1808,20 @@ def process_contact_research(): main_sheet = sh.sheet1 data = main_sheet.get_all_values() - # Ermittle die letzte Zeile in Spalte AM (Spalte 39), in der ein Timestamp eingetragen wurde - col_am = main_sheet.col_values(39) # Spalte AM hat den Index 39 (A=1, ..., AM=39) - last_filled_row = 1 # Header-Zeile + # Ermittle die letzte Zeile in Spalte AM (Spalte 39) mit einem Timestamp + col_am = main_sheet.col_values(39) + last_filled_row = 1 for idx, cell in enumerate(col_am): if cell.strip() != "": last_filled_row = idx + 1 start_row = last_filled_row + 1 - debug_print(f"Letzter Timestamp in Spalte AM wurde in Zeile {last_filled_row} gefunden. Starte Verarbeitung ab Zeile {start_row}.") + debug_print(f"Letzter Timestamp in Spalte AM in Zeile {last_filled_row}. Starte Verarbeitung ab Zeile {start_row}.") if start_row > len(data): - debug_print("Keine neuen Zeilen zu verarbeiten, da Timestamp in Spalte AM bereits bis zum Ende vorhanden ist.") + debug_print("Keine neuen Zeilen zu verarbeiten, da Timestamp in Spalte AM bis zum Ende vorhanden ist.") return - # Kontakte-Blatt öffnen oder erstellen + # Kontakte-Blatt öffnen oder erstellen (Header: A-K) try: contacts_sheet = sh.worksheet("Contacts") except gspread.exceptions.WorksheetNotFound: @@ -1825,7 +1831,7 @@ def process_contact_research(): contacts_sheet.update(values=[header], range_name="A1:K1") debug_print("Neues Blatt 'Contacts' erstellt und Header eingetragen.") - # Verarbeite jede Zeile ab der ermittelten Startzeile + # Gehe alle Zeilen im Hauptblatt ab der Startzeile durch for i in range(start_row, len(data) + 1): row = data[i - 1] company_name = row[1] if len(row) > 1 else "" @@ -1840,16 +1846,16 @@ def process_contact_research(): for pos in positions: count = count_linkedin_contacts(crm_kurzform, website, pos, crm_kurzform) contact_counts[pos] = count - contact = search_linkedin_contact(crm_kurzform, website, pos, crm_kurzform) - if contact: + # Abfrage: Es sollen nun alle Treffer (bis zu 100) verarbeitet werden + contacts = search_linkedin_contacts(crm_kurzform, website, pos, crm_kurzform, num_results=100) + for contact in contacts: firstname = contact.get("Vorname", "") lastname = contact.get("Nachname", "") gender_value = get_gender(firstname) if firstname else "unknown" email = get_email_address(firstname, lastname, website) timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - # Neue Reihenfolge: - # A: Firmenname, B: CRM Kurzform, C: Website, D: Geschlecht, E: Vorname, F: Nachname, - # G: Position, H: Suchbegriffskategorie, I: E-Mail-Adresse, J: LinkedIn-Link, K: Timestamp + # Spaltenanordnung: A: Firmenname, B: CRM Kurzform, C: Website, D: Geschlecht, E: Vorname, + # F: Nachname, G: Position, H: Suchbegriffskategorie, I: E-Mail-Adresse, J: LinkedIn-Link, K: Timestamp contact_row = [ company_name, crm_kurzform, @@ -1865,13 +1871,10 @@ def process_contact_research(): ] try: contacts_sheet.append_row(contact_row) - debug_print(f"Zeile {i}: Kontakt für Position '{pos}' in Contacts gespeichert: {contact_row}") + debug_print(f"Zeile {i}: Kontakt für '{pos}' gespeichert: {contact_row}") except Exception as e: - debug_print(f"Zeile {i}: Fehler beim Speichern des Kontakts in Contacts: {e}") - else: - debug_print(f"Zeile {i}: Kein passender Kontakt für Position '{pos}' gefunden.") - - # Aktualisiere Hauptblatt-Zeile mit Trefferzahlen und Timestamp + debug_print(f"Zeile {i}: Fehler beim Speichern des Kontakts für '{pos}': {e}") + # Aktualisiere Trefferzahlen und Timestamp im Hauptblatt try: main_sheet.update(values=[[str(contact_counts.get("Serviceleiter", 0))]], range_name=f"AI{i}") main_sheet.update(values=[[str(contact_counts.get("IT-Leiter", 0))]], range_name=f"AJ{i}")