bugfix
This commit is contained in:
@@ -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}")
|
||||||
|
|||||||
Reference in New Issue
Block a user