LinkedIn, dünyanın en büyük profesyonel ağ platformu olarak milyarlarca profil, şirket sayfası ve iş ilanına ev sahipliği yapıyor. Bu veri hazinesi, işe alım araçları geliştirenler, pazar araştırması ekipleri ve rekabet analizi yapanlar için büyük değer taşıyor. Ancak LinkedIn'den veri toplamak, hem teknik zorluklar hem de ciddi yasal ve etik sorumluluklar içeriyor.
Bu rehber, sadece kamuya açık verilere odaklanacak, özel verilere erişimin neden kabul edilemez olduğunu açıklayacak ve residential proxy kullanımının teknik gerekliliğini detaylandıracak. HiQ Labs v. LinkedIn davasını referans alarak mevcut yasal durumu tartışacağız — ancak bu yazı hukuki tavsiye niteliği taşımaz; her zaman kendi hukuk danışmanınıza başvurun.
Yasal Uyarı: CFAA ve Kamu Verisi Erişimi
Amerika Birleşik Devletleri'nde Bilgisayar Dolandırıcılığı ve Kötüye Kullanım Yasası (CFAA), yetkisiz bilgisayar erişimini suç sayıyor. Ancak hiQ Labs, Inc. v. LinkedIn Corporation davası (2017–2022), bu yasının kamuya açık verilere nasıl uygulandığına dair önemli bir emsal oluşturdu.
hiQ Labs v. LinkedIn özeti:
- hiQ Labs, LinkedIn'in kamuya açık profil verilerini toplayarak çalışan analitiği hizmeti sunuyordu
- LinkedIn, 2017'de hiQ'ya erişimi durdurmasını talep eden bir cease-and-desist mektubu gönderdi
- hiQ, LinkedIn'in bu engellemeyen CFAA'yı ihlal ettiğini iddia ederek dava açtı
- 9. Circuit Temyiz Mahkemesi (2019), kamuya açık verilerin toplanmasının CFAA kapsamında "yetkisiz erişim" sayılamayacağına hükmetti
- LinkedIn ABD Yüksek Mahkemesi'ne başvurdu, ancak Yüksek Mahkeme davayı reddetti (2022)
Önemli: Bu dava, kamuya açık verilere erişimin belirli koşullarda CFAA ihlali sayılmayabileceğini gösteriyor. Ancak her ülkenin kendi yasaları var — AB'de GDPR, veri işleme için meşru bir gerekçe talep ediyor. Türkiye'de KVKK benzer yükümlülükler getiriyor. Her zaman kendi yargı alanınıza uygun hukuki danışmanlık alın.
Kamuya Açık LinkedIn Verisi Nedir?
LinkedIn'de oturum açmadan (logged-out) erişilebilen veriler:
Kamu Profilleri
- URL formatı:
linkedin.com/in/{username} - Ad, soyad, profil fotoğrafı
- Mevcut pozisyon ve şirket
- Eğitim geçmişi (kısmen)
- Konum bilgisi
Erişilemeyen: Bağlantılar listesi, mesajlar, tam iş geçmişi, beceriler, onaylar, özel gizlilik ayarlı profiller.
Şirket Sayfaları
- URL formatı:
linkedin.com/company/{company-name} - Şirket adı, logo, endüstri
- Şirket boyutu, konum
- Açık pozisyon sayısı
İş İlanları
- URL formatı:
linkedin.com/jobs/view/{job-id} - Pozisyon başlığı, şirket, konum
- İş tanımı, gereklilikler
- Başvuru tarihi
Kritik ayrım: LinkedIn, oturum açmadan görüntülenen sayfalarda sınırlı veri sunuyor. "Giriş yaparak daha fazla görün" uyarıları, bu verilerin kamuya açık olmadığını gösterir.
Neden Residential Proxy Şart?
LinkedIn, bot tespiti konusunda dünyadaki en gelişmiş sistemlerden birine sahip. Datacenter IP'leri neredeyse anında tespit ediliyor ve engelleniyor. İşte residential proxy kullanımının teknik nedenleri:
1. IP Fingerprinting ve ASN Analizi
LinkedIn, IP bloklarının ASN (Autonomous System Number) kayıtlarını analiz ediyor. Datacenter ASN'leri (AWS, Google Cloud, DigitalOcean, vb.) otomatik olarak şüpheli işaretleniyor. Residential ASN'ler ise gerçek İSS'lere ait olduğu için meşru trafik gibi görünüyor.
2. Per-IP Rate Limiting
LinkedIn, tek bir IP'den gelen isteklerde agresif sınırlama uyguluyor:
- ~50–100 sayfa/saat oturum açmadan görüntüleme
- Her engel 24–72 saat sürebilir
- "429 Too Many Requests" veya CAPTCHA sayfası döner
3. Browser Fingerprinting
Sadece IP değil, tarayıcı parmak izi de kontrol ediliyor:
- Canvas fingerprint
- WebGL renderer
- Font listesi
- Ekran çözünürlüğü
- Timezone ve dil ayarları
Playwright veya Puppeteer kullanırken bu izleri minimize etmek için gerçekçi browser context'ler oluşturmak şart.
4. Behavioral Analysis
LinkedIn, insan davranışını taklit etmeyen istekleri tespit ediyor:
- Saniyede 10 sayfa istemek = bot
- Sayfada 0.1 saniye kalmak = bot
- Hiç kaydırma yapmamak = bot
Python + Playwright ile Güvenli Uygulama
Aşağıdaki örnek, residential proxy kullanarak LinkedIn kamu profillerini toplayan bir Playwright script'i içerir. ProxyHat residential proxy servisi kullanılmaktadır.
Kurulum
pip install playwright asyncio
playwright install chromium
Temel Script
import asyncio
import random
from playwright.async_api import async_playwright
class LinkedInScraper:
def __init__(self, proxy_user: str, proxy_pass: str):
self.proxy_user = proxy_user
self.proxy_pass = proxy_pass
# ProxyHat residential proxy - ülke bazlı hedefleme
self.proxy_url = f"http://{proxy_user}-country-US:{proxy_pass}@gate.proxyhat.com:8080"
async def create_browser_context(self, playwright):
"""Gerçekçi browser context oluşturur"""
browser = await playwright.chromium.launch(
proxy={"server": self.proxy_url},
headless=True
)
# Gerçekçi viewport ve user agent
context = await browser.new_context(
viewport={"width": 1920, "height": 1080},
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"
),
locale="en-US",
timezone_id="America/New_York"
)
return browser, context
async def scrape_profile(self, url: str) -> dict:
"""Tek bir kamu profili toplar"""
async with async_playwright() as p:
browser, context = await self.create_browser_context(p)
page = await context.new_page()
try:
# Sayfaya git
await page.goto(url, wait_until="networkidle", timeout=30000)
# İnsan taklidi bekleme
await asyncio.sleep(random.uniform(2, 5))
# Sayfayı kaydır (bot tespiti karşıtı)
await page.evaluate("window.scrollBy(0, 300)")
await asyncio.sleep(random.uniform(0.5, 1.5))
# Veriyi çek
profile_data = await page.evaluate("""() => {
const name = document.querySelector('h1')?.innerText || null;
const headline = document.querySelector('.text-body-medium')?.innerText || null;
const location = document.querySelector('.text-body-small.inline')?.innerText || null;
return { name, headline, location };
}""")
return profile_data
except Exception as e:
print(f"Hata: {e}")
return None
finally:
await browser.close()
# Kullanım
async def main():
scraper = LinkedInScraper(
proxy_user="kullanici_adi",
proxy_pass="sifre"
)
profile = await scraper.scrape_profile(
"https://www.linkedin.com/in/some-public-profile/"
)
print(profile)
asyncio.run(main())
Rate Limiting Stratejisi
import time
from collections import deque
class RateLimiter:
"""Token bucket algoritması ile rate limiting"""
def __init__(self, requests_per_hour: int = 30):
self.requests_per_hour = requests_per_hour
self.requests = deque()
self.min_interval = 3600 / requests_per_hour # saniye başına istek
def wait_if_needed(self):
now = time.time()
# Eski istekleri temizle (1 saatten eski)
while self.requests and now - self.requests[0] > 3600:
self.requests.popleft()
# Limit kontrolü
if len(self.requests) >= self.requests_per_hour:
sleep_time = 3600 - (now - self.requests[0])
if sleep_time > 0:
print(f"Rate limit: {sleep_time:.0f} saniye bekleniyor...")
time.sleep(sleep_time)
# Minimum aralık kontrolü
if self.requests:
elapsed = now - self.requests[-1]
if elapsed < self.min_interval:
time.sleep(self.min_interval - elapsed)
self.requests.append(time.time())
# Kullanım
limiter = RateLimiter(requests_per_hour=25) # Muhafazakar limit
for profile_url in profile_urls:
limiter.wait_if_needed()
# scrape_profile() çağır
Session Rotation ile Proxy Kullanımı
Her profil için farklı bir IP kullanmak, tek IP'de engellenme riskini azaltır:
def get_proxy_url(session_id: str) -> str:
"""Her istek için farklı session/IP"""
return f"http://user-session-{session_id}:pass@gate.proxyhat.com:8080"
# Session ID oluştur
import uuid
session_id = str(uuid.uuid4())[:8]
proxy_url = get_proxy_url(session_id)
LinkedIn İş İlanları Toplama
LinkedIn Jobs, kamu erişimine daha açık olsa da, yine de rate limiting ve fingerprinting uygulanıyor.
Jobs Search URL Yapısı
LinkedIn'in iş arama URL'si sorgu parametreleri ile çalışır:
BASE_URL = "https://www.linkedin.com/jobs/search/"
# Parametreler
params = {
"keywords": "software engineer",
"location": "San Francisco",
"f_JT": "F", # Tam zamanlı
"f_E": "2", # Deneyim seviyesi (1=Intern, 2=Entry, 3=Associate...)
"start": 0, # Pagination offset (0, 25, 50...)
}
# URL oluştur
from urllib.parse import urlencode
url = f"{BASE_URL}?{urlencode(params)}"
Jobs Scraper Örneği
import aiohttp
import asyncio
from bs4 import BeautifulSoup
class LinkedInJobsScraper:
def __init__(self, proxy_user: str, proxy_pass: str):
self.proxy_auth = aiohttp.BasicAuth(proxy_user, proxy_pass)
self.proxy_url = "http://gate.proxyhat.com:8080"
async def fetch_jobs_page(self, session, keywords: str, location: str, start: int = 0):
"""Tek bir jobs sayfasını çeker"""
params = {
"keywords": keywords,
"location": location,
"start": start
}
headers = {
"User-Agent": (
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
"AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"
),
"Accept-Language": "en-US,en;q=0.9"
}
url = f"https://www.linkedin.com/jobs/search/?{urlencode(params)}"
async with session.get(
url,
proxy=self.proxy_url,
proxy_auth=self.proxy_auth,
headers=headers,
timeout=aiohttp.ClientTimeout(total=30)
) as response:
if response.status == 429:
raise Exception("Rate limited - bekleyin")
return await response.text()
def parse_jobs(self, html: str) -> list:
"""HTML'den iş ilanlarını ayrıştırır"""
soup = BeautifulSoup(html, "html.parser")
jobs = []
# LinkedIn jobs kartları
job_cards = soup.select(".jobs-search__results-list li")
for card in job_cards:
title_elem = card.select_one(".base-search-card__title")
company_elem = card.select_one(".base-search-card__subtitle")
location_elem = card.select_one(".job-search-card__location")
link_elem = card.select_one("a.base-card__full-link")
if title_elem and company_elem:
jobs.append({
"title": title_elem.get_text(strip=True),
"company": company_elem.get_text(strip=True),
"location": location_elem.get_text(strip=True) if location_elem else None,
"url": link_elem["href"] if link_elem else None
})
return jobs
async def scrape_jobs(self, keywords: str, location: str, max_pages: int = 5):
"""Birden fazla sayfa toplar"""
connector = aiohttp.TCPConnector(limit=1) # Tek bağlantı
async with aiohttp.ClientSession(connector=connector) as session:
all_jobs = []
for page in range(max_pages):
start = page * 25
# Rate limiting
await asyncio.sleep(random.uniform(3, 7))
html = await self.fetch_jobs_page(session, keywords, location, start)
jobs = self.parse_jobs(html)
if not jobs:
break # Daha fazla sonuç yok
all_jobs.extend(jobs)
print(f"Sayfa {page + 1}: {len(jobs)} iş ilanı")
return all_jobs
# Kullanım
async def main():
scraper = LinkedInJobsScraper("kullanici_adi", "sifre")
jobs = await scraper.scrape_jobs(
keywords="python developer",
location="Berlin",
max_pages=3
)
print(f"Toplam: {len(jobs)} iş ilanı")
asyncio.run(main())
Hangi Durumlarda Toplama YapMAYIN
Bazı veri türleri ve erişim yöntemleri kesinlikle yasal ve etik sınırlar dışındadır:
1. Oturum Açarak (Logged-In) Erişim
Kendi hesabınızla giriş yapıp veri toplamak:
- LinkedIn Kullanım Sözleşmesi'ni ihlal eder
- Hesabınız kalıcı olarak yasaklanabilir
- CFAA kapsamında "yetkisiz erişim" olarak değerlendirilebilir
2. Sales Navigator ve Premium Veriler
Ücretli abonelikle erişilen veriler:
- Gelişmiş arama filtreleri
- InMail mesajları
- Detaylı şirket analitikleri
- Lead önerileri
3. Özel (Private) Profiller
Gizlilik ayarları "private" olarak belirlenmiş profiller:
- "LinkedIn üyesi" olarak görünen profiller
- Sadece bağlantıların görebildiği veriler
4. Mesajlar ve İletişim Verileri
- Inbox mesajları
- E-posta adresleri (çoğu durumda)
- Telefon numaraları
5. Bağlantı Ağları
- Bir kullanıcının bağlantı listesi
- 2. ve 3. derece bağlantı detayları
Altın kural: Eğer veriyi oturum açmadan, gizli modda (incognito) göremiyorsanız — o veri kamuya açık değildir ve toplamamalısınız.
Alternatif: Resmi LinkedIn API'leri
LinkedIn, belirli kullanım durumları için resmi API'ler sunuyor. Bu API'ler yasal ve güvenilir veri erişimi sağlar:
| API | Hedef Kullanıcı | Veri Türü | Fiyatlandırma |
|---|---|---|---|
| LinkedIn Recruiter System Connect | İşe alım platformları | Aday profilleri (onaylı) | Kurumsal anlaşma |
| Marketing Developer Platform | Reklam teknolojisi | Reklam metrikleri, audience | Kullanım bazlı |
| Profile API | Onaylı ortaklar | Profil verisi (kullanıcı onayı şart) | Değişken |
| Share API | Sosyal medya araçları | Paylaşım oluşturma | Ücretsiz |
API Kullanmanın Avantajları
- Yasal güvence: LinkedIn ile doğrudan sözleşme
- Stabil veri: Format değişikliklerinden etkilenmez
- Destek: Teknik dokümantasyon ve destek
- Rate limits: Net ve öngörülebilir sınırlar
API Kullanmanın Dezavantajları
- Onay süreci: LinkedIn partner olmak zorunda
- Maliyet: Kurumsal düzeyde ücretler
- Kısıtlı kapsam: Sadece belirli veri türleri
- Kullanıcı onayı: Bazı veriler için kullanıcı izni şart
Etik Veri Toplama İlkeleri
Yasal sınırların ötesinde, etik veri toplama için şu ilkeleri izleyin:
1. Saygılı Rate Limiting
LinkedIn'in altyapısına aşırı yük bindirmeyin. Dakikada 1–2 sayfa gibi muhafazakar sınırlar koyun.
2. Robots.txt Kontrolü
LinkedIn'in robots.txt dosyasını inceleyin:
# linkedin.com/robots.txt (örnek)
User-agent: *
Disallow: /search/
Disallow: /jobs/api/
Allow: /jobs/view/
Allow: /in/
Allow: /company/
3. Veri Minimizasyonu
Sadece gerçekten ihtiyacınız olan veriyi toplayın. Tüm profili değil, sadece gerekli alanları alın.
4. KVKK/GDPR Uyumu
AB'den veya Türkiye'den toplanan kişisel veriler için:
- Veri işleme amacını belgeleyin
- Meşru bir gerekçe belirleyin
- Veri saklama sürelerini sınırlayın
- Kişilerin erişim/silme taleplerini karşılayın
5. Veri Güvenliği
Toplanan kişisel verileri güvenli saklayın:
- Şifreli depolama
- Erişim kontrolü
- Gerektiğinde anonimleştirme
Ne Zaman Resmi API Kullanmalısınız?
Aşağıdaki durumlarda scraping yerine resmi API'leri tercih edin:
- Üretim sistemi: Müşterilerinize hizmet veren bir ürün geliştiriyorsanız
- Ölçeklenebilirlik: Binlerce profil toplamanız gerekiyorsa
- Yasal güvence: Hukuki risk almak istemiyorsanız
- Veri kalitesi: Doğru ve güncel veri kritikse
- Uzun vadeli: Proje yıllar sürecekse
Scraping, araştırma, prototipleme ve küçük ölçekli analizler için uygun olabilir. Üretim sistemleri için resmi API'ler veya LinkedIn Partner Program'ı değerlendirin.
Key Takeaways
- Sadece kamuya açık veriyi hedefleyin: Oturum açmadan görünen profiller, şirket sayfaları ve iş ilanları. Private veriler ve Sales Navigator içeriği kesinlikle yasak.
- Residential proxy şart: LinkedIn datacenter IP'leri anında engelliyor. Residential ASN'ler gerçek kullanıcı trafiği gibi görünür.
- hiQ Labs emsali: ABD'de kamuya açık veri toplama belirli koşullarda CFAA ihlali sayılmayabilir, ancak her ülkenin yasaları farklı. Kendi hukuk danışmanınıza başvurun.
- Rate limiting kritik: Saatte 25–30 sayfa gibi muhafazakar sınırlar koyun. İnsan taklidi bekleme süreleri ve sayfa kaydırma kullanın.
- Resmi API'leri değerlendirin: Üretim sistemleri için LinkedIn Recruiter System Connect veya Marketing Developer Platform daha güvenli ve yasal.
- GDP/KVKK uyumu: Kişisel veri işleme için meşru gerekçe belirleyin, veri minimizasyonu uygulayın, güvenli saklama sağlayın.
Sonuç
LinkedIn'den kamuya açık veri toplamak, doğru teknik yaklaşım ve yasal/etik sınırlara uyumla mümkün. Residential proxy kullanımı, gerçekçi browser context'ler ve muhafazakar rate limiting, teknik başarının anahtarları.
Ancak unutmayın: yasal olmak ile etik olmak aynı şey değil. Sadece yasalara uymak yetmez; kullanıcıların gizliliğine saygı göstermek, platformun kurallarını dikkate almak ve veriyi sorumlu şekilde kullanmak da etik sorumluluğunuz.
ProxyHat olarak, residential proxy hizmetlerimizle yasal veri toplama projelerinize destek sunuyoruz. Daha fazla bilgi için web scraping kullanım senaryoları sayfamızı inceleyebilirsiniz.






