Rechtlicher Rahmen: Was Sie wissen müssen, bevor Sie beginnen
Bevor wir uns den technischen Aspekten widmen, ist ein wichtiger Hinweis notwendig: Dieser Artikel behandelt ausschließlich den Zugriff auf öffentlich zugängliche Daten auf LinkedIn. Wir geben keine Rechtsberatung — konsultieren Sie unbedingt einen qualifizierten Juristen, bevor Sie ein Scraping-Projekt starten.
Der Fall hiQ Labs v. LinkedIn hat wichtige Präzedenzen geschaffen. hiQ Labs, ein Datenanalyse-Unternehmen, scrapte öffentliche LinkedIn-Profile, um KI-gestützte Talent-Analysen anzubieten. LinkedIn untersagte dies mit einer Abmahnung und technischen Blockaden. hiQ verklagte LinkedIn und argumentierte, dass öffentlich zugängliche Daten nicht unter den Computer Fraud and Abuse Act (CFAA) fallen.
Kernfrage: Darf ein Unternehmen den Zugriff auf öffentliche Daten auf seiner Website einschränken, oder verstoßen solche Einschränkungen gegen den Wettbewerb und die Informationsfreiheit?
2019 entschied ein Bundesgericht, dass hiQ wahrscheinlich Erfolg haben würde, da die CFAA nicht auf öffentlich zugängliche Daten anwendbar sei. LinkedIn legte Berufung ein. 2022 bestätigte ein Berufungsgericht weitgehend die Entscheidung der Vorinstanz. Der Supreme Court hat sich bisher nicht abschließend geäußert.
In der EU gelten zusätzlich die DSGVO und nationale Datenschutzgesetze. Selbst öffentlich zugängliche personenbezogene Daten dürfen nicht ohne Rechtsgrundlage verarbeitet werden. Ein berechtigtes Interesse muss sorgfältig abgewogen werden.
Was ist ohne Login öffentlich zugänglich?
LinkedIn unterscheidet strikt zwischen öffentlichen und nicht-öffentlichen Inhalten. Ohne Anmeldung sind folgende Daten typischerweise zugänglich:
- Öffentliche Profil-URLs: Ein eingeschränkter Ausschnitt eines Profils — Name, aktuelle Position, Unternehmen, manchmal Ausbildung und Standort.
- Öffentliche Unternehmensseiten: Basisinformationen zu Unternehmen, Mitarbeiterzahlen, Branche, Website.
- Öffentliche Stellenausschreibungen: Titel, Beschreibung, Standort, Unternehmen — oft vollständig einsehbar.
Was nicht öffentlich ist ohne Login:
- Vollständige Profil-Historien und Empfehlungen
- Kontaktdaten (außer explizit veröffentlicht)
- Private Aktivitäten, Nachrichten, Verbindungen
- Sales Navigator und Recruiter-Daten
Warum Residential Proxies unverzichtbar sind
LinkedIn betreibt eines der aggressivsten Anti-Scraping-Systeme im Web. Die Gründe sind nachvollziehbar: Datenschutz der Nutzer, Schutz vor Konkurrenzanalyse, und die Verhinderung von Datenmissbrauch.
Warum Datacenter-Proxies scheitern:
- LinkedIn pflegt umfangreiche IP-Reputation-Datenbanken
- Datacenter-IP-Blöcke werden oft präventiv blockiert
- Die Erkennungsrate für DC-Proxies liegt bei über 95%
- Selbst rotierende DC-Proxies werden schnell erkannt
Residential Proxies nutzen IP-Adressen echter Privathaushalte. Sie erscheinen für LinkedIn wie normale Nutzer, die über typische Heimanschlüsse zugreifen. Das bedeutet jedoch nicht, dass Sie unbegrenzt scrapen können — Sie müssen sich wie ein normaler Nutzer verhalten.
| Proxy-Typ | Erkennungsrisiko | Eignung für LinkedIn | Kosten |
|---|---|---|---|
| Datacenter | Sehr hoch (95%+) | Nicht empfohlen | Niedrig |
| Residential | Niedrig (bei korrekter Nutzung) | Empfohlen | Mittel-Hoch |
| Mobile/4G | Sehr niedrig | Optimal, aber teuer | Hoch |
| Kein Proxy | IP wird schnell blockiert | Nur für kleine Tests | Keine |
Python + Playwright: Ein robuster Ansatz
Playwright ist ideal für LinkedIn-Scraping, da es echte Browser automatisiert und JavaScript rendert. Hier ist ein durchdachtes Beispiel mit Residential Proxies und realistischem Browser-Verhalten:
import asyncio
import random
from playwright.async_api import async_playwright
# ProxyHat Residential Proxy Konfiguration
PROXY_CONFIG = {
"server": "http://gate.proxyhat.com:8080",
"username": "user-country-US", # US-IP für LinkedIn.com
"password": "YOUR_PASSWORD"
}
# Realistische User-Agent Rotation
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0"
]
async def scrape_profile(browser, profile_url: str) -> dict:
"""Scraped ein öffentliches LinkedIn-Profil."""
context = await browser.new_context(
user_agent=random.choice(USER_AGENTS),
viewport={"width": 1920, "height": 1080},
locale="en-US",
timezone_id="America/New_York",
proxy=PROXY_CONFIG
)
page = await context.new_page()
try:
# Realistische Verzögerung
await asyncio.sleep(random.uniform(2, 5))
await page.goto(profile_url, wait_until="networkidle", timeout=30000)
# Prüfen auf Blockierung
if "authwall" in page.url or "checkpoint" in page.url:
print(f"Blockiert: {profile_url}")
return None
# Warten auf Inhaltsladung
await page.wait_for_selector(".text-heading-xlarge", timeout=10000)
# Daten extrahieren
data = await page.evaluate("""() => {
const name = document.querySelector('.text-heading-xlarge')?.innerText || '';
const headline = document.querySelector('.text-body-medium')?.innerText || '';
const location = document.querySelector('.text-body-small.inline')?.innerText || '';
const company = document.querySelector('[aria-label="Current company"]')?.innerText || '';
return { name, headline, location, company };
}""")
return data
except Exception as e:
print(f"Fehler bei {profile_url}: {e}")
return None
finally:
await context.close()
async def main():
profiles = [
"https://www.linkedin.com/in/example-profile-1/",
"https://www.linkedin.com/in/example-profile-2/"
]
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
results = []
for url in profiles:
data = await scrape_profile(browser, url)
if data:
results.append(data)
# Wichtig: Pause zwischen Requests
await asyncio.sleep(random.uniform(8, 15))
await browser.close()
return results
if __name__ == "__main__":
results = asyncio.run(main())
print(results)Wichtige Aspekte dieses Codes:
- Browser-Fingerprinting: Vollständiger Browser-Kontext mit realistischen Eigenschaften
- Rate Limiting: 8-15 Sekunden Pause zwischen Requests — nicht unterschreiten
- Error Handling: Erkennung von Authwalls und Checkpoints
- Proxy-Rotation: Ändern Sie den Username für jede Session
Stellenausschreibungen scrapen: Spezifika
LinkedIn-Jobs sind oft vollständig öffentlich und weniger stark geschützt. Dennoch gelten strenge Rate-Limits.
import requests
from urllib.parse import quote
import time
import random
BASE_URL = "https://www.linkedin.com/jobs/search/"
def search_jobs(keywords: str, location: str, page: int = 0) -> dict:
"""Durchsucht LinkedIn-Stellenausschreibungen."""
params = {
"keywords": keywords,
"location": location,
"start": page * 25 # 25 Ergebnisse pro Seite
}
# ProxyHat Residential Proxy
proxies = {
"http": "http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080",
"https": "http://user-country-US:YOUR_PASSWORD@gate.proxyhat.com:8080"
}
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
"Accept-Language": "en-US,en;q=0.9",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
}
try:
response = requests.get(
BASE_URL,
params=params,
proxies=proxies,
headers=headers,
timeout=30
)
if response.status_code == 429:
print("Rate limit erreicht. Warte 60 Sekunden...")
time.sleep(60)
return search_jobs(keywords, location, page)
# Parse Job-Listings (vereinfacht)
# In der Praxis: BeautifulSoup oder Playwright für JS-Rendering
return {"status": response.status_code, "html": response.text[:500]}
except Exception as e:
print(f"Fehler: {e}")
return None
def scrape_jobs_paginated(keywords: str, location: str, max_pages: int = 5):
"""Scraped mehrere Seiten mit angemessenem Rate Limiting."""
results = []
for page in range(max_pages):
print(f"Scrape Seite {page + 1}...")
data = search_jobs(keywords, location, page)
if data:
results.append(data)
# Kritisch: Ausreichend warten
delay = random.uniform(10, 20)
time.sleep(delay)
return results
# Beispiel-Aufruf
if __name__ == "__main__":
jobs = scrape_jobs_paginated("Software Engineer", "Berlin", max_pages=3)
print(f"{len(jobs)} Seiten gescraped")Pagination und Filter
LinkedIn nutzt Offset-basierte Pagination mit 25 Ergebnissen pro Seite. Wichtige Filter-Parameter:
f_JT: Job-Typ (F, C, P, T, I, V für Full-time, Contract, etc.)f_E: Erfahrungsniveau (1, 2, 3 für Entry, Associate, Senior)f_WT: Arbeitsmodell (1=On-site, 2=Remote, 3=Hybrid)distance: Umkreis in km
Wann Sie NICHT scrapen sollten
Ethische und rechtliche Grenzen sind nicht verhandelbar. Hier sind klare „Red Lines“:
1. Private Daten
Niemals auf Daten zugreifen, die nur für eingeloggte Nutzer sichtbar sind:
- Private Nachrichten und InMail
- Eingeschränkte Profil-Details
- Verbindungen und Netzwerk-Informationen
- Empfehlungen, die als privat markiert sind
2. Login-geschützte Bereiche
Das Scraping nach einem Login (auch mit eigenen Credentials) ist besonders riskant:
- Verletzung der LinkedIn-Nutzungsbedingungen
- Potentielle CFAA-Haftung in den USA
- Konto-Sperrung und rechtliche Schritte
3. Sales Navigator und Recruiter
Diese Premium-Produkte sind explizit geschützt:
- Erweiterte Suchfilter und -ergebnisse
- InMail-Credits und Lead-Vorschläge
- Angepasste Listen und gespeicherte Suchen
Grundsatz: Wenn Sie sich einloggen müssen, um auf Daten zuzugreifen, sind diese nicht „öffentlich“ im Sinne der hiQ-Entscheidung. Überschreiten Sie diese Grenze nicht.
Offizielle Alternativen: LinkedIn APIs
Für viele Anwendungsfälle sind offizielle APIs die bessere Wahl — legal sicher, zuverlässig, und mit Support.
| API | Zielgruppe | Daten | Kosten |
|---|---|---|---|
| LinkedIn Marketing API | Werbetreibende | Anzeigen, Analytics, Lead Gen | Kostenlos (mit Werbekonto) |
| LinkedIn Recruiter System Connect | Recruiting-Tools | Kandidaten-Suche, InMail | Enterprise-Preise |
| LinkedIn Learning API | LMS-Integrationen | Kursdaten, Fortschritt | Enterprise |
| Profile API (begrenzt) | Partner | Eigene Profil-Daten | Partnership nötig |
Das LinkedIn Partner Program ist der offizielle Weg für Datenzugriff. Die Hürden sind hoch, aber für ernsthafte Unternehmen oft machbar.
Ethische Best Practices
Respektieren Sie robots.txt
LinkedIn's robots.txt erlaubt bestimmte Pfade nicht. Auch wenn sie rechtlich nicht bindend ist, signalisiert sie die Präferenzen der Plattform:
# Beispiel: Prüfen vor dem Scraping
import urllib.robotparser
rp = urllib.robotparser.RobotFileParser()
rp.set_url("https://www.linkedin.com/robots.txt")
rp.read()
can_fetch = rp.can_fetch("*", "https://www.linkedin.com/jobs/search/")
print(f"Darf scrapen: {can_fetch}") # Prüfen Sie das ErgebnisRate Limiting ist Pflicht
Aggressives Scraping schadet allen:
- Maximal 60-100 Requests pro Stunde und IP
- Vermeiden Sie Peak-Zeiten (US-Business-Stunden)
- Implementieren Sie exponentielles Backoff bei Fehlern
- Verteilen Sie Last über mehrere IPs und Zeiträume
Datenminimierung nach DSGVO
In der EU gilt das Prinzip der Datenminimierung:
- Nur sammeln, was Sie wirklich brauchen
- Personenbezogene Daten anonymisieren wo möglich
- Speicherfristen definieren und einhalten
- Datenschutz-Folgenabschätzung durchführen
Key Takeaways
- Rechtlicher Rahmen: Der hiQ-Fall zeigt, dass öffentliche Daten unter CFAA möglicherweise zugänglich sind — aber die Rechtslage bleibt unsicher, besonders außerhalb der USA.
- Nur öffentliche Daten: Beschränken Sie sich strikt auf Inhalte, die ohne Login sichtbar sind.
- Residential Proxies sind Pflicht: LinkedIn erkennt Datacenter-IPs fast immer. Nutzen Sie echte Residential IPs mit geografischer Verteilung.
- Rate Limiting schützt Sie: Verhalten Sie sich wie ein normaler Nutzer. Weniger ist mehr.
- Offizielle APIs bevorzugen: Wenn verfügbar und bezahlbar, sind Partner-APIs der sicherere Weg.
- Ethische Verantwortung: Denken Sie an den Datenschutz echter Personen hinter den Profilen.
Nächste Schritte
Für Ihre ersten Scraping-Experimente mit Residential Proxies besuchen Sie unseren ProxyHat Tarif-Vergleich. Unsere IPs aus über 195 Ländern eignen sich ideal für vorsichtige, verteilte Datenextraktion.
Weitere technische Details finden Sie in unserem Web Scraping Use Case Guide und im Artikel zu Proxy-Rotation.






