nodriver, ultrafunkamsterdam tarafından geliştirilen bir Python tarayıcı otomasyon kütüphanesi; aynı zamanda undetected-chromedriver’ın arkasındaki isimdir ve kendisini o projenin devamı olarak konumlandırır. Mimari açıdan en net ayrım Selenium ve chromedriver çizgisinden gelir: nodriver, WebDriver ikilisi yerine Chrome DevTools Protocol (CDP) üzerinden Chromium ile konuşur ve API’si asenkron yapıdadır. Playwright ve Puppeteer ise farklı bir karşılaştırma grubuna girer. Onlar da protokole dayalı tarayıcı denetleyicileridir; WebDriver türevi değillerdir. Aralarındaki pratik farklar API tasarımı, paketleme, tarayıcıyı sağlama biçimi ve uyumluluk politikasıdır.
Bu inceleme, nodriver 0.50.3 sürümünü canlı savunmalara karşı test etmek yerine envanterlemeye odaklanıyor. Yüklü paketi, import davranışını, API yüzeyini, disk kapladığı alanı ve lisansını ölçtüm; ardından tarayıcıları yalnızca 127.0.0.1 üzerinde sunulan sayfalara karşı çalıştırdım. Hiçbir canlı hedef, anti-bot hizmeti veya CAPTCHA kullanılmadı. Sonuçlar paket davranışını ve varsayılan tarayıcı ifşasını anlatır; anti-detection etkinliğini kanıtlamaz.
Bu sınır içinde üç şey öne çıktı. İlki, Python 3.14’te kütüphane hiç içe aktarılamıyor — tek bir başıboş bayt, herhangi bir şey çağırmadan önce tüm paketi çökertiyor. İkincisi, bu kategoriye göre şaşırtıcı derecede küçük: üç doğrudan bağımlılık, bunlara eklenen bir geçişli paket ve ölçülen ortamda yaklaşık 17 MB. Üçüncüsü, kendi sayfamda nodriver’ın stock Playwright ve stock Puppeteer’dan görünür biçimde ayrıldığı tek özellik bir boolean — üstelik headless kullanıcı aracısı dizisi üçünde de hâlâ HeadlessChrome diyor. Geriye kalan sürprizler lisans tarafında.
Python 3.14’teki import kırılması
İlk bitecek yerden başlayalım, çünkü sizin kodunuz çalışmadan önce orada patlıyor. Python 3.14 üzerinde düz bir import nodriver doğrudan başarısız oluyor:
File ".../nodriver/cdp/network.py", line 1345
#: JSON (±Inf).
^
SyntaxError: Non-UTF-8 code starting with '\xb1' on line 1345, but no encoding declared; see PEP 263
Hata veren dosya otomatik üretilmiş cdp/network.py (başlığında # DO NOT EDIT THIS FILE! yazıyor). Dosyada, #: JSON (±Inf). yorumundaki ± karakterine ait 0xb1 adlı tek bir UTF-8 dışı bayt var ve kaynak kodlama bildirimi yok. Modül zinciri nodriver/__init__ → cdp/__init__ → network şeklinde yüklendiği için ayrıştırma hatası import’u kesiyor. Paket taramasında UTF-8 dışı başka bir kaynak dosya bulunmadı.
Sürüm sınırını dikkatli ifade etmek gerekiyor. Python 3.14.2 bu dosyayı reddediyor; Python 3.12.13 ise yamalanmamış haliyle import ediyor. Bu ortamlardaki network.py dosyaları bayt düzeyinde aynı (SHA-256 ef755f41800d4efb593736f8b55b331bba68eec373ed62ece433e66e4b491cd6), dolayısıyla sonuç farkını farklı kaynak artefaktları açıklamıyor. Bu inceleme, tam olarak hangi CPython tokenizer değişikliğinin bunu tetiklediğini izole etmedi; Python 3.13 de test edilmedi. Burada yapılan şey, iki ölçülmüş yorumlayıcı sonucunu raporlamak; her eski Python sürümünün 3.12 gibi davrandığını iddia etmek değil.
Bu iki ölçüm noktasından Python 3.13 hakkında bir sonuç çıkmaz.
Bu bir keşiften çok, bir yeniden üretim. Aynı Python 3.14 traceback’i nodriver issue #35 içinde de yer alıyor; önerilen çözüm pull request #36 içinde sunulmuş. 0.50.3 sürümü hâlâ o baytı içeriyor. PyPI metadata’sı Python 3.13’e kadar sınıflandırıcılar listeliyor, 3.14 desteği iddia etmiyor.
Geçici çözüm, hatanın kendisi kadar küçük: test edilmiş bir Python sürümü kullanın ya da o tek dosyayı pull request #36’nın önerdiği gibi UTF-8’e yeniden kodlayın. Yeniden kodlama sonrası paket 3.14’te import oluyor ve introspection aşamasında ikinci bir engel çıkmıyor. Aşağıdaki temiz 3.12.13 çalışması yamalanmamış bir yolu doğruluyor; Python 3.13 burada hâlâ test edilmedi.
Python 3.12.13 üzerinde import nodriver yama olmadan başarıyla çalışıyor. Bu incelemedeki tüm tarayıcı sürüşü sayıları o kurulumdan geliyor. Disk alanı ve import süresi ölçümleri ise 3.14 üzerindeki tek baytlık yeniden kodlanmış kopyadan alındı ve buna göre etiketlendi. Python 3.13 burada hâlâ test edilmedi; bir 3.12 sonucu, eski aralığın tamamını kapsadığı anlamına gelmez.
Varsayılan olarak güncel yorumlayıcılar kullanıyorsanız — ve birçok ekip yeni Python’a hızlıca geçiyor — bu, küçük ama gerçek bir duvardır. Bunu önceden bilin ki hiç yazmadığınız bir dosyadaki SyntaxError yüzünden bir öğleden sonranız boşa gitmesin.
nodriver gerçekte nedir, perde arkasında ne yapar?
Tek cümlelik tanım (“CDP-native, webdriver yok”) önce pazarlama gibi gelebilir; ta ki wheel içinde ne gönderildiğine bakana kadar. nodriver, kendi tam DevTools Protocol bağlayıcı setini paketliyor: nodriver.cdp paketi 57 protokol alanı modülü içeriyor — accessibility, dom, network, page, fetch, runtime, target, storage, input, emulation ve daha fazlası. Bu modül sayısı, “doğrudan CDP, Selenium yok” iddiasının teknik karşılığı. WebDriver konuşan bir chromedriver çalıştırıp çeviri yaptırmak yerine, nodriver CDP alanları için Python nesneleri üretiyor ve protokolü WebSocket üzerinden doğrudan konuşuyor. İçinde bozuk bayt bulunan cdp/network.py dosyası da bu 57 otomatik üretilmiş modülden biri; yani kırılma, kimsenin elle düzenlemediği üretilmiş kodda yaşıyor.
Bu protokol katmanının üstünde daha kullanıcı dostu bir nesne modeli var. Tab nesnesi 62 genel yöntem sunuyor ve bulma yüzeyi çoğu sürücünün alıştığından geniş: find() ve find_all() ile metin eşleştirme, select() ve select_all() ile CSS, ayrıca yerel bir xpath() girişi. Aynı nesnede XPath, CSS ve metin araması olması gerçekten kullanışlı; başka kütüphanelerde XPath için bazen evaluate() seviyesine inmeniz gerekir. Config kurucusu ise user_data_dir, headless, browser_executable_path, browser_args, sandbox, varsayılanı 'en-US' olan lang, host, port, expert ve **kwargs parametrelerini açığa çıkarıyor. Bu API envanteri sırasında Config(headless=True) oluşturmak 16 Chromium başlatma bayrağı üretti; bunların arasında --no-first-run, --no-default-browser-check, --remote-allow-origins=* ve --homepage=about:blank var. Bu introspection adımında start() çağırmadım. Aşağıdaki tarayıcı testleri ayrı bir çalıştırmaydı.

Kütüphane, anti-detection odaklı bir API yüzeyi de sunuyor — varlığını doğruladım ama davranışını herhangi bir hedefe karşı denemedim. Bunu bir kez not edip geçiyorum; çünkü gerçek bir servis üzerindeki etkisi, özellikle test etmeyi bilinçli olarak seçmediğim şeyin ta kendisi. Tarafsız söylemek gerekirse: nodriver’ın isimlendirmesi, bazı stealth etiketli rakiplerine göre daha ölçülü. Hikâye, detect_and_bypass tarzı yöntem isimleriyle dolu bir yüzeyden ziyade mimari bir iddia üzerine kurulu: CDP-native, her çalıştırmada yeni profil. Bunu sonuç değil, API tasarımı gözlemi olarak okuyun.
Bu sayılar paketi import edip Python’ın kendi introspection araçlarını — inspect, modül dolaşımı, attribute sayımı — kullanarak çıkarıldı. Hiçbir siteye dokunulmadı. Yeniden çalıştırmak isterseniz, değerler doğrudan paket içinden geliyor; sezgi değil.
Kendi hakkında ne ilan ediyor?

Gerçek bir savunmaya yaklaşmadan cevaplanabilecek bir soru: nodriver bir tarayıcıyı çalıştırdığında, o tarayıcı baktığı sayfaya kendi hakkında neleri açık ediyor? navigator.webdriver, user-agent, platform, diller, plugin ve donanım sayıları, window.chrome yapısı, Permissions API’nin söylediği şey, pencere ve ekran geometrisi gibi bariz alanları okuyan bir sayfa yazdım; bunu 127.0.0.1 üzerinde servis ettim ve dört yığını sayfaya gönderdim: nodriver, Botasaurus ve kontrol olarak stock Playwright ile stock Puppeteer. Dördü de aynı Chrome derlemesini kullandı (Chrome for Testing 151.0.7922.10), dolayısıyla farklılık varsa sebep tarayıcı değil, kütüphanenin kendisi. Headless ve headed, her biri üç kez. Aşağıdaki her değer üç denemede de aynı kaldı.
| Yığın | Mod | navigator.webdriver | User-agent belirteci | navigator.languages |
|---|---|---|---|---|
| nodriver 0.50.3 | headless | false | HeadlessChrome/151.0.0.0 | ["en-US"] |
| nodriver 0.50.3 | headed | false | Chrome/151.0.0.0 | ["en-US"] |
| Botasaurus 4.0.92 | headless / headed | false | HeadlessChrome/151 / Chrome/151 | ["en-US"] |
| Playwright 1.56.0 | headless / headed | true | HeadlessChrome/151 / Chrome/151 | ["en-US","en"] |
| Puppeteer 24.16.0 | headless / headed | true | HeadlessChrome/151 / Chrome/151 | ["en-US"] |
En net fark tek boolean. Bu varsayılan başlatma yapılandırmalarında nodriver, navigator.webdriver değerini false olarak döndürürken stock Playwright ve Puppeteer hem headless hem headed çalışmalarda true döndürüyor. Test, yapılandırma düzeyinde bir farkı gösteriyor; bunun yalnızca WebDriver binary’sinin olmamasından kaynaklandığını kanıtlamıyor.
Dört yığında da özellik hâlâ Navigator.prototype üzerindeki tarayıcının yerel getter’ı — function get webdriver() { [native code] } — olarak duruyor; instance üzerinde bir own-property değil ve sayfa JavaScript’i tarafından sonradan değiştirilmiş bir fonksiyon da değil. Özellik, yüklemeden sonra sayfa JavaScript’i tarafından yeniden yazılmadı. Test, farklı değerden sorumlu başlatma argümanını ya da kaynak yolunu tek tek ayırmadı.
İkinci detay pazarlamayı sınırlıyor. Headless modda nodriver’ın user-agent’ı hâlâ HeadlessChrome/151.0.0.0 olarak görünür — stock Puppeteer ile aynı, stock Playwright ile aynı. Headed çalıştırınca da Chrome/151.0.0.0 oluyor, yine birebir aynı. Bir anti-detection kütüphanesinin tarayıcı otomasyonunda en meşhur kimlik belirten dizesini varsayılan olarak maskelediğini varsayıyorsanız, öyle değil. Bunu siz ayarlamalısınız.
Geri kalan neredeyse her şey dört yığın arasında aynıydı; bunu açıkça söylemek önemli, çünkü anlatıyı daraltıyor — aşağıdaki her özellik nodriver, Botasaurus, Playwright ve Puppeteer üzerinde aynı değeri verdi:
| Özellik | Tüm dört yığında aynı değer |
|---|---|
platform | MacIntel |
vendor | Google Inc. |
| Plugin sayısı | beş |
| MIME türleri | iki |
pdfViewerEnabled | true |
| Mantıksal çekirdek | on iki |
| Bildirilen cihaz belleği | 16 GB |
| Dokunma noktası | sıfır |
window.chrome | mevcut, app/csi/loadTimes var ve runtime yok |
| WebGL renderer dizesi | dört yığında da aynı |
Permissions API ile Notification.permission arasındaki klasik çelişki hiçbir yerde çıkmadı — dördü de default ve prompt değerlerini uyumlu şekilde raporladı. Eski WebDriver yığınlarında bilinen cdc_ tarzı kalıntılar için document ve window üzerinde de tarama yaptım: hepsinde boştu.
nodriver’ın kontrol örneğine göre daha çok çıplak bir otomatik tarayıcıya benzediği tek yer pencere geometrisi. Headless nodriver, 800×600 ekranda outerWidth/outerHeight değerlerini 0×0 olarak bildiriyor; headless Playwright ise sizin yerinize bir viewport ayarladığı için 1280×720 raporluyor. Puppeteer, nodriver gibi 0×0’da kalıyor. Bu, yetenek farkı değil varsayılan yapılandırma farkı; değiştirmek sizin elinizde.
Dağıtımdan önce bilinmesi gereken son şey: nodriver tarayıcıyla birlikte gelmiyor, yani kutudan çıktığı haliyle makinenizde yüklü olan Chrome neyse onu kullanıyor. Benim makinemde bu, /Applications/Google Chrome.app dizinini otomatik olarak bulduğu ve Chrome 150.0.7871.187 kullandığı anlamına geldi; açıkladığı user-agent da sabitlenmiş bir sürüm değil, bu sürümdü. Filonuzun açıkladığı tarayıcı sürümü, filonuzda ne yüklüyse odur.
Bunun ne olduğunu ve ne olmadığını net söyleyelim. Bu, kimsenin saklamasını istemediğinde otomatik bir yığının ne açıkladığının kaydıdır — savunma tarafındaysanız faydalı, kendi araçlarınızın ne yayınladığını bilmek istiyorsanız da faydalı. Ancak bunun herhangi bir servis için ne ifade ettiğinin ölçüsü değildir. Bunu test etmedim ve yukarıdaki hiçbir satır bir sonuca işaret ediyor diye okunmamalıdır.
Gerçekten sayfadan içerik çekebiliyor mu?
Kendini ilan etmek bir şeydir; doğru HTML’yi geri vermek işin kendisidir. nodriver’ı, bu benchmark deposunun kullandığı aynı üç içerik sınıfına sahip test sayfasında çalıştırdım; böylece sayılar burada ölçülen diğer araçlarla aynı çizgide kalıyor. Sayfa üç şey içeriyor: A, sunulan baytların içinde düz metin olarak yer alan bir işaretçiye sahip statik bir bağlantı; B, ayrıştırma sırasında satır içi bir script tarafından üretilen ve işaretçisi ile URL’si parçalar halinde birleştirilen, ancak JavaScript çalıştırıldığında ortaya çıkan bir düğüm; ve C, yükleme olayından 800 ms sonra eklenen ve aynı şekilde birleştirilen bir düğüm. Sınıf C adversarial olan: yükleme anında yapılan bir okuma onu göremez.
| Yığın | Varsayılan okuma | Açık bir bekleme ile |
|---|---|---|
| nodriver 0.50.3 | 3’ün 2’si (A + B, C’yi kaçırır) | 3’ün 3’ü |
| Botasaurus 4.0.92 | 3’ün 2’si | 3’ün 3’ü |
| Playwright 1.56.0 | 3’ün 2’si | 3’ün 3’ü |
| Puppeteer 24.16.0 | 3’ün 2’si | 3’ün 3’ü |
nodriver tam da büyük oyuncuların ulaştığı yere geliyor. browser.get() ardından doğrudan tab.get_content() bir load anı anlık görüntüsüdür: JavaScript’i doğru biçimde işler — sınıf B bunu kanıtlar, çünkü sınıf B servis edilen baytların hiçbir yerinde yokken yine de geri gelir — fakat yüklemeden sonra enjekte edilen hiçbir şeyi görmez. tab.select("#delayed-injected") eklerseniz üçte üç alırsınız. Aynı tuzak, aynı çözüm; Playwright ve Puppeteer ile aynı. Üç tekrar ve tüm paketin üç ayrı çalıştırmasında istikrarlı, sıfır flake.
Varsayılan okumanın ne zaman pes ettiğini bulmak için enjeksiyon gecikmesini taradım. nodriver, enjeksiyon yüklemeden 100 ms veya daha sonra gerçekleştiğinde sınıf C’yi görmeyi bırakıyor — stock kontrol örnekleriyle aynı eşik. (Buradaki tek istisna Botasaurus; anti-detect kütüphaneler arasındaki gerçekten ilginç fark da bu: get() çağrısı varsayılan olarak tam sayfa yüklenmesini bekliyor, bu yüzden varsayılan okuması 300 ms’deki enjeksiyonu hâlâ yakalıyor. Bunun karşılığında her gezinmede yaklaşık 250 ms ödüyor.)
Bekleme mekanizmasının bütçeye yazılması gereken bir tuhaflığı var. Bu taramada nodriver’ın tab.select() metodu, enjeksiyon gecikmesini yakından izlemek yerine kaba polling aralıklarıyla ilerledi:
| Sınıf-C gecikmesi | 0 ms | 100 ms | 400 ms | 800 ms | 1500 ms |
|---|---|---|---|---|---|
nodriver select() | 124–152 ms | 1132–1141 | 1128–1129 | 2132–2177 | 2138–2150 |
Puppeteer waitForSelector | 113–129 ms | 203–216 | 512–516 | 911–919 | 1608–1611 |
Bu fixture’da 100 ms’lik bir enjeksiyon, select() için yaklaşık 1.1 saniyeye çıktı. Yüklü döngü, kaçırılan durumda await self ve ardından await self.sleep(0.5) yapıyor; ölçülen birleşik döngü burada yaklaşık bir saniyeye denk geldi, ama await self evrensel olarak sabit süreli bir uyku olarak tanımlanmış değil. Test edilen her gecikmeli düğüm bulundu. Puppeteer’ın bekleyicisi bu gecikmeleri daha yakından izledi. Birçok ardışık bekleme farkı büyütebilir, fakat bu inceleme otuz seçicili bir üretim sayfasını benchmark etmedi.
Başlatma aşaması, asenkron ve hafif çerçevesinin gerçek hayattaki karşılığıdır. Tarayıcıyı ayağa kaldırmak nodriver’ı Botasaurus ve Puppeteer’la aynı kümeye yerleştirdi, Playwright’ın ise belirgin şekilde gerisinde bıraktı:
| Yığın | Tarayıcı başlatma süresi, çalıştırmalar boyunca |
|---|---|
| nodriver 0.50.3 | 910–1583 ms |
| Botasaurus 4.0.92 | 986–1151 ms |
| Puppeteer 24.16.0 | 969–1008 ms |
| Playwright 1.56.0 | 282–365 ms |
Çalıştıktan sonra nodriver’ın gezinme ve okuma süresi 119–129 ms ile dördün en hızlısı. Direksiyonda hafif, ateşlemede ortalama.
Kurulum ve ayak izi: gerçekten güçlü taraf
nodriver’ın “odaklı” etiketini hak ettiği yer burası; bu, taramadan ziyade doğrulanabilir ve zararsız bir kurulum gerçeği. Temiz bir pip install nodriver şu bağımlılıklara çözülüyor:
| Kurulum gerçeği | nodriver 0.50.3 |
|---|---|
| Beyan edilen doğrudan çalışma zamanı bağımlılıkları | üç — websockets, mss, deprecated |
| Çözülen geçişli bağımlılık | wrapt (deprecated üzerinden) |
Ölçülen site-packages toplamı | yaklaşık 17.2 MB, 6 dist-info dizini boyunca, ortamın pip paketi dahil |
| nodriver’ın kendi payı | 3.7 MB |
| numpy / lxml | ikisi de yok |
| Kurulum sırasında tarayıcı ikili dosyası | hiçbir şey indirilmedi |
Bu kategoride çoğu zaman bir render yığınını da yanında sürükleyen araçlar düşünüldüğünde, bu oldukça hafif bir dokunuş.
Doğrudan ve geçişli paketler arasındaki ayrım bakım için önemlidir. nodriver’ın metadata’sı mss, websockets ve deprecated istiyor; wrapt ise deprecated onu gerektirdiği için geliyor. 17.2 MB’lık ortamda altıncı dist-info dizini, zaten o sanal ortamın parçası olan pip. Dolayısıyla “altı dağıtım boyunca 17.2 MB” ölçülen ortamı tanımlar; “eklenen dört çalışma zamanı paketi” ise kurulumun çözdüğünü anlatır. Bunlar ilişkili ama birbirinin yerine geçmeyen sayılardır.
Bu sayıya anlam kazandıran karşılaştırma şu: aynı makinede kardeş çerçeve Botasaurus 44 paket boyunca 122.3 MB kaplıyor — kabaca yedi kat disk ayak izi. Odaklı bir CDP sürücüsü ile her şey dahil bir çerçevenin farkı bu; iki taraflı çalışıyor. nodriver size gerçekten denetlenebilir, ince ve okunabilir bir bağımlılık ağacı veriyor; Botasaurus ise kutudan çıktığı anda daha fazlasını sunuyor ama karşılığında disk ve bağımlılık yüzeyi istiyor. Soyut anlamda hiçbiri “daha iyi” değil — ihtiyacınız bir sürücü mü yoksa bir çerçeve mi, buna bağlı — ama küçük ve incelenebilir bir kurulum istiyorsanız, nodriver yaptığı iş için alışılmadık derecede temiz.
Küçük disk ayak izi, küçük import anlamına gelmiyor. Baytı düzeltilmiş Python 3.14 kopyasında import nodriver yaklaşık 158 ms sürdü (yeni subprocess import’larında medyan, kabaca 151–199 ms). Yamalanmamış 3.14 import’u başarısız olduğundan, onun için bir zamanlama raporlanmıyor. 57 CDP alan modülünün hepsi eager olarak yükleniyor ve import sonrası resident bellek, henüz hiçbir Chrome süreci olmadan 31.5–31.9 MB idi. Başlatılmış bir tarayıcı bunun çok üstüne çıkar; bu ölçüme dahil edilmedi.
Bu sayılara bağlı iki not. Bunlar tek bir makineden — macOS arm64 — geliyor ve footprint ile import süresi ölçümleri Python 3.14 üzerinde, tek baytlık yamalanmış kopyaya karşı alındı; çünkü yamalanmamış paket bu yorumlayıcıda hiç içe aktarılmıyor. Desteklenen bir Python sürümünde yamaya ihtiyacınız yok: 3.12.13 üzerinde temiz, yamalanmamış import’u doğruladım; tüm tarayıcı ölçümleri de oradan geliyor. Ayrıca çalışma zamanında gerçek bir Chrome, Chromium, Edge ya da Brave ikili dosyası hâlâ gerekiyor — nodriver mevcut bir tarayıcıyı sürer, kendi tarayıcısını paketlemez; yani kurulum maliyeti 17 MB hesabının dışında kalır. Ve ifşa bölümünün gösterdiği gibi, makinede hangisi yüklüyse traffic’inizin açıkladığı sürüm de odur.
Asıl benimseme kararı lisans

Ücretsiz bir araç üzerine yapılan incelemelerin çoğu, “açık kaynak” ifadesini lisans konuşmasının sonu sayar. nodriver için bu başlangıçtır; çünkü lisansı AGPL-3.0 — hem wheel içindeki LICENSE.txt dosyasında hem de depo içindeki spdx_id alanında doğrulandı. Bu, güçlü bir ağ copyleft lisansıdır ve komşularının talep ettiğinden maddi olarak farklı bir taahhüttür.
Çoğu kişinin nodriver ile karşılaştırdığı emsaller permissive lisanslıdır; farkı somutlaştıran da bu:
| Araç | Lisans | Modifiye edilmiş bir kopyayı ağ hizmeti olarak çalıştırırsanız |
|---|---|---|
| nodriver | AGPL-3.0 | 13. bölüm, değiştirilen sürümünüzün karşılık gelen kaynak kodunu uzaktaki kullanıcılara sunmanızı gerektirebilir |
| Playwright | Apache-2.0 | eşdeğer bir network-copyleft hükmü yok |
| Puppeteer | Apache-2.0 | yukarıdaki gibi |
| Botasaurus | MIT | yukarıdaki gibi |
Kapsam önemlidir. AGPL’nin 13. bölümü, uzaktan ağ etkileşimi için kullanılan ve lisans kapsamındaki programın değiştirilmiş bir sürümüne yöneliktir. Bu inceleme, çevredeki ayrı servis kodunun kapsanan çalışmanın parçası olup olmadığına karar vermez; dahili kullanım ya da kurumsal sınır vakalarını da çözmez. Eğer barındırılan ürününüz nodriver’ı değiştiriyorsa, lisans metnini ve mimariyi hukuk danışmanlığıyla birlikte gözden geçirin. Bu, teknik bir benimseme uyarısıdır; hukuki tavsiye değildir.
AGPL iyi mi kötü mü diye ahkâm kesmiyorum — copyleft meşru bir tercihtir ve birçok ciddi proje bunu kullanır. Burada işaret ettiğim şey şu: “nodriver ücretsiz ve açık kaynak” ifadesi doğru ama eksik. Yükümlülük gerçektir, bu ekosistemdeki permissive varsayımdan farklıdır ve kararda yer almalıdır; “ücretsiz” etiketine düzleştirilmemelidir. (İşin ilginci, PyPI hiçbir lisans sınıflandırıcısı sunmuyor; AGPL metni wheel’de, SPDX id ise repoda, dolayısıyla bunu size paket indeksinin göstermesini beklemeyin.)
Tarihlenmiş metadata
GitHub API ve PyPI’dan alınmış anlık depo ve paket sayıları:
| Gerçek | 14 Temmuz 2026’daki değer |
|---|---|
| Yıldız | 4,511 |
| Fork | 422 |
| Açık issue | 14 |
| Oluşturulma | Şubat 2024 |
| Son push | Mayıs 2026 |
| En son PyPI sürümü | 0.50.3 |
| Wheel | saf Python py3-none-any |
requires-python | >=3.9 |
| Python sınıflandırıcıları | 3.7–3.13 |
Gözlemlenebilir bakım sinyalleri karışık: repo Mayıs 2026’da push edilmiş, ancak en son ölçülen paket hâlâ Python 3.14 import problemini içeriyordu ve önerilen düzeltme araştırma tarihinde yayınlanmamıştı. Yıldız ve açık issue sayıları bu ritmin sizin bakım eşiğinizi karşılayıp karşılamadığını tek başına söylemez.
Artılar ve eksiler
Artılar:
- Küçük ve okunabilir kurulum: üç beyan edilmiş bağımlılık artı geçişli
wrapt, ölçülen ortamda altıdist-infodizini boyunca yaklaşık 17.2 MB (bunlardan biripip), numpy/lxml yok ve kurulum sırasında tarayıcı indirilmiyor. - Gerçek anlamda CDP-native: kendi 57 modüllük DevTools Protocol bağlayıcılarını paketliyor ve protokolü doğrudan konuşuyor; arada chromedriver/Selenium ikilisi yok.
Tabüzerinde geniş bir öğe bulma yüzeyi (62 genel yöntem) ve birinci sınıf XPath, CSS ve metin araması — XPath için hamevaluate()seviyesine inmek gerekmiyor.- Tasarım gereği asenkron; her çalıştırmada yeni profil; headless, executable path, args, dil ve port gibi yaygın ayarlar için temiz bir
Configyüzeyi. - JavaScript ile oluşturulmuş bir sayfadan heavy-weight araçlar kadar iyi içerik çekiyor: varsayılan okumada 3 içerik sınıfının 2’si, açık bir bekleme ile 3’ün 3’ü — aynı fixture’da stock Playwright ve Puppeteer ile aynı, üç çalıştırmada da stabil. Gezinme ve okuma süresi 119–129 ms ile dördün en hızlısıydı.
- Varsayılan olarak
navigator.webdriverdeğerinitruedöndüren stock kontrol örneklerinin aksinefalseraporluyor; üstelik özelliği yamalamadan — descriptor tarayıcının kendi yerel getter’ı olarak kalıyor. - undetected-chromedriver’ın CDP-native devamı olarak net bir mimari kimliği var.
Eksiler:
- Python 3.14’te kutudan çıktığı haliyle import olmuyor — tek baytlık UTF-8 dışı dosya (
cdp/network.py) import sırasındaSyntaxErrorfırlatıyor. Açık issue #35 ile yeniden üretildi, 0.50.3’te hâlâ düzeltilmedi. ≤3.13 sürümünü sabitleyin (3.12.13’te temiz doğrulandı) ya da dosyayı yeniden kodlayın. - AGPL-3.0, modifiye edilmiş bir kopyayı ağ hizmeti olarak çalıştıran herkes için gerçek bir benimseme kararıdır — Apache/MIT rakiplerinden daha sıkıdır.
- Küçük disk ayak izi küçük import demek değildir: 57 CDP modülünün hepsi önceden yüklendiği için yaklaşık 158 ms soğuk başlangıç ve yaklaşık 31.5 MB resident bellek var.
tab.select()yarım saniyelik polling ile çalışıyor, bu yüzden kısa beklemeler yukarı yuvarlanıyor — 100 ms bekleme yaklaşık 1.1 s’ye mal oluyor. Her seferinde doğru, ama çok sayıda kısa bekleme için maliyet büyür.- Headless çalışmalarda user-agent varsayılan olarak hâlâ
HeadlessChromeilan ediyor; stock kontrol örnekleriyle birebir aynı, en bariz kimlik belirten dize sizin yerinize gizlenmiyor. - Çalışma zamanında hâlâ gerçek bir Chrome/Chromium/Edge/Brave ikili dosyasına ihtiyaç duyuyor; hafif pip kurulumu hikâyenin yalnızca yarısı ve makinede hangi Chrome varsa traffic’iniz o sürümü açık ediyor.
- Herhangi bir anti-bot sistemine karşı etkinlik burada doğrulanmadı — stealth iddiasının tamamı tasarım gereği test edilmedi.
Neyi test etmediğimi, dolayısıyla ne hakkında konuşamayacağımı da açıkça söyleyeyim: tab başına bellek, CDP round-trip gecikmesi, profil yönetimi, ölçekte throughput, Linux veya Windows, özellikle Python 3.13 ve — en önemlisi — gerçek bir canlı servise karşı anti-bot etkinliği. Bu incelemede tüm tarayıcılar yalnızca 127.0.0.1 üzerindeki bir fixture ile konuştu. Tüm sayılar tek bir makineye ait (macOS arm64); tarayıcı sürüşü ölçümleri yamalanmamış Python 3.12.13’ten, daha eski footprint ve import süresi ölçümleri ise tek baytlık yamalı Python 3.14 kopyasından geliyor.
Ayrıca bir tarayıcı yükseltme matrisi çalıştırmadım; bu yüzden gelecekteki Chrome sürümleriyle uyumluluk, bu incelemenin sonucu değil, benimseyen tarafın operasyonel kontrolü olarak kalıyor.
Kimler için uygun, kimler uzak dursun?
nodriver, canlı bir Chromium için hafif, asenkron, CDP-native bir sürücü istiyorsanız ve tarayıcıyı, güncellemeleri ve çalışma zamanını sahiplenmek sizin için sorun değilse uygundur. Küçük bağımlılık ağacı denetlemeyi kolaylaştırır ve CDP-first tasarım protokol düzeyinde kontrol için uygundur. Konteyner uyumluluğu test edilmedi: bu inceleme Linux imajlarını, tarayıcı kurulumunu, paylaşımlı kütüphaneleri, sandboxing’i veya process cleanup’ı denemedi.
İki grup başka yere bakmalı. Python 3.14 kullanıyor ve yorumlayıcınızı sabitlemek ya da vendored bir dosyayı yamalamak istemiyorsanız, düzeltme yayınlanana kadar bekleyin — import kırılması bugün için kesin bir durma noktası. Ve AGPL-3.0, planladığınız dağıtım biçimi için problemse — özel değişiklikler içeren barındırılan bir servis — daha başlamadan önce permissive lisanslı bir alternatifi değerlendirmek için lisans tek başına yeterli sebep olur. Bunlar kodun kusuru değil; compliance incelemesinde değil, şimdi öğrenmek isteyeceğiniz kısıtlardır.
Ayrıca gerçekten ihtiyacınız olan şey veri ise, elle sürdüğünüz bir tarayıcı değilse, bunu da atlayın. nodriver size scriptlenebilir bir tab ve 62 yöntem verir; render edilmiş bir sayfayı temiz, yapılandırılmış kayıtlara dönüştürmek ise hâlâ sizin kodunuzdur. O başka bir iştir ve managed API tam da o birleşimde devreye girer.
Alternatifler ve Thunderbit’in konumu
Önce dürüst çerçeve: nodriver ücretsiz, AGPL ve self-hosted. Tarayıcıyı siz çalıştırırsınız, güncellemeleri siz yönetirsiniz, çalışma zamanını ve içindeki her hatayı siz sahiplenirsiniz. Tam da bu kontrolü isteyen bir geliştirici için, halihazırda sahip olduğunuz bir kütüphaneyle fiyat açısından yarışabilecek yönetimli bir servis yoktur.
Açık kaynak içinde, marka adına göre değil işe göre kıyaslayın. Gerçek tarayıcı sürücülerini değerlendiriyorsanız, Playwright ve Puppeteer karşılaştırmamız nodriver’ın yanında duran iki bariz ağır topu kapsıyor. Gizlilik/stealth ekseninde Python tarafına en yakın komşu Scrapling olabilir. Ham tarayıcı kontrolü yerine LLM-dostu çıktı istiyorsanız, Crawl4AI sayfaları render edip size Markdown döner; Scrapy ise büyük ölçekli, tarayıcısız taramalar için hâlâ referans çerçevedir. Bunların birkaçı arasında aynı anda seçim yapıyorsanız, açık kaynak scraper özeti kategorileri yan yana koyuyor.
Açıklama: bu makale Thunderbit tarafından yayımlanmıştır. Thunderbit, yönetimli bir çıkarım servisidir; dolayısıyla karşılaştırma mimari değil, görev odaklıdır. nodriver size kendi barındırdığınız ve programladığınız bir tarayıcı kontrol katmanı verir; Thunderbit ise render etmeyi üstlenir ve sayfa içeriğini ya da şema biçimli kayıtları servis olarak döndürür. Protokol düzeyinde tarayıcı kontrolü ve self-hosting sizin için şartsa nodriver’ı kullanın. Çıktı kaydı ve operasyonel devri, tarayıcıyı sahiplenmekten daha önemliyse yönetimli bir çıkarıcıyı değerlendirin. Değişebilir uç nokta, kredi ve toplu işlem limitleri ayrıntıları kütüphane benchmark’ının içinde değil, fiyatlandırma sayfasında yer almalı.
Buradaki takas, işin nereye ait olduğudur: nodriver, tarayıcı yönetimini ve çalışma zamanı bakımını sizde bırakır; talep başına servis ücreti yoktur. Yönetimli bir API ise o katmanı üstlenir ve çağrı başına ücretlendirir.
Web Verisi Çıkarma için Thunderbit’i Deneyin
Sonuç
Küçük, asenkron, CDP-native bir Chromium sürücüsü istiyorsanız; bunu Python sürümünüzde doğruladıysanız (burada 3.12.13 temiz çalıştı); ve AGPL-3.0 lisansını nasıl dağıtacağınızı gözden geçirdiyseniz nodriver’ı kullanın. Çözümlenen çalışma zamanı grafiği dört paket ekledi, ölçülen ortam yaklaşık 17 MB yer kapladı ve kütüphane 62 yöntemli Tab yüzeyi ile yerel XPath içeren 57 CDP alan modülüyle geliyor. Bu fixture’da varsayılan olarak 3 içerik sınıfının 2’sini, bekleme ile 3’ün 3’ünü döndürdü; stock Playwright ve Puppeteer ile eşleşti.
Ama çekinceleri dürüstçe ölçmek gerekir. Python 3.14’te, tek bir UTF-8 dışı baytı düzeltene kadar hiç import olmuyor — belgelenmiş, hâlâ açık bir sorun; gizem değil ama takıldığınız gün için kesin durak. Lisans AGPL-3.0; bu, modifiye edilmiş bir kopyayı servis olarak çalıştıran herkes için gerçek bir karar, formalite değil. Küçük kurulum, küçük import satın almıyor; çünkü CDP modüllerinin hepsi baştan yükleniyor ve select()’in yarım saniyelik polling’i kısa beklemeleri tek tek yaklaşık bir saniyeye çıkarıyor. Varsayılan ifşa konusunda gerçekten cevaplayabildiğim resim pazarlamanın ima ettiğinden daha dar: stock Puppeteer’dan yalnızca bir boolean farklı, headless user-agent hâlâ HeadlessChrome diyor ve ölçtüğüm diğer her özellik dört yığın arasında aynıydı. Ve birçok kişinin nodriver’ı ilk kez aramasına neden olan anti-detection varsayımı — tümünü özellikle test etmeyi seçmediğim şey — hakkında kesin konuşmuyorum. Kütüphaneyi envanterledim ve canlı savunmalara karşı değil, kendi makinemdeki bir sayfaya karşı çalıştırdım; size kanıtlayamayacağım bir bypass vaadi vermektense bunu açıkça söylemeyi tercih ederim. Cevaplayabildiğim sorularda nodriver, iki keskin kenarı olan — bir Python sürüm duvarı ve copyleft lisansı — iyi yapılmış, olağanüstü hafif bir sürücü.
Web Verisi Çıkarma için Thunderbit’i Deneyin Get Started Free
SSS
import nodriver neden Python 3.14’te başarısız oluyor?
Çünkü cdp/network.py, kaynak kodlama bildirimi olmadan UTF-8 dışı bir ± baytı içeriyor. Python 3.14.2 bu dosyayı reddediyor ve zincirleme import’u durduruyor; Python 3.12.13 ise bayt düzeyinde aynı dosyayı yamalanmamış hâliyle içe aktarıyor. Bu inceleme, tam yorumlayıcı değişikliğini izole etmedi ve Python 3.13’ü test etmedi. Yukarı yöndeki iz nodriver issue #35 ve pull request #36. Test ettiğiniz bir sürümü kullanın ya da dosyayı UTF-8’e yeniden kodlayın.
“CDP-native, no webdriver” bana ne kazandırır ve kurulumda ne öderim?
nodriver, 57 DevTools Protocol alan modülünü paketler ve Selenium üzerinden chromedriver çağırmak yerine CDP’yi bir WebSocket üzerinden konuşur. Tab 62 yöntem sunar; buna yerel XPath de dahildir. Paket metadata’sı üç çalışma zamanı gereksinimi belirtir (websockets, mss, deprecated); bunların çözümü wrapt ekler. Ölçülen ortam, pip dahil altı dist-info dizini boyunca yaklaşık 17.2 MB idi; numpy, lxml veya indirilmiş bir tarayıcı ikilisi yoktu. İki not: yamalanmış kopyadaki import süresi yaklaşık 158 ms idi, çünkü 57 CDP modülünün hepsi eager yükleniyor; ayrıca ayrı bir Chrome ailesi tarayıcı hâlâ gerekiyor.
AGPL-3.0 lisansı projem için önemli mi? Nasıl dağıttığınıza bağlı. AGPL-3.0 bir network-copyleft lisansıdır: nodriver’ın değiştirilmiş bir sürümünü başkalarının kullandığı bir servis olarak çalıştırırsanız, o değiştirilmiş kaynağı sunmakla yükümlüsünüz. Kişisel bir script veya dışarı açmadığınız dahili bir araç için bu bir sorun değildir. Yamanmış bir nodriver üzerine kurulu barındırılan ticari bir ürün için ise compliance sahibiyle konuşulması gereken gerçek bir konudur — ve bu, benzer araçlardaki Apache-2.0 ve MIT lisanslarından daha sıkıdır.
nodriver kendisi hakkında ne açığa çıkarıyor — ve bu, Cloudflare’ı yendiği anlamına mı gelir?
İlk yarı ölçülüdür; ikinci yarı değildir ve fark önemlidir. 127.0.0.1 üzerinden sunduğum bir sayfada, kontrol örnekleriyle aynı Chrome derlemesini çalıştırırken: navigator.webdriver değeri false döner; stock Playwright ve stock Puppeteer ise true raporlar. Bu değer, özelliği yamalayarak değil, tarayıcı başlatma zamanında ayarlanır — descriptor hâlâ Chrome’un kendi yerel getter’ıdır. Bu boolean dışında neredeyse her şey kontrol örnekleriyle birebir eşleşti: aynı platform dizesi, beş plugin, on iki çekirdek, 16 GB bildirilen cihaz belleği, aynı window.chrome yapısı, Permissions API çelişkisi yok ve document ya da window üzerinde cdc_ tarzı kalıntılar yok. Headless çalışmalarda user-agent hâlâ HeadlessChrome/151.0.0.0 diye ilan ediliyor; hem de iki kontrol örneğiyle aynı biçimde — bu sizin için maskelenmiyor. Bunların hiçbiri, gerçek bir anti-bot servisine karşı işe yarayıp yaramadığını söylemez. nodriver’ı hiçbir zaman canlı bir siteye yöneltmedim, hiçbir anti-bot servisine bağlanmadım ve CAPTCHA ile hiç uğraşmadım; bu bilinçli olarak kapsam dışıydı. Yukarıdaki ifşa tablosu, yığının ne açıkladığını söyler. Kimlerin dinlediğini ya da buna ne yaptığını söylemez.
nodriver JavaScript ile render edilen içeriği doğru işler mi?
Evet, ama her zamanki uyarıyla: ne zaman okuduğunuz önemlidir. Üç içerik sınıflı bir fixture’da, varsayılan browser.get() + tab.get_content() 3’ün 2’sini döndürdü — JavaScript’i doğru çalıştırıyor (senkron enjekte edilen sınıf, servis edilen baytların hiçbir yerinde yokken geri geldi), fakat yükleme olayında okuma yaptığı için sonradan enjekte edilenleri kaçırıyor. tab.select("#delayed-injected") eklediğinizde 3’ün 3’ünü alırsınız. Aynı sayfada stock Playwright ve stock Puppeteer ile birebir aynı. Bir tuhaflık için bütçe ayırın: select() yarım saniyelik back-off ile polling yapıyor, bu yüzden 100 ms bekleme yaklaşık 1.1 saniyeye mal oluyor.


