Ulasan nodriver: Driver CDP Ringan yang Gagal Diimpor di Python 3.14

Terakhir diperbarui pada August 18, 2026
Ulasan nodriver: Driver CDP Ringan yang Gagal Diimpor di Python 3.14
Ringkasan AI
nodriver adalah library otomasi browser Python dari ultrafunkamsterdam, kreator di balik undetected-chromedriver, dan diposisikan sebagai penerus proyek tersebut. Perbedaan arsitekturnya jelas dari Selenium dan chromedriver: nodriver berkomunikasi dengan Chromium melalui Chrome DevTools Protocol (CDP), tanpa binary WebDriver di tengah, dan API-nya async. Playwright dan Puppeteer berada dalam kelas pembanding yang berbeda. Keduanya juga pengendali browser berbasis protokol, bukan turunan WebDriver; perbedaan yang paling relevan ada pada desain API, packaging, penyediaan browser, dan kebijakan kompatibilitas. Ulasan ini menelusuri nodriver 0.50.3, bukan mengujinya terhadap pertahanan aktif di dunia nyata.

nodriver adalah library otomasi browser untuk Python dari ultrafunkamsterdam, kreator di balik undetected-chromedriver, dan diposisikan sebagai penerus proyek tersebut. Perbedaan arsitekturnya jelas dari Selenium dan chromedriver: nodriver berkomunikasi dengan Chromium melalui Chrome DevTools Protocol (CDP), tanpa binary WebDriver di tengah, dan API-nya bersifat async. Playwright dan Puppeteer berada dalam kelas pembanding yang berbeda. Keduanya juga pengendali browser berbasis protokol, bukan turunan WebDriver; perbedaan yang paling relevan ada pada desain API, packaging, penyediaan browser, dan kebijakan kompatibilitas.

Ulasan ini menelusuri nodriver 0.50.3, bukan mengujinya terhadap pertahanan aktif di dunia nyata. Saya mengukur paket yang terpasang, proses import, permukaan API, ukuran di disk, dan lisensi, lalu menjalankan browser hanya ke halaman yang disajikan dari 127.0.0.1. Tidak ada target live, layanan anti-bot, atau CAPTCHA yang disentuh. Hasilnya menjelaskan perilaku paket dan disclosure browser bawaan; bukan bukti efektivitas anti-deteksi.

Ada tiga hal yang paling menonjol dalam batasan itu. Pertama, di Python 3.14 library ini sama sekali tidak bisa di-import — satu byte nyasar sudah cukup menjatuhkan seluruh paket sebelum Anda sempat memanggil apa pun. Kedua, untuk driver di kategori ini, ukurannya sangat kecil: tiga dependency langsung plus satu dependency transitif yang ter-resolve, dan sekitar 17 MB di lingkungan pengujian. Ketiga, di halaman milik saya sendiri, satu-satunya properti yang tampak berbeda antara nodriver, stock Playwright, dan stock Puppeteer hanyalah satu boolean — sementara string user-agent headless tetap menampilkan HeadlessChrome pada ketiganya. Selebihnya, kejutan terbesar ada di lisensi.

Kerusakan import di Python 3.14

Mulai dari temuan yang paling dulu akan menggigit Anda, karena terjadi sebelum kode Anda sendiri sempat berjalan. Di Python 3.14, import nodriver biasa langsung gagal:

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

File yang gagal adalah cdp/network.py yang digenerate otomatis (header-nya berbunyi # DO NOT EDIT THIS FILE!). File ini memuat satu byte non-UTF-8, 0xb1, untuk karakter ± pada komentar #: JSON (±Inf)., dan tidak memiliki deklarasi encoding sumber. Modul dimuat melalui alur nodriver/__init__ → cdp/__init__ → network, jadi kegagalan parse langsung membatalkan import. Pemindaian paket tidak menemukan file sumber non-UTF-8 lainnya.

Batas versi perlu dirumuskan dengan hati-hati. Python 3.14.2 menolak file tersebut, sedangkan Python 3.12.13 bisa mengimpornya tanpa patch. File network.py yang terpasang identik byte demi byte di kedua lingkungan itu (SHA-256 ef755f41800d4efb593736f8b55b331bba68eec373ed62ece433e66e4b491cd6), jadi perbedaan artifact sumber bukan penyebabnya. Ulasan ini tidak mengisolasi perubahan tokenizer CPython yang tepat, dan Python 3.13 juga tidak diuji. Jadi yang dilaporkan di sini adalah dua hasil pengukuran interpreter yang memang diuji, bukan klaim bahwa semua Python yang lebih lama pasti berperilaku seperti 3.12.

Tidak ada kesimpulan tentang Python 3.13 yang bisa ditarik dari dua titik ujung pengukuran itu.

Ini adalah reproduksi, bukan penemuan baru. Traceback Python 3.14 yang sama tercatat di issue nodriver #35, dengan perbaikan yang diusulkan di pull request #36. Versi 0.50.3 masih mengandung byte tersebut. Metadata PyPI mencantumkan classifier hingga Python 3.13 dan tidak mengklaim dukungan 3.14.

Solusinya sesederhana bug-nya: gunakan versi interpreter yang sudah teruji, atau encode ulang file itu ke UTF-8, seperti yang diusulkan pada pull request #36. Setelah re-encode, paket bisa di-import dan diinspeksi di 3.14 tanpa hambatan kedua yang muncul. Run bersih di 3.12.13 di bawah ini memverifikasi satu jalur yang tidak dipatch; Python 3.13 tidak diuji dalam ulasan ini.

Di Python 3.12.13, import nodriver berhasil tanpa patch apa pun. Semua angka yang berhubungan dengan browser di bagian selanjutnya berasal dari instalasi itu. Angka footprint dan timing import memakai salinan 3.14 yang byte-nya sudah di-encode ulang dan diberi label sesuai itu. Python 3.13 tetap tidak diuji di sini; satu hasil di 3.12 tidak berarti cakupan untuk seluruh rentang sebelumnya.

Kalau Anda terbiasa memakai interpreter modern secara default — dan banyak tim memang cepat pindah ke Python baru — ini adalah penghalang nyata, meski sangat mudah diperbaiki. Ketahui keberadaannya supaya Anda tidak kehilangan setengah hari hanya karena SyntaxError di file yang bahkan tidak pernah Anda tulis.

Sebenarnya nodriver itu apa, dari sisi internal

Deskripsi satu barisnya ("CDP-native, no webdriver") terdengar seperti jargon pemasaran sampai Anda melihat isi wheel-nya. nodriver membundel sendiri binding DevTools Protocol lengkap: paket nodriver.cdp berisi 57 modul domain protokol — accessibility, dom, network, page, fetch, runtime, target, storage, input, emulation, dan seterusnya. Jumlah modul ini adalah mekanisme di balik klaim "langsung ke CDP, tanpa Selenium". Alih-alih memanggil executable chromedriver yang berbicara WebDriver dan menerjemahkan untuk Anda, nodriver menghasilkan objek Python untuk domain-domain CDP dan berbicara protokol itu sendiri lewat WebSocket. cdp/network.py — file yang mengandung byte bermasalah tadi — adalah salah satu dari 57 modul auto-generated itu, sebab itulah kerusakan ini hidup di kode yang tidak pernah diedit manual.

Di atas lapisan protokol itu ada model objek yang lebih ramah. Objek Tab mengekspos 62 metode publik, dan permukaan pencarian elemennya lebih luas daripada kebanyakan driver: pencocokan teks lewat find() dan find_all(), CSS lewat select() dan select_all(), serta entry point xpath() bawaan. XPath, CSS, dan pencarian teks di objek yang sama memang sangat praktis; library lain kadang memaksa Anda turun ke evaluate() untuk XPath. Konstruktor Config menampilkan user_data_dir, headless, browser_executable_path, browser_args, sandbox, lang (default 'en-US'), host, port, expert, plus **kwargs. Saat inventarisasi API ini, membuat Config(headless=True) menghasilkan 16 flag peluncuran Chromium, termasuk --no-first-run, --no-default-browser-check, --remote-allow-origins=*, dan --homepage=about:blank. Saya tidak memanggil start() pada tahap introspeksi ini. Pengujian browser di bawah dilakukan dalam run terpisah.

System diagram: Direct CDP Control Path

Library ini memang menyediakan permukaan API yang berorientasi anti-deteksi — metode-metode yang keberadaannya saya konfirmasi, tetapi perilakunya tidak saya uji terhadap target apa pun. Saya sebutkan itu sekali saja lalu lanjut, karena efeknya terhadap layanan nyata justru adalah hal yang sengaja tidak saya uji. Secara netral, yang bisa saya katakan: penamaan nodriver lebih tertahan dibanding beberapa pesaing yang mengusung stealth. Ceritanya lebih arsitektural — CDP-native, profile baru tiap run — daripada daftar metode dengan nama seperti detect_and_bypass. Anggap itu observasi desain API, bukan klaim hasil.

Semua hitungan itu berasal dari import paket dan introspeksi Python sendiri — inspect, walking modul, penghitungan atribut. Tidak ada situs yang disentuh. Kalau Anda ingin mengulangnya, angkanya langsung keluar dari paket; bukan sekadar impresi.

Apa yang diumumkan browser tentang dirinya sendiri

Measured results chart: Browser disclosure fields in the tested stacks

Berikut pertanyaan yang bisa dijawab tanpa mendekati sistem pertahanan mana pun: saat nodriver menggerakkan browser, apa yang browser itu sukarela bocorkan ke halaman yang sedang dibukanya? Saya membuat halaman yang membaca hal-hal paling jelas — navigator.webdriver, user-agent, platform, languages, jumlah plugin dan hardware, bentuk window.chrome, apa kata Permissions API, geometri window dan screen — lalu menyajikannya dari 127.0.0.1, dan mengarahkannya ke empat stack: nodriver, Botasaurus, serta stock Playwright dan stock Puppeteer sebagai kontrol. Keempatnya memakai build Chrome yang sama (Chrome for Testing 151.0.7922.10), jadi bila ada perbedaan, itu berasal dari library-nya, bukan browser-nya. Headless dan headed, masing-masing tiga run. Semua nilai di bawah ini konsisten di ketiga run tersebut.

StackModenavigator.webdriverToken user-agentnavigator.languages
nodriver 0.50.3headlessfalseHeadlessChrome/151.0.0.0["en-US"]
nodriver 0.50.3headedfalseChrome/151.0.0.0["en-US"]
Botasaurus 4.0.92headless / headedfalseHeadlessChrome/151 / Chrome/151["en-US"]
Playwright 1.56.0headless / headedtrueHeadlessChrome/151 / Chrome/151["en-US","en"]
Puppeteer 24.16.0headless / headedtrueHeadlessChrome/151 / Chrome/151["en-US"]

Perbedaan yang paling jelas adalah satu boolean itu. Dengan konfigurasi peluncuran default tersebut, nodriver melaporkan navigator.webdriver sebagai false, sementara stock Playwright dan Puppeteer melaporkan true, baik pada run headless maupun headed. Pengujian ini menunjukkan perbedaan di level konfigurasi; tidak membuktikan bahwa nilai itu semata-mata muncul karena tidak adanya binary WebDriver.

Di keempat stack, properti itu tetap getter native browser di Navigator.prototype — function get webdriver() { [native code] } — bukan properti milik instance atau fungsi yang diganti. Properti tersebut tidak ditulis ulang oleh JavaScript halaman setelah load. Pengujian ini tidak menginventarisasi argumen peluncuran atau jalur sumber yang menyebabkan nilai berbeda.

Detail kedua adalah yang menahan klaim pemasaran tadi. Dalam mode headless, user-agent nodriver tetap menyatakan HeadlessChrome/151.0.0.0 — sama persis dengan stock Puppeteer, sama persis dengan stock Playwright. Saat dijalankan headed, ia berubah menjadi Chrome/151.0.0.0, juga identik. Kalau Anda mengira library anti-deteksi akan menyamarkan string pengenal diri yang paling terkenal dalam otomasi browser secara default, ternyata tidak. Itu harus Anda set sendiri.

Hampir semuanya sama di keempat stack, dan itu penting untuk dikatakan terang-terangan karena mempersempit ceritanya — setiap properti di bawah ini terbaca sama pada nodriver, Botasaurus, Playwright, dan Puppeteer:

PropertiNilai, identik di keempat stack
platformMacIntel
vendorGoogle Inc.
Pluginslima
MIME typesdua
pdfViewerEnabledtrue
Logical coresdua belas
Reported device memory16 GB
Touch pointsnol
window.chromeada, dengan app/csi/loadTimes dan tanpa runtime
String renderer WebGLidentik di keempat stack

Kasus klasik ketika Permissions API dan Notification.permission saling bertentangan tidak muncul di mana pun — keempatnya sama-sama melaporkan default dan prompt secara konsisten. Saya juga memindai document dan window untuk sisa-sisa gaya cdc_ yang dulu dikenal dari stack WebDriver lama: kosong di keempatnya.

Satu-satunya tempat di mana nodriver terlihat lebih seperti browser otomatis mentah dibanding kontrol lain adalah geometri window. Headless nodriver melaporkan outerWidth/outerHeight sebesar 0×0 pada layar 800×600; headless Playwright melaporkan 1280×720 karena ia mengatur viewport untuk Anda. Puppeteer berada di 0×0 seperti nodriver. Itu beda konfigurasi default, bukan beda kemampuan, dan bisa Anda ubah.

Satu hal lagi yang penting diketahui sebelum deployment: nodriver tidak menyertakan browser, jadi secara default ia memakai Chrome yang memang sudah terpasang di mesin Anda. Di mesin saya, itu berarti ia otomatis mendeteksi /Applications/Google Chrome.app — Chrome 150.0.7871.187 — dan user-agent yang diungkapkan mengikuti versi itu, bukan versi yang dipin. Versi browser yang terlihat oleh sistem tujuan adalah versi apa pun yang kebetulan terpasang di fleet Anda.

Sebut saja ini sebagaimana adanya. Ini adalah catatan tentang apa yang diungkapkan stack otomatis ketika tidak ada yang memintanya bersembunyi — berguna bagi sisi defensif, berguna juga kalau Anda ingin tahu apa yang dipancarkan tooling Anda sendiri. Ini bukan ukuran apakah semua itu penting bagi layanan tertentu. Saya tidak menguji hal itu, dan tidak satu pun baris di atas boleh dibaca sebagai implikasi hasil.

Apakah ia benar-benar bisa mengambil konten dari halaman?

Mengenalkan diri itu satu hal; mengembalikan HTML yang tepat adalah tugasnya. Saya menjalankan nodriver terhadap fixture tiga kelas konten yang sama seperti yang dipakai benchmark repo ini, sehingga angka-angkanya sejalan dengan semua tool lain yang diukur di sini. Halamannya memuat tiga hal: A, link statis dengan penanda literal di bytes yang disajikan; B, node yang dibangun oleh inline script saat parsing, dengan penanda dan URL dirangkai dari potongan-potongan sehingga hanya dengan menjalankan JavaScript Anda bisa melihatnya; dan C, node yang disuntikkan 800 ms setelah event load, dengan perakitan yang sama. Kelas C adalah yang adversarial — pembacaan yang dilakukan pada event load tidak akan melihatnya.

StackPembacaan defaultDengan explicit wait
nodriver 0.50.32 dari 3 (A + B, gagal melihat C)3 dari 3
Botasaurus 4.0.922 dari 33 dari 3
Playwright 1.56.02 dari 33 dari 3
Puppeteer 24.16.02 dari 33 dari 3

nodriver jatuh tepat di tempat yang sama dengan para raksasa itu. browser.get() lalu langsung tab.get_content() adalah snapshot pada saat load: ia merender JavaScript dengan benar — kelas B membuktikannya, karena kelas B tidak ada di bytes yang disajikan — tetapi ia melewatkan apa pun yang disuntikkan setelah load. Tambahkan tab.select("#delayed-injected") dan Anda mendapatkan ketiganya. Jebakan sama, perbaikan sama, seperti Playwright dan Puppeteer. Stabil di tiga pengulangan dan tiga run terpisah seluruh suite, tanpa flake.

Saya menyapu delay injeksi untuk mencari batas kapan pembacaan default menyerah. nodriver berhenti melihat kelas C begitu injeksi masuk 100 ms atau lebih setelah load — batas yang sama seperti kedua kontrol stock. (Botasaurus adalah outlier di sini, dan justru itu perbedaan yang paling menarik antara dua library anti-detect: get()-nya menunggu hingga page load lengkap secara default, jadi pembacaan default masih menangkap injeksi pada 300 ms. Ia membayar sekitar 250 ms per navigasi untuk itu.)

Wait-nya sendiri punya keanehan yang layak dianggarkan. Pada sweep ini, tab.select() milik nodriver mendarat dalam bucket polling kasar, bukan melacak delay injeksi secara rapat:

Delay kelas C0 ms100 ms400 ms800 ms1500 ms
nodriver select()124–152 ms1132–11411128–11292132–21772138–2150
Puppeteer waitForSelector113–129 ms203–216512–516911–9191608–1611

Dalam fixture ini, injeksi 100 ms berujung pada sekitar 1,1 detik untuk select(). Loop yang terpasang menjalankan await self lalu await self.sleep(0.5) setelah miss; siklus gabungan yang terukur jatuh mendekati satu detik di sini, tetapi await self tidak ditetapkan sebagai sleep fixed-duration universal. Semua node tertunda yang diuji berhasil ditemukan. Wait milik Puppeteer mengikuti delay ini lebih dekat. Banyak wait serial bisa memperbesar selisihnya, walau ulasan ini tidak membenchmark halaman produksi dengan tiga puluh selector.

Peluncuran browser adalah titik lain tempat narasi async dan ramping bertemu kenyataan. Menyalakan browser menempatkan nodriver di kisaran yang sama dengan Botasaurus dan Puppeteer, dan jauh di belakang Playwright:

StackPeluncuran browser, lintas run
nodriver 0.50.3910–1583 ms
Botasaurus 4.0.92986–1151 ms
Puppeteer 24.16.0969–1008 ms
Playwright 1.56.0282–365 ms

Setelah berjalan, navigate-and-read milik nodriver adalah yang tercepat dari keempatnya, di 119–129 ms. Ringan saat mengemudi, biasa saja saat menyalakan mesin.

Install dan footprint: bagian yang benar-benar bagus

Di sinilah nodriver layak mendapat label "fokus", dan ini adalah fakta install yang bersih dan bisa diverifikasi, bukan soal scraping. Inilah yang di-resolve oleh pip install nodriver yang bersih:

Fakta installnodriver 0.50.3
Direct runtime dependencies yang dideklarasikantiga — websockets, mss, deprecated
Transitive dependency yang ter-resolvewrapt (melalui deprecated)
Total site-packages yang terukursekitar 17,2 MB di 6 direktori dist-info, termasuk pip pada environment
Porsi milik nodriver sendiri3,7 MB
numpy / lxmltidak ada
Binary browser saat installtidak ada yang diunduh

Untuk kategori yang sering menyeret rendering stack ke mana-mana, ini termasuk ringan.

Perbedaan antara paket langsung dan paket transitif penting untuk maintenance. Metadata nodriver meminta mss, websockets, dan deprecated; wrapt datang karena deprecated membutuhkannya. Direktori dist-info keenam di lingkungan 17,2 MB itu adalah pip, yang sudah ada di virtual environment tersebut. Jadi "17,2 MB di enam distribusi" menggambarkan lingkungan terukur, sedangkan "empat paket runtime tambahan" menggambarkan hasil resolve install. Keduanya saling terkait, tapi tidak saling menggantikan.

Kontras yang membuat angka ini terasa berarti: di mesin yang sama, framework saudara Botasaurus memakan 122,3 MB di 44 paket — kira-kira tujuh kali footprint disk-nya. Itulah bedanya antara driver CDP yang fokus dan framework lengkap, dan perbedaannya dua arah. nodriver memberi Anda tree dependency yang tipis dan mudah dibaca, yang benar-benar bisa diaudit; Botasaurus memberi Anda lebih banyak fitur bawaan dan membayar dengan ruang disk serta permukaan dependency. Tidak ada yang "lebih baik" secara abstrak — tergantung apakah Anda ingin driver atau framework — tetapi jika Anda menghargai install yang kecil dan mudah diperiksa, nodriver sangat rapi untuk apa yang dikerjakannya.

Footprint disk yang kecil tidak berarti import yang kecil. Pada salinan Python 3.14 yang byte-nya sudah diperbaiki, import nodriver memakan sekitar 158 ms (median dari import subprocess baru, sekitar 151–199 ms). Import 3.14 yang belum dipatch gagal, jadi tidak ada timing yang dilaporkan untuk itu. Semua 57 modul domain CDP dimuat secara eager, dan resident memory setelah import adalah 31,5–31,9 MB sebelum ada proses Chrome apa pun. Browser yang diluncurkan tentu akan menambah jauh lebih besar dan tidak dihitung dalam pengukuran memori ini.

Dua catatan tempelan untuk angka-angka itu. Angka-angka ini berasal dari satu mesin — macOS arm64 — dan footprint serta timing import diambil di Python 3.14 pada salinan yang byte-nya di-patch, karena paket yang belum dipatch memang tidak bisa di-import di interpreter itu. Pada Python yang didukung Anda tidak perlu patch: saya mengonfirmasi import bersih tanpa patch di 3.12.13, yang juga menjadi sumber semua pengukuran browser. Dan Anda tetap memerlukan binary Chrome, Chromium, Edge, atau Brave yang nyata saat runtime — nodriver mengendalikan browser yang sudah ada, bukan membundelnya — jadi biaya install browser itu berada di luar 17 MB, dan seperti yang ditunjukkan di bagian disclosure, versi Chrome yang terpasang di mesin Anda adalah versi yang akan diumumkan trafik Anda.

Lisensi adalah keputusan adopsi yang sesungguhnya

System diagram: The license is the real adoption decision

Kebanyakan ulasan tools gratis menganggap "open source" sebagai penutup pembicaraan lisensi. Untuk nodriver, justru itu awalnya, karena lisensinya adalah AGPL-3.0 — dikonfirmasi baik di LICENSE.txt pada wheel maupun di spdx_id repo. Ini adalah lisensi strong network-copyleft, dan komitmennya jauh berbeda dari apa yang diminta tetangga-tetangganya.

Peer yang paling sering dibandingkan dengan nodriver umumnya bersifat permissive, dan itu membuat kontrasnya sangat nyata:

ToolLisensiJika Anda menjalankan salinan yang dimodifikasi sebagai layanan jaringan
nodriverAGPL-3.0Section 13 dapat mewajibkan operator menawarkan source yang sesuai dari versi modifikasinya kepada pengguna jarak jauh
PlaywrightApache-2.0tidak ada klausul network-copyleft yang setara
PuppeteerApache-2.0sama seperti di atas
BotasaurusMITsama seperti di atas

Ruang lingkupnya penting. AGPL Section 13 menyasar versi yang dimodifikasi dari program yang tercakup ketika dipakai untuk interaksi jaringan jarak jauh. Ulasan ini tidak memutuskan apakah kode layanan di sekitarnya termasuk bagian dari karya yang tercakup, dan juga tidak menyelesaikan kasus tepi penggunaan internal atau batas korporat. Jika produk hosted Anda memodifikasi nodriver, tinjau teks lisensi dan arsitekturnya bersama penasihat hukum. Ini adalah penanda adopsi teknis, bukan nasihat hukum.

Saya tidak sedang mengomentari apakah AGPL itu baik atau buruk — copyleft adalah pilihan yang sah dan banyak proyek serius menggunakannya. Yang saya tandai adalah bahwa "nodriver gratis dan open source" itu benar, tetapi tidak lengkap. Kewajibannya nyata, berbeda dari default permissive di bagian ekosistem ini, dan harus masuk ke dalam keputusan, bukan disederhanakan menjadi "gratis". (Sebagai catatan, PyPI tidak mencantumkan classifier lisensi apa pun; teks AGPL ada di wheel dan id SPDX ada di repo, jadi jangan mengandalkan package index untuk menampilkannya.)

Metadata, dengan tanggal

Angka repo dan paket pada titik waktu tertentu, langsung dari GitHub API dan PyPI:

FaktaNilai pada 14 Juli 2026
Stars4.511
Forks422
Open issues14
DibuatFebruari 2024
Terakhir pushMei 2026
Rilis PyPI terbaru0.50.3
Wheelpure-Python py3-none-any
requires-python>=3.9
Python classifiers3.7–3.13

Sinyal maintenance yang bisa diamati campur-aduk: repo mendapat push pada Mei 2026, sementara paket terbaru yang saya ukur masih mengandung masalah import Python 3.14 dan perbaikan yang diusulkan belum dirilis pada tanggal riset. Jumlah stars dan open issues tidak otomatis menentukan apakah ritme itu memenuhi ambang maintenance Anda.

Kelebihan dan kekurangan

Kelebihan:

  • Install kecil dan mudah dibaca: tiga dependency yang dideklarasikan plus wrapt transitif, sekitar 17,2 MB di enam direktori dist-info pada lingkungan terukur (satu di antaranya pip), tanpa numpy/lxml, dan tanpa download browser saat install.
  • Benar-benar CDP-native: membundel 57 binding DevTools Protocol miliknya sendiri dan berbicara protokol secara langsung, tanpa binary chromedriver/Selenium di jalurnya.
  • Permukaan pencarian elemen di Tab luas (62 metode publik) dengan XPath, CSS, dan pencarian teks kelas satu — tidak perlu turun ke evaluate() mentah untuk XPath.
  • Async by design, profile baru per run, dan surface Config yang bersih untuk knob umum (headless, executable path, args, lang, port).
  • Mengambil konten dari halaman yang dibangun JavaScript sebaik para heavyweight: 2 dari 3 kelas konten pada default read, 3 dari 3 dengan explicit wait — identik dengan stock Playwright dan Puppeteer pada fixture yang sama, stabil di tiga run. Navigate-and-read juga yang tercepat di antara keempatnya, 119–129 ms.
  • Melaporkan navigator.webdriver sebagai false secara default saat kedua kontrol stock melaporkan true, tanpa mempatch properti — descriptor-nya tetap getter native browser.
  • Identitas arsitektur yang jelas sebagai penerus CDP-native dari undetected-chromedriver.

Kekurangan:

  • Tidak bisa di-import di Python 3.14 out of the box — satu byte non-UTF-8 di cdp/network.py memicu SyntaxError saat import. Sudah direproduksi terhadap issue terbuka #35, dan masih belum diperbaiki di 0.50.3. Pin ke ≤3.13 (teruji bersih di 3.12.13) atau re-encode file-nya.
  • AGPL-3.0 adalah pertimbangan adopsi yang nyata untuk siapa pun yang menjalankan salinan modifikasi sebagai layanan jaringan — lebih ketat daripada peer Apache/MIT.
  • Footprint disk yang kecil tidak berarti import yang kecil: cold-start sekitar 158 ms dan resident sekitar 31,5 MB sebelum browser apa pun ada, karena semua 57 modul CDP dimuat secara eager.
  • tab.select() melakukan polling dengan back-off setengah detik, jadi wait singkat akan dibulatkan ke atas — wait 100 ms bisa memakan sekitar 1,1 detik, sementara waiter milik Puppeteer sekitar 210 ms. Tetap akurat, tapi biaya ini menumpuk kalau wait kecil jumlahnya banyak.
  • Run headless masih mengumumkan HeadlessChrome di user-agent secara default, persis seperti kontrol stock; tidak ada yang disamarkan dari string pengenal diri yang paling jelas.
  • Tetap memerlukan binary Chrome/Chromium/Edge/Brave sungguhan saat runtime; install pip yang ringan itu baru separuh cerita dependency, dan Chrome yang terpasang di mesin Anda adalah versi yang akan diungkap trafik Anda.
  • Efektivitas terhadap sistem anti-bot mana pun tidak diverifikasi di sini — seluruh premis stealth memang sengaja tidak diuji.

Yang tidak saya uji, dan karena itu tidak bisa saya komentari: memori per tab, latency round-trip CDP, handling profile, throughput skala besar, Linux atau Windows, khusus Python 3.13, dan — yang paling penting — efektivitas anti-bot dunia nyata terhadap layanan live apa pun. Semua browser dalam ulasan ini hanya berbicara ke fixture di 127.0.0.1. Semua angka berasal dari satu mesin (macOS arm64); angka driving browser berasal dari Python 3.12.13 tanpa patch, sedangkan angka footprint dan timing import yang lebih lama berasal dari Python 3.14 pada salinan yang byte-nya dipatch.

Saya juga tidak menjalankan matriks upgrade browser, jadi kompatibilitas terhadap rilis Chrome mendatang tetap menjadi pemeriksaan operasional bagi adopter, bukan hasil dari ulasan ini.

Cocok untuk siapa, dan siapa yang sebaiknya melewatkannya

nodriver cocok jika Anda menginginkan driver Chromium yang ramping, async, dan CDP-native, serta siap memikul browser, update, dan runtime-nya sendiri. Tree dependency yang kecil lebih mudah diaudit, dan desain CDP-first cocok untuk kontrol di level protokol. Kecocokan untuk container tetap belum diuji: ulasan ini tidak menyentuh image Linux, instalasi browser, shared library, sandboxing, atau cleanup proses.

Ada dua kelompok yang sebaiknya mencari opsi lain. Jika Anda memakai Python 3.14 dan tidak mau pin interpreter atau mempatch file vendored, tunggu sampai fix-nya rilis — kerusakan import ini adalah hard stop hari ini. Dan jika AGPL-3.0 menjadi masalah untuk cara Anda ingin merilis produk — misalnya layanan hosted dengan modifikasi privat — lisensi itu sendiri sudah cukup menjadi alasan untuk mempertimbangkan alternatif berlisensi permisif sebelum membangun di atas ini. Keduanya bukan kritik terhadap kodenya; keduanya adalah batasan yang lebih baik diketahui sekarang daripada saat review kepatuhan.

Juga, lewatkan saja kalau yang sebenarnya Anda butuhkan adalah data, bukan browser yang Anda kendalikan secara manual. nodriver memberi Anda tab yang bisa diprogram dan 62 metode; mengubah halaman hasil render menjadi record yang bersih dan terstruktur tetap menjadi pekerjaan kode Anda. Itu pekerjaan yang berbeda, dan di sanalah API terkelola masuk.

Alternatif, dan posisi Thunderbit

Pertama, framing yang jujur: nodriver gratis, AGPL, dan self-hosted. Anda menjalankan browser, mengelola update, dan bertanggung jawab atas runtime beserta setiap kegagalannya. Untuk developer yang memang menginginkan kontrol seperti itu, tidak ada layanan terkelola yang bisa menandingi harga library yang sudah Anda miliki.

Di ranah open source, bandingkan berdasarkan pekerjaan, bukan logo. Jika Anda sedang menimbang driver browser sungguhan, perbandingan Playwright dan Puppeteer kami membahas dua heavyweight yang paling jelas berdiri di sebelah nodriver. Scrapling adalah tetangga Python paling dekat di jalur stealth-oriented jika itu alasan Anda mencarinya. Untuk output yang siap LLM, bukan kontrol browser mentah, Crawl4AI merender halaman dan mengembalikan Markdown, sementara Scrapy tetap menjadi framework andalan untuk crawl besar tanpa browser. Kalau Anda membandingkan beberapa di antaranya sekaligus, roundup open-source scraper menaruh kategorinya berdampingan.

Pengungkapan: artikel ini diterbitkan oleh Thunderbit. Thunderbit adalah layanan ekstraksi terkelola, jadi perbandingannya berbasis tugas, bukan arsitektur. nodriver memberi Anda lapisan kontrol browser yang Anda host dan program sendiri; Thunderbit menangani rendering dan mengembalikan konten halaman atau record dengan skema tertentu sebagai layanan. Gunakan nodriver saat kontrol browser di level protokol dan self-hosting memang menjadi syarat. Pertimbangkan extractor terkelola ketika record output dan handoff operasional lebih penting daripada memiliki browser-nya. Detail endpoint yang bisa berubah, kredit, dan batas batch sebaiknya dilihat di halaman pricing, bukan di benchmark library.

Trade-off-nya ada pada di mana pekerjaan hidup: nodriver menempatkan manajemen browser dan pemeliharaan runtime di sisi Anda tanpa biaya layanan per request; API terkelola mengambil alih lapisan itu dan menagih untuk call-nya.

Coba Thunderbit untuk Ekstraksi Data Web

Putusan

Pilih nodriver jika Anda menginginkan driver Chromium yang kecil, async, dan CDP-native, sudah memverifikasinya pada versi Python Anda (di sini 3.12.13 berhasil di-import bersih), dan sudah meninjau AGPL-3.0 untuk cara distribusi Anda. Graph runtime ter-resolve-nya menambah empat paket, lingkungan terukur memakai sekitar 17 MB, dan library ini membawa 57 modul domain CDP dengan permukaan Tab 62 metode serta XPath native. Pada fixture ini, ia mengambil 2 dari 3 kelas konten secara default dan 3 dari 3 dengan wait, menyamai stock Playwright dan Puppeteer.

Tetapi, ukuran kewajibannya harus dihitung jujur. Di Python 3.14, paket ini sama sekali tidak bisa di-import sampai Anda memperbaiki satu byte non-UTF-8 — issue yang terdokumentasi dan masih terbuka, bukan misteri, tetapi hard stop saat Anda menginjaknya. Lisensinya AGPL-3.0, yang merupakan keputusan nyata bagi siapa pun yang menjalankan salinan modifikasi sebagai layanan, bukan formalitas. Install yang kecil tidak otomatis berarti import yang kecil, karena semua modul CDP itu dimuat di awal, dan polling setengah detik pada select() membuat wait singkat memakan waktu sekitar satu detik per kasus. Pada pertanyaan disclosure default yang benar-benar bisa saya jawab, gambarnya lebih sempit daripada klaim marketing: hanya satu boolean yang berbeda dari stock Puppeteer, user-agent headless tetap mengatakan HeadlessChrome, dan semua properti lain yang saya ukur identik di keempat stack. Dan seluruh premis anti-deteksi — alasan banyak orang menemukan nodriver sejak awal — adalah sesuatu yang sengaja tidak saya uji. Saya menginventarisasi library ini dan menjalankannya ke halaman di mesin saya sendiri, bukan menantangnya dengan pertahanan live, dan saya lebih memilih mengatakan itu secara gamblang daripada memberi Anda klaim bypass yang tidak bisa saya pertanggungjawabkan. Pada pertanyaan yang bisa saya jawab, nodriver adalah driver yang dibangun dengan baik, sangat hemat, dengan dua tepi tajam — tembok versi Python dan lisensi copyleft — yang sebaiknya Anda lihat dari jauh sebelum terlanjur terkena.

Coba Thunderbit untuk Ekstraksi Data Web Get Started Free

FAQ

Mengapa import nodriver gagal di Python 3.14? Karena cdp/network.py mengandung byte ± non-UTF-8 tanpa deklarasi encoding sumber. Python 3.14.2 menolak file tersebut dan membatalkan import transitive; Python 3.12.13 berhasil mengimpor file yang byte-nya identik tanpa patch. Ulasan ini tidak mengisolasi perubahan interpreter yang tepat dan tidak menguji Python 3.13. Jalur upstream-nya adalah issue nodriver #35 dan pull request #36. Gunakan versi yang sudah Anda uji, atau encode ulang file tersebut ke UTF-8.

Apa manfaat "CDP-native, no webdriver", dan berapa biaya install-nya? nodriver membundel 57 modul domain DevTools Protocol dan berbicara CDP lewat WebSocket, alih-alih memanggil chromedriver melalui Selenium. Tab mengekspos 62 metode, termasuk XPath native. Metadata paket mendeklarasikan tiga requirement runtime (websockets, mss, deprecated); hasil resolve menambah wrapt. Lingkungan terukur memakai sekitar 17,2 MB di enam direktori dist-info, termasuk pip, tanpa numpy, lxml, atau browser binary yang diunduh. Dua catatan: import salinan yang sudah dipatch memakan ~158 ms karena semua 57 modul CDP dimuat secara eager, dan browser keluarga Chrome tetap diperlukan secara terpisah.

Apakah lisensi AGPL-3.0 penting untuk proyek saya? Tergantung cara Anda merilisnya. AGPL-3.0 adalah lisensi network-copyleft: jika Anda menjalankan versi nodriver yang dimodifikasi sebagai layanan yang dipakai orang lain, Anda wajib menawarkan source yang dimodifikasi itu kepada mereka. Untuk script pribadi atau tool internal yang tidak pernah diekspos ke luar, ini biasanya bukan masalah. Untuk produk komersial hosted yang dibangun di atas nodriver yang dipatch, ini adalah pertanyaan nyata yang perlu dibahas dengan pihak yang menangani compliance — dan ini lebih ketat daripada lisensi Apache-2.0 dan MIT pada tool yang sebanding.

Apa yang dibocorkan nodriver tentang dirinya — dan apakah itu berarti ia lebih baik dari Cloudflare? Bagian pertama terukur; bagian kedua tidak, dan perbedaan itu penting. Pada halaman yang saya sajikan dari 127.0.0.1, dengan Chrome build yang sama seperti kontrol: navigator.webdriver kembali false, sementara stock Playwright dan stock Puppeteer sama-sama melaporkan true. Nilai itu ditetapkan saat browser diluncurkan, bukan dengan mempatch properti — descriptor-nya tetap getter native milik Chrome. Di luar boolean itu, hampir semuanya cocok persis dengan kontrol: string platform sama, lima plugin, dua belas core, reported device memory 16 GB, bentuk window.chrome sama, tidak ada kontradiksi Permissions API, dan tidak ada sisa gaya cdc_ pada document atau window. Run headless masih mengumumkan HeadlessChrome/151.0.0.0 di user-agent, sama seperti kedua kontrol — itu tidak disamarkan untuk Anda. Namun semua itu tidak memberi tahu apakah ada yang bekerja melawan layanan anti-bot nyata. Saya tidak pernah mengarahkan nodriver ke situs live, tidak pernah menghubungi layanan anti-bot, dan tidak pernah menyentuh CAPTCHA; itu memang di luar cakupan secara sengaja. Tabel disclosure di atas hanya menyatakan apa yang diumumkan stack. Ia tidak mengatakan siapa yang mendengarkan, atau apa yang mereka lakukan terhadapnya.

Apakah nodriver menangani konten yang dirender JavaScript dengan benar? Ya, dengan catatan biasa tentang kapan Anda membaca. Pada fixture dengan tiga kelas konten, browser.get() + tab.get_content() default mengembalikan 2 dari 3 — JavaScript dijalankan dengan benar (kelas yang disuntikkan sinkron tidak ada di bytes yang disajikan, tetapi tetap muncul), tetapi pembacaannya terjadi pada event load, jadi apa pun yang disuntikkan sesudahnya terlewat. Menambahkan tab.select("#delayed-injected") menghasilkan 3 dari 3. Hasilnya identik dengan stock Playwright dan stock Puppeteer pada halaman yang sama. Satu keanehan yang perlu dianggarkan: select() melakukan polling dengan back-off setengah detik, jadi wait 100 ms bisa memakan sekitar 1,1 detik.

Ke
Ke
CTO di Thunderbit | Senior Data Scientist & Expert ML Dengan pengalaman hampir satu dekade di bidang machine learning dan data science, Ke Shen adalah lulusan Columbia University dan mantan Senior Data Scientist di Walmart Labs. Dengan keahlian mendalam yang diakui sejawat dalam Python, R, Java, dan Statistik, ia membagikan wawasan teruji lapangan tentang bagaimana membawa algoritma AI yang kompleks dari teori ke arsitektur siap produksi.
Daftar isi
Thunderbit · Agen data web AI

Ekstrak data dari halaman apa pun dalam 1 klik

Dipercaya 250.000+ pengguna
tersedia paket gratis
Dari halaman web ke spreadsheet
Jelaskan kebutuhanmu — Agen AI Thunderbit akan men-scrape lalu mengekspor ke Excel, Google Sheets, Airtable, atau Notion. Gratis untuk memulai.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week