This commit is contained in:
2025-04-10 06:42:46 +00:00
parent e93d2615a8
commit fddc7539fd

View File

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