Geonode logo
Geonode Team

Geonode Team

Diperbarui: 7 September 2026

Diterbitkan: 2 September 2026

ChromeDP: Panduan Memulai

chromedp mengemudikan Chrome dari Go memakai DevTools Protocol, tanpa dependensi eksternal — tanpa biner driver, tanpa Java, tanpa proses server terpisah. Desain itu membuat deploy menyenangkan dan belajar sedikit tidak biasa, karena semuanya bergantung pada paket `context` Go dengan cara yang tidak jelas di awal. Panduan ini membahas model context, actions yang benar-benar Anda pakai, menunggu dengan benar, serta konfigurasi deploy dan proxy yang membuat orang tersandung.

Kepentingan kami: kami adalah Geonode dan menjual proxy, dan otomasi browser adalah hal paling haus bandwidth yang bisa Anda lakukan lewat satunya. Browser headless mengambil setiap gambar, font, skrip, dan preload video, jadi menjalankan chromedp lewat lalu lintas terukur biayanya kira-kira satu orde magnitudo lebih dari permintaan HTTP mentah untuk halaman yang sama. Ada bagian tentang memangkas itu, dan teknik di dalamnya akan menghemat lebih daripada memilih penyedia yang lebih murah. Konfigurasi proxy sendiri tiga baris dan punya satu jebakan sungguhan, yang juga dibahas.

Apa itu chromedp

Proyek ini menggambarkan dirinya sebagai "a faster, simpler way to drive browsers supporting the Chrome DevTools Protocol in Go without external dependencies".

Klausa terakhir itulah nilai jualnya. Selenium butuh biner driver yang cocok dengan versi browser; Playwright mengirim runtime sendiri. chromedp berbicara DevTools Protocol langsung lewat websocket, jadi biner Go plus instalasi Chrome adalah seluruh deploy.

Berlisensi MIT dan aktif dipelihara — versi 0.15.1 dirilis April 2026, dengan commit hingga Juli, dicek September 2026.

Instalasinya tidak istimewa:

go get -u github.com/chromedp/chromedp

Binding protokol yang dihasilkan tinggal di paket pendamping, github.com/chromedp/cdproto, yang Anda tuju saat butuh sesuatu yang tidak dibungkus API tingkat tinggi.

Model context

Bagian yang harus dipahami dulu, karena sisanya mengikuti dari situ.

chromedp memakai context.Context untuk dua pekerjaan sekaligus: pembatalan, seperti Go selalu, dan membawa handle browser serta tab. Tujuan ganda itu sebabnya pengaturan terlihat seperti itu.

ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()

var title string
err := chromedp.Run(ctx,
    chromedp.Navigate("https://example.com"),
    chromedp.Text("h1", &title, chromedp.NodeVisible),
)

NewContext pertama mengalokasikan browser. Context berikutnya yang diturunkan darinya membuat tab baru di browser yang sama, cara Anda menjalankan banyak halaman tanpa membayar biaya start-up berulang:

browserCtx, cancelBrowser := chromedp.NewContext(context.Background())
defer cancelBrowser()

tabCtx, cancelTab := chromedp.NewContext(browserCtx)
defer cancelTab()

Membatalkan menutup hal. Membatalkan context tab menutup tab; membatalkan context browser menutup browser. defer cancel() bukan pembukuan opsional — mengabaikannya membocorkan proses Chrome.

Timeout disusun cara Go biasa:

ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()

Dua error dari FAQ proyek sendiri layak diketahui lebih dulu.

"Executing an action without Run results in 'invalid context'." FAQ menjelaskan bahwa "by default, a chromedp context does not have an executor, however one can be specified manually if necessary". Actions tidak mengeksekusi diri — mereka nilai yang dijalankan Run.

"I'm seeing 'context canceled' errors." FAQ menautkannya ke kehilangan koneksi: "when the connection to the browser is lost, chromedp cancels the context, and it may result in this error. This occurs, for example, if the browser is closed manually, or if the browser process has been killed or otherwise terminated." Jadi error context canceled sering berarti Chrome mati, bukan timeout yang menembak — layak dibedakan sebelum menaikkan timeout.

Actions

Run menerima urutan actions dan menjalankannya berurutan. Yang umum mencakup sebagian besar pekerjaan.

err := chromedp.Run(ctx,
    chromedp.Navigate("https://example.com/search"),
    chromedp.WaitVisible(`input[name="q"]`),
    chromedp.SendKeys(`input[name="q"]`, "golang"),
    chromedp.Click(`button[type="submit"]`, chromedp.NodeVisible),
    chromedp.WaitVisible(`.results`),
    chromedp.Text(`.results`, &results, chromedp.NodeVisible),
)

Mengekstrak dari beberapa elemen memakai Nodes atau Evaluate:

var links []string
err := chromedp.Run(ctx,
    chromedp.Navigate(url),
    chromedp.Evaluate(`[...document.querySelectorAll('a')].map(a => a.href)`, &links),
)

Evaluate menjalankan JavaScript di halaman dan unmarshal hasilnya ke nilai Go, yang sering jalur terpendek untuk apa pun yang melibatkan beberapa elemen sekaligus. Nilainya harus bisa diserialisasi JSON.

Untuk actions yang mengembalikan beberapa nilai, FAQ memberi wrapper:

chromedp.Run(ctx, chromedp.ActionFunc(func(ctx context.Context) error {
    _, err := domain.SomeAction().Do(ctx)
    return err
}))

ActionFunc juga cara Anda turun ke panggilan cdproto mentah untuk apa pun yang tidak dicakup API tingkat tinggi — mengatur cookie, mencegat permintaan jaringan, meniru perangkat. Pintu darurat itu tersedia untuk seluruh DevTools Protocol, permukaan yang besar.

Menunggu dengan benar

Perbedaan antara scraper andal dan yang rapuh, dan kesalahannya selalu sama.

Jangan sleep. chromedp.Sleep(3*time.Second) ada, menggoda, dan entah terlalu pendek — gagal sesekali di hari lambat — atau terlalu panjang, membuang waktu setiap jalan. Biasanya keduanya, di mesin berbeda.

Tunggu hal yang Anda pedulikan:

chromedp.WaitVisible(`.results`, chromedp.ByQuery)
chromedp.WaitNotVisible(`.spinner`)
chromedp.WaitReady(`#content`)

WaitVisible menunggu elemen ada dan terlihat; WaitReady menunggu ada di DOM. Untuk konten yang dimuat setelah interaksi, terlihat biasanya kondisi yang tepat.

Untuk kondisi yang tidak diungkapkan selektor apa pun, poll di halaman:

chromedp.Poll(`document.querySelectorAll('.item').length >= 20`, nil)

Itulah jawaban untuk "tunggu sampai daftar selesai dimuat", yang tidak bisa diungkapkan menunggu berbasis elemen.

Selalu batasi menunggu dengan timeout context. WaitVisible pada selektor yang tidak akan pernah cocok memblokir sampai context kedaluwarsa, dan tanpa timeout itu selamanya.

Berjalan headless, dan di Docker

Chrome berjalan headless secara default. FAQ menjawab pertanyaan pertama orang: "By default, Chrome is run in headless mode. See DefaultExecAllocatorOptions, and an example to override the default options."

Untuk melihatnya bekerja saat mengembangkan:

opts := append(chromedp.DefaultExecAllocatorOptions[:],
    chromedp.Flag("headless", false),
)
allocCtx, cancelAlloc := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancelAlloc()

ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()

NewExecAllocator adalah tempat Anda mengatur flag baris perintah Chrome, dan itu lapisan di atas context browser.

Untuk kontainer, rekomendasi proyek spesifik: "The simplest way is to run the Go program that uses chromedp inside the chromedp/headless-shell image. That image contains headless-shell, a smaller headless build of Chrome, which chromedp is able to find out of the box."

Layak diikuti. Merakit Chrome yang jalan di kontainer dengan tangan berarti mengejar pustaka bersama dan paket font yang hilang, dan hasilnya lebih besar daripada image yang dibuat untuk itu.

Satu perilaku khusus Linux dari FAQ, yang mengejutkan orang yang menjalankan Chrome terpisah: "On Linux, chromedp is configured to avoid leaking resources by force-killing any started Chrome child processes. If you need to launch a long-running Chrome instance, manually start Chrome and connect using RemoteAllocator."

RemoteAllocator terhubung ke browser yang sudah berjalan lewat endpoint websocket-nya, pola untuk kumpulan browser bersama atau browser di kontainer terpisah.

Memakai proxy

Tiga baris, dan satu jebakan.

opts := append(chromedp.DefaultExecAllocatorOptions[:],
    chromedp.ProxyServer("http://proxy.example.com:9000"),
)
allocCtx, cancelAlloc := chromedp.NewExecAllocator(context.Background(), opts...)
defer cancelAlloc()

ctx, cancel := chromedp.NewContext(allocCtx)
defer cancel()

ProxyServer mengatur flag --proxy-server Chrome.

Jebakannya autentikasi. Flag --proxy-server Chrome tidak menerima kredensial — URL dengan nama pengguna dan kata sandi tidak akan mengautentikasi. Chrome merespons tantangan proxy dengan dialog, dan browser headless tidak punya siapa pun untuk mengisinya.

Dua jalan, urutan preferensi.

Pakai daftar izinkan IP. Jika alamat sumber stabil, daftarkan ke penyedia dan buang kredensial. Itu jawaban paling bersih dan menghapus masalah, bukan mengitarinya.

Tangani peristiwa autentikasi. chromedp bisa merespons permintaan autentikasi DevTools Protocol lewat fetch.Enable dengan handleAuthRequests, memberi kredensial secara program. Lebih banyak kode, dan itu jalur saat alamat tidak tetap.

Lalu verifikasi berhasil, karena proxy salah konfigurasi di browser itu senyap:

var ip string
err := chromedp.Run(ctx,
    chromedp.Navigate("https://api.ipify.org"),
    chromedp.Text("body", &ip, chromedp.NodeVisible),
)

Jalankan dengan dan tanpa opsi proxy. Jika alamat tidak berubah, Chrome tidak memakainya — dan tidak ada yang memberitahu Anda. Untuk kerja bertarget geo, lanjutkan dan pastikan konten yang berbeda secara regional memang berbeda, karena alamat adalah bagian mudah. Ini pola gagal senyap yang kami jelaskan di mengapa menguji proxy penting.

Sesuaikan locale dengan negara keluar sambil itu. Keluar Jerman dengan header bahasa en-US dan zona waktu London adalah kombinasi yang tidak dihasilkan pengunjung nyata, dan banyak situs memakai locale terlepas dari alamat:

chromedp.Flag("lang", "de-DE"),

Memangkas bandwidth

Bagian yang paling menghemat uang, dan berlaku untuk otomasi browser apa pun.

Halaman 200 KB HTML bisa jadi 4 MB setelah setiap gambar, font, skrip pelacak, dan preload video diambil. Pada tarif residensial $0,79/GB — angka kami, dicek di halaman harga September 2026 — selisih itu seluruh anggaran.

Blokir jenis sumber daya yang tidak Anda butuhkan. Dengan fetch.Enable dan listener request-paused, batalkan permintaan gambar, media, dan font:

chromedp.ListenTarget(ctx, func(ev interface{}) {
    if e, ok := ev.(*fetch.EventRequestPaused); ok {
        go func() {
            c := chromedp.FromContext(ctx)
            execCtx := cdp.WithExecutor(ctx, c.Target)
            switch e.ResourceType {
            case network.ResourceTypeImage, network.ResourceTypeMedia, network.ResourceTypeFont:
                _ = fetch.FailRequest(e.RequestID, network.ErrorReasonBlockedByClient).Do(execCtx)
            default:
                _ = fetch.ContinueRequest(e.RequestID).Do(execCtx)
            }
        }()
    }
})

Itu secara rutin memangkas sebagian besar lalu lintas, dan mempercepat jalan sebagai efek samping.

Pakai ulang browser, buat tab. Start-up browser mahal; tab murah. Untuk jalan di banyak halaman, alokasikan sekali dan turunkan context tab.

Dan tanyakan apakah Anda butuh browser. Jika konten ada di HTML awal, permintaan HTTP biasa biayanya sepersekian dan jauh lebih cepat. Cek sumber halaman sebelum meraih otomasi — refleks merender semuanya adalah sumber biaya tak perlu yang paling umum di bidang ini.

Debug saat tidak ada yang jalan

Otomasi browser gagal secara buram — selektor yang tidak pernah cocok dan halaman yang tidak pernah dimuat menghasilkan timeout yang sama. Urutan tetap menyelesaikan sebagian besar.

Nyalakan browser dan lihat. Diagnosis tercepat, dan yang orang sisakan terakhir:

opts := append(chromedp.DefaultExecAllocatorOptions[:],
    chromedp.Flag("headless", false),
)

Separuh waktu jawabannya langsung terlihat — spanduk cookie menutup tombol, pengalihan ke halaman login, layar tantangan, atau tata letak yang berbeda dari yang Anda uji.

Tangkap halaman saat jalan gagal. Di lingkungan headless ini menggantikan melihat:

var buf []byte
_ = chromedp.Run(ctx, chromedp.FullScreenshot(&buf, 90))
_ = os.WriteFile("failure.png", buf, 0644)

Pasangkan dengan HTML, karena tangkapan menunjukkan apa yang dirender dan sumber menunjukkan apa yang tiba:

var html string
_ = chromedp.Run(ctx, chromedp.OuterHTML("html", &html, chromedp.ByQuery))

Cek selektor benar-benar terselesaikan sebelum menganggap masalah waktu:

var count int
_ = chromedp.Run(ctx, chromedp.Evaluate(`document.querySelectorAll('.item').length`, &count))

Nol berarti masalah selektor, dan tidak ada jumlah menunggu yang memperbaikinya.

Aktifkan pencatatan browser saat Anda curiga halaman sendiri sedang error:

opts := append(chromedp.DefaultExecAllocatorOptions[:],
    chromedp.Flag("enable-logging", true),
    chromedp.Flag("v", "1"),
)

Dengarkan pesan konsol dan permintaan yang gagal, yang sering menjelaskan halaman yang merender kosong:

chromedp.ListenTarget(ctx, func(ev interface{}) {
    switch e := ev.(type) {
    case *runtime.EventConsoleAPICalled:
        log.Printf("console.%s", e.Type)
    case *network.EventLoadingFailed:
        log.Printf("failed: %s %s", e.Type, e.ErrorText)
    }
})

Halaman yang semua panggilan API-nya gagal tampak identik dengan halaman yang selektornya berubah, dan hanya peristiwa jaringan yang membedakannya.

Dan pakai chromedp-proxy sebagai jalan terakhir. Ia duduk di antara program Anda dan browser dan mencatat lalu lintas DevTools Protocol dua arah. Saat perilaku tidak masuk akal sama sekali, melihat pertukaran protokol sungguhan biasanya menjelaskannya dalam satu bacaan.

Kapan chromedp pilihan yang tepat

Pakai saat Anda sudah menulis Go dan ingin satu biner statis tanpa driver untuk didistribusikan, atau saat Anda butuh akses langsung DevTools Protocol untuk sesuatu yang tidak diekspos alat tingkat lebih tinggi.

Pertimbangkan Playwright saat Anda ingin dukungan lintas-browser, menunggu otomatis di setiap action, pelacakan dan tangkapan layar saat gagal, atau kumpulan dokumentasi dan contoh yang lebih besar. Port Go ada tetapi ekosistem berpusat pada JavaScript dan Python.

Pertimbangkan HTTP biasa saat konten ada di respons awal. Lebih cepat, lebih murah, lebih sederhana, dan lebih banyak web yang menyajikan HTML berguna daripada yang disarankan wacana.

Daftar sumber FAQ sendiri peta yang baik untuk langkah berikutnya: repositori examples untuk actions kompleks dan tangkapan halaman penuh, referensi cdproto untuk API protokol yang dihasilkan, dan chromedp-proxy — proxy pencatatan CDP — untuk melihat persis apa yang dikatakan program dan browser satu sama lain, alat debug jalan terakhir dan yang sungguh baik.

Pertanyaan yang sering diajukan

Apa itu chromedp?

Paket Go yang mengemudikan browser yang berbicara Chrome DevTools Protocol, tanpa dependensi eksternal. Tidak seperti Selenium, tidak butuh biner driver, dan tidak seperti Playwright, tidak mengirim runtime — biner Go plus instalasi Chrome adalah seluruh deploy.

Mengapa saya mendapat "invalid context" di chromedp?

Karena Anda mengeksekusi action tanpa Run. FAQ menjelaskan bahwa context chromedp tidak punya executor secara default. Actions adalah nilai yang dijalankan chromedp.Run; memanggil satunya langsung tidak punya apa pun untuk menjalankannya.

Apa arti "context canceled" di chromedp?

Biasanya bahwa koneksi browser hilang. FAQ menautkannya ke browser ditutup manual atau proses dibunuh. Layak dibedakan dari timeout, karena perbaikannya berbeda — Chrome yang crash tidak diselesaikan dengan menunggu lebih lama.

Bagaimana saya menjalankan chromedp dengan browser terlihat?

Chrome berjalan headless secara default. Tambahkan chromedp.Flag("headless", false) ke DefaultExecAllocatorOptions dan berikan ke NewExecAllocator, lalu turunkan context dari allocator itu.

Bagaimana saya memakai proxy dengan chromedp?

Tambahkan chromedp.ProxyServer("http://host:port") ke opsi allocator. Kredensial di URL tidak akan jalan, karena flag --proxy-server Chrome tidak menerimanya — pakai daftar izinkan IP jika alamat stabil, atau tangani permintaan autentikasi lewat DevTools Protocol.

Bagaimana saya menjalankan chromedp di Docker?

Jalankan program Go di dalam image chromedp/headless-shell, yang direkomendasikan proyek secara eksplisit. Berisi build Chrome headless lebih kecil yang ditemukan chromedp tanpa konfigurasi, dan menghindari merakit lingkungan browser dengan tangan.

Bagaimana saya menunggu elemen di chromedp?

Pakai WaitVisible, WaitReady, atau WaitNotVisible dengan selektor, atau Poll dengan ekspresi JavaScript untuk kondisi yang tidak diungkapkan selektor. Hindari Sleep — terlalu pendek dan rapuh atau terlalu panjang dan boros, biasanya keduanya tergantung mesin.

Bagaimana saya mengurangi bandwidth saat memakai chromedp?

Blokir gambar, font, dan media dengan mengaktifkan intersepsi permintaan dan menggagalkan jenis sumber daya itu, yang biasanya menghapus sebagian besar lalu lintas. Pakai ulang satu browser dan buat tab daripada mengalokasikan berulang. Dan cek apakah konten ada di HTML awal, yang dalam hal itu lewati browser sepenuhnya.

Penutup

Kurva belajar chromedp hampir seluruhnya model context. Setelah Anda menginternalisasi bahwa context membawa browser atau tab, bahwa membatalkan satunya menutupnya, dan bahwa actions tidak melakukan apa pun sampai Run menjalankannya, sisa API-nya lurus.

Kebiasaan yang penting sama seperti otomasi browser mana pun. Tunggu kondisi daripada tidur, batasi setiap tunggu dengan timeout context, dan pakai ulang satu browser di banyak tab daripada membayar biaya start-up berulang.

Dua poin khusus Go layak diingat. defer cancel() pada setiap context, atau Anda membocorkan proses Chrome — dan di Linux, chromedp membunuh paksa anak Chrome yang dimulainya, jadi browser yang berjalan lama perlu diluncurkan terpisah dan dijangkau dengan RemoteAllocator.

Dan jika Anda berjalan lewat proxy terukur, blokir jenis sumber daya yang tidak Anda butuhkan sebelum melakukan apa pun. Halaman yang dirender biayanya satu orde magnitudo lebih dari HTML yang dikandungnya, dan sebagian besar itu gambar yang tidak akan Anda lihat.