nodriver ile scraping, undetected-chromedriver'ın yaratıcısı Ultrafunkamsterdam tarafından geliştirilen, tamamen asenkron ve sıfır-Selenium bir Chrome DevTools Protocol (CDP) otomasyon kütüphanesidir. Eğer undetected-chromedriver'ı Cloudflare Turnstile, Imperva Incapsula veya DataDome engelleriyle baş etmek için kullandıysanız ve artık yetersiz kaldığını fark ettiyseniz, bu rehber tam size göre. Bu yazıda nodriver'ın mimarisini, deyimsel (idiomatic) API'sini ve ProxyHat residential proxy'lerle nasıl entegre edileceğini pratik kod örnekleriyle ele alacağız.
Yasal Uyarı: Bu rehber yalnızca yetkili otomasyon ve herkese açık veri toplama amaçlıdır. ABD'de CFAA (Computer Fraud and Abuse Act), AB'de ise GDPR kapsamında, hedef sitenin kullanım şartlarını (ToS) ve robots.txt direktiflerini ihlal etmek yasal sonuçlar doğurabilir. Yalnızca erişim izniniz olan veya herkese açık verileri toplayın.
nodriver Nedir ve Neden Var?
Selenium ve WebDriver tabanlı otomasyon araçlarının temel sorunu şudur: Chrome, WebDriver protokolü üzerinden kontrol edildiğinde navigator.webdriver özelliğini true olarak ayarlar. Bu bayrak, Cloudflare ve Imperva gibi anti-bot sistemleri tarafından kolayca tespit edilir. undetected-chromedriver bu bayrağı yamalamaya çalışırdı, ancak Chrome güncellendikçe yamalar kırılıyordu.
nodriver bu sorunu kökten çözer: Selenium'ı tamamen çıkarır ve doğrudan Chrome DevTools Protocol üzerinden WebSocket bağlantısı kurar. WebDriver katmanı olmadığı için navigator.webdriver bayrağı hiçbir zaman ayarlanmaz. CDP sızıntıları (CDP leak) en aza indirilir. Sonuç: tarayıcı, normal bir kullanıcı tarafından kullanılıyormuş gibi görünür.
nodriver'ın temel avantajları:
- Sıfır WebDriver:
navigator.webdriveryok, CDP izleri minimum. - Tamamen asenkron: Python
asyncioüzerine kurulu, yüksek eşzamanlılık (concurrency) desteği. - Deyimsel API:
uc.start(),Browser/Tabnesneleri,await tab.find()ile element bekleme. - Olay kancaları (event hooks):
tab.add_handler()ile sayfa olaylarını dinleme,WebDriverWaityerine.
nodriver Mimarisi: CDP ve Anti-Bot Tespiti
nodriver, Chrome'u --remote-debugging-port bayrağıyla başlatır ve doğrudan CDP WebSocket endpoint'ine bağlanır. Her sekme (Tab) ayrı bir CDP hedefidir. Bu mimarinin anti-bot tespitine karşı avantajları şöyledir:
1. navigator.webdriver Bayrağının Yokluğu
WebDriver protokolü kullanılmadığı için Chrome, navigator.webdriver özelliğini undefined veya false bırakır. Cloudflare'nin Turnstile sistemi bu bayrağı aktif olarak kontrol eder.
2. CDP Sızıntılarının Azaltılması
Selenium tabanlı araçlar Runtime.evaluate komutlarını sıkça kullanır ve bu komutların izleri JavaScript yığınında (stack trace) görünür. nodriver, CDP komutlarını daha az gürültülü şekilde kullanır.
3>Gerçek Kullanıcı Parmak İzi (Fingerprint)
nodriver, Chrome'u mümkün olduğunca az modifiye eder. Patched Chrome yerine gerçek Chrome binary'si kullanılır. Bu, chrome.runtime, Permissions API ve WebGL renderer parmak izlerinin tutarlı kalmasını sağlar.
Deyimsel nodriver API'si
nodriver, undetected_chromedriver paketinin devamı niteliğindedir ve uc modülü olarak içe aktarılır. Temel akış şöyledir:
import nodriver as uc
import asyncio
async def main():
# Browser başlat
browser = await uc.start()
# Yeni sekme aç ve sayfaya git
tab = await browser.get('https://example.com')
# Element bekle ve seç
element = await tab.select('h1', timeout=10)
print(await element.get_attribute('innerText'))
# Event hook ile sayfa yüklenmesini bekle
# WebDriverWait yerine: tab.add_handler kullan
browser.stop()
asyncio.run(main())
Dikkat edin: tab.select() ve tab.find() asenkron olarak element bekler. WebDriverWait veya expected_conditions yoktur. Element bulunana kadar otomatik olarak beklenir.
Event Hooks: Sayfa Olaylarını Dinleme
nodriver, CDP event'lerini doğrudan dinlemenize izin verir. Bu, ağ isteklerini yakalamak, console mesajlarını almak veya sayfa yönlendirmelerini izlemek için kullanışlıdır:
import nodriver as uc
async def main():
browser = await uc.start()
tab = await browser.get('https://example.com')
# Network response'larını yakala
async def on_response(event):
if event.response.url.endswith('.json'):
print(f'JSON response: {event.response.url}')
tab.add_handler(uc.cdp.network.ResponseReceived, on_response)
# Sayfada bir işlem tetikle
await tab.select('#load-data', timeout=10)
await tab.evaluate('document.getElementById("load-data").click()')
import asyncio
await asyncio.sleep(5)
browser.stop()
asyncio.run(main())
nodriver Proxy Entegrasyonu: browser_args ile ProxyHat
nodriver'ın yerleşik proxy rotasyonu yoktur. Chrome'un --proxy-server= bayrağını browser_args parametresi olarak geçmeniz gerekir. Ancak Chrome'un HTTP proxy kimlik doğrulaması (authentication) desteği sınırlıdır: --proxy-server bayrağı kullanıcı adı ve şifre kabul etmez. Bu nedenle iki yaklaşım vardır:
- CDP üzerinden kimlik doğrulama:
Fetch.authRequiredolayını yakalayıp kimlik bilgileri sağlama. - ProxyHat'in IP kimlik doğrulaması: ProxyHat panelinden IP'nizi whitelist'e ekleyerek şifre gereksinimini kaldırma.
İlk yaklaşım daha esnektir ve kod içinde tutarlıdır. İşte ProxyHat residential proxy ile nodriver entegrasyonu:
import nodriver as uc
import asyncio
PROXY_HOST = 'gate.proxyhat.com'
PROXY_PORT = '8080'
PROXY_USER = 'user-country-US-session-abc123'
PROXY_PASS = 'your_password'
async def main():
# Proxy'yi browser_args olarak geç
browser = await uc.start(
browser_args=[
f'--proxy-server=http://{PROXY_HOST}:{PROXY_PORT}'
]
)
tab = await browser.get('https://example.com')
# Proxy kimlik doğrulaması için CDP Fetch handler
async def auth_required(event):
await tab.send(uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=uc.cdp.fetch.AuthChallengeResponse(
response='ProvideCredentials',
username=PROXY_USER,
password=PROXY_PASS
)
))
tab.add_handler(uc.cdp.fetch.AuthRequired, auth_required)
# Fetch domain'i etkinleştir
await tab.send(uc.cdp.fetch.enable(
handle_auth_requests=True
))
# Sayfayı yeniden yükle
await tab.reload()
# İçeriği çıkar
h1 = await tab.select('h1', timeout=15)
text = await h1.get_attribute('innerText')
print(f'Sayfa başlığı: {text}')
browser.stop()
asyncio.run(main())
Bu örnekte ProxyHat'in gate.proxyhat.com:8080 HTTP gateway'ini kullanıyoruz. Kullanıcı adında country-US-session-abc123 formatı ile hem ülke hedeflemesi (ABD) hem de yapışkan oturum (sticky session) tanımlıyoruz. Yapışkan oturum, aynı IP'nin oturum boyunca korunmasını sağlar — bu, oturum açma (login) gerektiren akışlar için kritiktir.
Residential proxy'ler headful stealth için neden esastır? Datacenter IP'leri ASN veritabanlarında kolayca işaretlenir. Cloudflare, bir datacenter IP'sinden gelen istekleri otomatik olarak daha sıkı challenge'lara tabi tutar. Residential IP'ler ise gerçek ISP'lere atanmıştır ve normal kullanıcı trafiğiyle ayırt edilmesi çok daha zordur. ProxyHat'in 190+ lokasyonu ile coğrafi hedeflemeyi de kolayca yapabilirsiniz.
ProxyHat SDK ile Proxy URL Oluşturma
Daha temiz bir entegrasyon için proxy URL'sini dinamik olarak oluşturan bir yardımcı fonksiyon tanımlayabilirsiniz:
import nodriver as uc
import asyncio
import random
import string
class ProxyHatConfig:
HOST = 'gate.proxyhat.com'
HTTP_PORT = 8080
SOCKS5_PORT = 1080
def __init__(self, username, password):
self.username = username
self.password = password
def build_username(self, country=None, city=None, session_id=None):
parts = [self.username]
if country:
parts.append(f'country-{country}')
if city:
parts.append(f'city-{city}')
if session_id:
parts.append(f'session-{session_id}')
return '-'.join(parts)
def get_http_proxy_arg(self, country=None, city=None, session_id=None):
user = self.build_username(country, city, session_id)
return f'--proxy-server=http://{self.HOST}:{self.HTTP_PORT}'
def get_auth_credentials(self, country=None, city=None, session_id=None):
user = self.build_username(country, city, session_id)
return user, self.password
def random_session():
return ''.join(random.choices(string.ascii_lowercase + string.digits, k=12))
async def scrape_protected_page(url, country='US'):
config = ProxyHatConfig('user', 'your_password')
session_id = random_session()
proxy_arg = config.get_http_proxy_arg(country=country, session_id=session_id)
auth_user, auth_pass = config.get_auth_credentials(
country=country, session_id=session_id
)
browser = await uc.start(browser_args=[proxy_arg])
tab = await browser.get(url)
# Auth handler
async def auth_required(event):
await tab.send(uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=uc.cdp.fetch.AuthChallengeResponse(
response='ProvideCredentials',
username=auth_user,
password=auth_pass
)
))
tab.add_handler(uc.cdp.fetch.AuthRequired, auth_required)
await tab.send(uc.cdp.fetch.enable(handle_auth_requests=True))
await tab.reload()
# JSON veri çıkar
content = await tab.evaluate(
'document.querySelector("pre").innerText',
await_promise=True
)
import json
data = json.loads(content['value'])
print(f'Çekilen veri: {data}')
browser.stop()
return data
asyncio.run(scrape_protected_page('https://protected-site.com/api/data'))
Ölçeklendirme: Eşzamanlı Sekmeler ve Docker Fleet
nodriver'ın asenkron doğası, birden fazla sekme ve browser örneğini eşzamanlı olarak çalıştırmaya uygundur. İşte ölçeklendirme desenleri:
1. Eşzamanlı Sekmeler (Concurrent Tabs)
import nodriver as uc
import asyncio
async def scrape_task(browser, url, proxy_user, proxy_pass):
tab = await browser.get(url)
async def auth_required(event):
await tab.send(uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=uc.cdp.fetch.AuthChallengeResponse(
response='ProvideCredentials',
username=proxy_user,
password=proxy_pass
)
))
tab.add_handler(uc.cdp.fetch.AuthRequired, auth_required)
await tab.send(uc.cdp.fetch.enable(handle_auth_requests=True))
title = await tab.select('title', timeout=15)
result = await title.get_attribute('innerText')
await tab.close()
return result
async def main():
urls = [
'https://example.com/page1',
'https://example.com/page2',
'https://example.com/page3',
'https://example.com/page4',
]
browser = await uc.start(
browser_args=['--proxy-server=http://gate.proxyhat.com:8080']
)
tasks = [
scrape_task(browser, url, 'user-country-US-session-s1', 'pass')
for url in urls
]
results = await asyncio.gather(*tasks)
print(results)
browser.stop()
asyncio.run(main())
2. Docker Fleet ile Headless Browser Ölçeklendirme
Büyük ölçekli scraping için her browser örneğini ayrı bir Docker container'da çalıştırmak, kaynak izolasyonu ve hata toleransı sağlar. Her container'a farklı bir ProxyHat oturum atayarak IP rotasyonu yapabilirsiniz:
# Dockerfile.nodriver
FROM python:3.11-slim
RUN apt-get update && apt-get install -y \
chromium \
chromium-driver \
fonts-liberation \
libnss3 \
libxss1 \
&& rm -rf /var/lib/apt/lists/*
RUN pip install nodriver
COPY scraper.py /app/scraper.py
WORKDIR /app
ENV PROXYHAT_USER=user
ENV PROXYHAT_PASS=pass
ENV COUNTRY=US
CMD ["python", "scraper.py"]
Docker Compose ile birden fazla worker başlatın:
# docker-compose.yml
version: '3.8'
services:
scraper-1:
build: .
environment:
- SESSION_ID=worker1abc
- COUNTRY=US
scraper-2:
build: .
environment:
- SESSION_ID=worker2def
- COUNTRY=DE
scraper-3:
build: .
environment:
- SESSION_ID=worker3ghi
- COUNTRY=GB
3. Per-Context Proxy Ataması ve Graceful Shutdown
Her browser context'e farklı bir ProxyHat oturumu atayarak IP rotasyonunu yönetebilirsiniz. Graceful shutdown için signal modülü ile SIGINT yakalayın:
import nodriver as uc
import asyncio
import signal
class ScraperFleet:
def __init__(self, proxyhat_user, proxyhat_pass):
self.user = proxyhat_user
self.pass_ = proxyhat_pass
self.browsers = []
async def launch_worker(self, country, session_id):
proxy_arg = f'--proxy-server=http://gate.proxyhat.com:8080'
browser = await uc.start(browser_args=[proxy_arg])
self.browsers.append(browser)
return browser
async def shutdown(self):
for browser in self.browsers:
browser.stop()
async def main():
fleet = ScraperFleet('user', 'pass')
loop = asyncio.get_event_loop()
for sig in (signal.SIGINT, signal.SIGTERM):
loop.add_signal_handler(sig, lambda: asyncio.create_task(fleet.shutdown()))
await fleet.launch_worker('US', 'sess-001')
await fleet.launch_worker('DE', 'sess-002')
# ... scraping logic ...
await fleet.shutdown()
asyncio.run(main())
Ne Zaman Tam Browser Kullanmamalı?
nodriver güçlüdür, ancak her scraping görevi için bir browser gerektirmez. Hedef site JavaScript render gerektirmiyorsa veya basit HTTP istekleri yeterliyse, curl_cffi veya httpx gibi HTTP istemcileri çok daha ucuz ve hızlıdır:
| Yaklaşım | Bellek Kullanımı | Hız (istek/sn) | Anti-Bot Geçişi | Maliyet |
|---|---|---|---|---|
| nodriver + Residential Proxy | ~300-500 MB / browser | 5-20 | Yüksek (Cloudflare, Imperva) | Yüksek |
| curl_cffi + Residential Proxy | ~10-20 MB / istek | 100-500 | Orta (TLS parmak izi taklit) | Düşük |
| httpx + Datacenter Proxy | ~10 MB / istek | 500-1000 | Düşük | En düşük |
Eğer hedef site yalnızca basit TLS parmak izi kontrolü yapıyorsa, curl_cffi ile impersonate="chrome120" parametresi yeterli olabilir. Bu yaklaşım, browser başlatma maliyetini ortadan kaldırır ve saniyede 100+ istek yapmanıza izin verir. nodriver'ı yalnızca gerçekten gerekli olduğunda — yani JavaScript execution, canvas/WebGL render veya karmaşık etkileşim gerektiğinde — kullanın.
ProxyHat'e Özel Kurulum
ProxyHat residential proxy'leri nodriver ile kullanmak için şu adımları izleyin:
- ProxyHat pricing sayfasından residential proxy planınızı seçin.
- Dashboard'a giriş yapın ve proxy kimlik bilgilerinizi alın.
- Yukarıdaki kod örneklerinde
your_passwordveuserdeğerlerini kendi kimlik bilgilerinizle değiştirin. - Web scraping use case sayfamızdan diğer scraping desenlerini inceleyin.
- SERP tracking için SERP tracking use case rehberimize bakın.
- Tüm mevcut lokasyonları görmek için locations sayfasını ziyaret edin.
Detaylı API dokümantasyonu için ProxyHat Docs sayfasına başvurun.
Key Takeaways
- nodriver, WebDriver'ı tamamen kaldırır:
navigator.webdriverbayrağı yok, CDP sızıntıları minimum — Cloudflare ve Imperva'ya karşı en temiz browser otomasyonu. - Asenkron mimari:
asyncioüzerine kurulu, eşzamanlı sekme ve browser yönetimi için ideal.WebDriverWaityerine event hooks kullanılır. - Proxy entegrasyonu
browser_argsile: nodriver'ın yerleşik proxy rotasyonu yoktur;--proxy-server=bayrağı ve CDPFetch.authRequiredhandler'ı ile kimlik doğrulama yapılır. - Residential proxy'ler esastır: Datacenter IP'leri Cloudflare tarafından otomatik olarak işaretlenir; residential IP'ler normal kullanıcı trafiği gibi görünür.
- Ölçeklendirme: Eşzamanlı sekmeler, Docker fleet ve per-context proxy ataması ile IP rotasyonu yönetilir. Graceful shutdown için signal handler kullanın.
- Her zaman browser kullanmayın: Basit HTTP istekleri için
curl_cffi10x daha ucuz ve hızlıdır. nodriver'ı yalnızca JS render gerektiğinde kullanın.
SSS
nodriver ile scraping nedir?
nodriver ile scraping, Selenium/WebDriver kullanmadan doğrudan Chrome DevTools Protocol üzerinden tarayıcı kontrol ederek web sayfalarından veri toplama yöntemidir. nodriver, undetected-chromedriver'ın yaratıcısı tarafından geliştirilen, tamamen asenkron ve sıfır-Selenium bir kütüphanedir. navigator.webdriver bayrağını hiç ayarlamadığı için Cloudflare ve Imperva gibi anti-bot sistemlerine karşı daha etkilidir.
nodriver proxy kullanıcıları için neden önemli?
nodriver proxy entegrasyonu, browser tabanlı scraping sırasında IP engellemelerini aşmak için kritiktir. nodriver'ın yerleşik proxy rotasyonu olmadığından, --proxy-server= bayrağı ve CDP Fetch.authRequired handler'ı ile proxy kimlik doğrulaması yapmanız gerekir. Residential proxy'ler, datacenter IP'lerinin aksine gerçek ISP'lere atanmıştır ve Cloudflare tarafından normal kullanıcı trafiği olarak değerlendirilir.
nodriver ile hangi proxy tipi en iyisidir?
Residential proxy'ler nodriver için en iyi seçimdir. Datacenter IP'leri ASN veritabanlarında işaretlenir ve Cloudflare tarafından otomatik olarak daha sıkı challenge'lara tabi tutulur. Mobile proxy'ler de etkilidir ancak daha pahalıdır. ProxyHat, 190+ ülkede residential IP'ler sunar ve country-US-session-abc123 formatı ile hem geo-targeting hem de sticky session destekler. SOCKS5 için gate.proxyhat.com:1080, HTTP için gate.proxyhat.com:8080 kullanın.
nodriver ile scraping yaparken engellenmekten nasıl kaçınılır?
Engellenmekten kaçınmak için: (1) Residential proxy kullanın ve her görev için farklı bir sticky session atayın; (2) İstekler arasına rastgele gecikmeler ekleyin (2-5 saniye); (3) Gerçekçi User-Agent ve Accept-Language header'ları kullanın; (4) robots.txt direktiflerine uyun; (5) Eşzamanlı istek sayısını sınırlayın (browser başına 3-5 sekme); (6) Hedef sitenin ToS'unu okuyun ve yalnızca herkese açık verileri toplayın.
nodriver undetected-chromedriver'dan nasıl farklı?
nodriver, undetected-chromedriver'ın halefidir. Temel farklar: (1) Tamamen asenkron (asyncio), undetected-chromedriver senkron; (2) Sıfır WebDriver, doğrudan CDP — undetected-chromedriver WebDriver'ı yamalar; (3) Event hooks ile sayfa olaylarını dinleme; (4) Daha az Chrome modifikasyonu, daha tutarlı parmak izi; (5) Daha aktif geliştirme ve bakım. undetected-chromedriver'ın yaratıcısı her iki projeyi de geliştirir, ancak nodriver'ı öncelikli olarak tavsiye eder.






