Kepentingan kami: kami ialah Geonode dan kami menjual proksi, dan automasi pelayar ialah perkara paling lapar lebar jalur yang boleh anda lakukan melaluinya. Pelayar headless mengambil setiap imej, fon, skrip dan pramuat video, jadi menjalankan chromedp melalui trafik bermeter kosnya kira-kira satu magnitudo lebih daripada permintaan HTTP mentah untuk halaman yang sama. Ada bahagian tentang memangkas itu, dan teknik di dalamnya akan menjimatkan lebih daripada memilih penyedia yang lebih murah. Konfigurasi proksi sendiri tiga baris dan ada satu jebakan sebenar, yang turut diliputi.
Apa itu chromedp
Projek ini menggambarkan dirinya sebagai "a faster, simpler way to drive browsers supporting the Chrome DevTools Protocol in Go without external dependencies".
Klausa terakhir itulah titik jualan. Selenium memerlukan binari driver yang sepadan dengan versi pelayar; Playwright menghantar runtime sendiri. chromedp bercakap DevTools Protocol terus melalui websocket, jadi binari Go plus pemasangan Chrome ialah seluruh penerapan.
Dilesenkan MIT dan diselenggara secara aktif — versi 0.15.1 dikeluarkan April 2026, dengan komit hingga Julai, disemak September 2026.
Pemasangan tidak luar biasa:
go get -u github.com/chromedp/chromedp
Pengikatan protokol yang dijana tinggal dalam pakej rakan, github.com/chromedp/cdproto, yang anda tuju apabila memerlukan sesuatu yang tidak dibungkus API peringkat tinggi.
Model context
Bahagian yang perlu difahami dahulu, kerana yang lain mengikutinya.
chromedp menggunakan context.Context untuk dua kerja sekaligus: pembatalan, seperti Go sentiasa, dan membawa pemegang pelayar serta tab. Tujuan berganda itu sebabnya persediaan kelihatan 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 memperuntukkan pelayar. Context berikutnya yang diterbitkan daripadanya mencipta tab baharu dalam pelayar yang sama, cara anda menjalankan banyak halaman tanpa membayar kos permulaan berulang:
browserCtx, cancelBrowser := chromedp.NewContext(context.Background())
defer cancelBrowser()
tabCtx, cancelTab := chromedp.NewContext(browserCtx)
defer cancelTab()
Membatalkan menutup perkara. Membatalkan context tab menutup tab; membatalkan context pelayar menutup pelayar. defer cancel() bukan perakaunan pilihan — meninggalkannya membocorkan proses Chrome.
Tamat masa disusun cara Go biasa:
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel()
Dua ralat daripada FAQ projek sendiri wajar diketahui terlebih dahulu.
"Executing an action without Run results in 'invalid context'." FAQ menjelaskan bahawa "by default, a chromedp context does not have an executor, however one can be specified manually if necessary". Actions tidak melaksanakan diri — ia nilai yang dijalankan Run.
"I'm seeing 'context canceled' errors." FAQ menautkannya kepada kehilangan sambungan: "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 ralat context canceled kerap bermakna Chrome mati, bukan tamat masa yang melepaskan — wajar dibezakan sebelum menaikkan tamat masa.
Actions
Run mengambil jujukan actions dan melaksanakannya mengikut tertib. Yang biasa merangkumi kebanyakan kerja.
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 daripada beberapa elemen menggunakan 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 dalam halaman dan unmarshal hasilnya ke nilai Go, yang kerap laluan terpendek untuk apa-apa yang melibatkan beberapa elemen sekaligus. Nilai mesti boleh diserialkan JSON.
Untuk actions yang mengembalikan beberapa nilai, FAQ memberi pembungkus:
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-apa yang tidak diliputi API peringkat tinggi — menetapkan kuki, memintas permintaan rangkaian, meniru peranti. Pintu kecemasan itu tersedia untuk seluruh DevTools Protocol, permukaan yang besar.
Menunggu dengan betul
Perbezaan antara pengikis yang boleh dipercayai dan yang goyah, dan kesilapannya sentiasa sama.
Jangan tidur. chromedp.Sleep(3*time.Second) wujud, menggoda, dan sama ada terlalu pendek — kegagalan berselang pada hari lambat — atau terlalu panjang, membuang masa setiap larian. Biasanya kedua-duanya, pada mesin berbeza.
Tunggu perkara yang anda peduli:
chromedp.WaitVisible(`.results`, chromedp.ByQuery)
chromedp.WaitNotVisible(`.spinner`)
chromedp.WaitReady(`#content`)
WaitVisible menunggu elemen wujud dan kelihatan; WaitReady menunggu ia wujud dalam DOM. Untuk kandungan dimuatkan selepas interaksi, kelihatan biasanya syarat yang betul.
Untuk syarat yang tiada pemilih ungkapkan, undi dalam halaman:
chromedp.Poll(`document.querySelectorAll('.item').length >= 20`, nil)
Itulah jawapan untuk "tunggu sehingga senarai selesai dimuatkan", yang tiada tunggu berasaskan elemen boleh ungkapkan.
Sentiasa hadkan tunggu dengan tamat masa context. WaitVisible pada pemilih yang tidak akan pernah sepadan menyekat sehingga context luput, dan tanpa tamat masa itu selama-lamanya.
Berjalan headless, dan dalam Docker
Chrome berjalan headless secara lalai. FAQ menjawab soalan pertama orang: "By default, Chrome is run in headless mode. See DefaultExecAllocatorOptions, and an example to override the default options."
Untuk melihatnya bekerja semasa membangun:
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 ialah tempat anda menetapkan bendera baris perintah Chrome, dan ia lapisan di atas context pelayar.
Untuk bekas, cadangan projek khusus: "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."
Wajar diikuti. Memasang Chrome yang berfungsi dalam bekas dengan tangan bermakna mengejar perpustakaan kongsi dan pakej fon yang hilang, dan hasilnya lebih besar daripada imej yang dibina untuk itu.
Satu tingkah laku khusus Linux daripada FAQ, yang mengejutkan orang yang menjalankan Chrome berasingan: "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 menyambung ke pelayar yang sudah berjalan melalui hujung websocketnya, corak untuk kumpulan pelayar kongsi atau pelayar dalam bekas berasingan.
Menggunakan proksi
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 menetapkan bendera --proxy-server Chrome.
Jebakannya pengesahan. Bendera --proxy-server Chrome tidak menerima kelayakan — URL dengan nama pengguna dan kata laluan tidak akan mengesahkan. Chrome membalas cabaran proksi dengan dialog, dan pelayar headless tiada siapa untuk mengisinya.
Dua jalan, mengikut keutamaan.
Gunakan senarai izinkan IP. Jika alamat sumber stabil, daftarkannya dengan penyedia dan buang kelayakan. Itu jawapan paling bersih dan membuang masalah, bukan mengelilinginya.
Kendalikan peristiwa pengesahan. chromedp boleh membalas permintaan pengesahan DevTools Protocol melalui fetch.Enable dengan handleAuthRequests, membekalkan kelayakan secara program. Lebih banyak kod, dan itu laluan apabila alamat tidak tetap.
Kemudian sahkan ia berjaya, kerana proksi salah konfigurasi dalam pelayar senyap:
var ip string
err := chromedp.Run(ctx,
chromedp.Navigate("https://api.ipify.org"),
chromedp.Text("body", &ip, chromedp.NodeVisible),
)
Jalankan dengan dan tanpa pilihan proksi. Jika alamat tidak berubah, Chrome tidak menggunakannya — dan tiada apa yang memberitahu anda. Untuk kerja sasar geo, pergi lebih jauh dan sahkan kandungan yang berbeza secara serantau benar-benar berbeza, kerana alamat ialah bahagian mudah. Ini corak kegagalan senyap yang kami huraikan dalam mengapa menguji proksi penting.
Padankan lokal dengan negara keluar sambil itu. Keluar Jerman dengan pengepala bahasa en-US dan zon masa London ialah gabungan yang tidak dihasilkan pelawat sebenar, dan banyak tapak menggunakan lokal bebas daripada alamat:
chromedp.Flag("lang", "de-DE"),
Memangkas lebar jalur
Bahagian yang paling menjimatkan wang, dan ia terpakai kepada sebarang automasi pelayar.
Halaman 200 KB HTML mungkin 4 MB setelah setiap imej, fon, skrip penjejakan dan pramuat video diambil. Pada kadar kediaman $0.79/GB — angka kami, disemak terhadap halaman harga pada September 2026 — perbezaan itu seluruh belanjawan.
Sekat jenis sumber yang tidak anda perlukan. Menggunakan fetch.Enable dan pendengar request-paused, batalkan permintaan untuk imej, media dan fon:
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)
}
}()
}
})
Ini secara rutin memotong kebanyakan trafik, dan mempercepatkan larian sebagai kesan sampingan.
Guna semula pelayar, cipta tab. Permulaan pelayar mahal; tab murah. Untuk larian merentas banyak halaman, peruntukkan sekali dan terbitkan context tab.
Dan tanya sama ada anda perlukan pelayar langsung. Jika kandungan ada dalam HTML awal, permintaan HTTP biasa kosnya pecahan dan berjalan jauh lebih pantas. Semak sumber halaman sebelum meraih automasi — refleks merender segala-galanya ialah sumber kos tidak perlu yang paling biasa dalam bidang ini.
Menyahpepijat apabila tiada yang berjalan
Automasi pelayar gagal secara kabur — pemilih yang tidak pernah sepadan dan halaman yang tidak pernah dimuatkan menghasilkan tamat masa yang sama. Jujukan tetap menyelesaikan kebanyakan.
Hidupkan pelayar dan lihat. Diagnostik terpantas, dan yang orang tinggalkan hingga akhir:
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("headless", false),
)
Separuh masa jawapannya kelihatan serta-merta — sepanduk kuki menutup butang, lencongan ke halaman log masuk, skrin cabaran, atau tataletak yang berbeza daripada yang anda uji.
Tangkap halaman apabila larian gagal. Dalam persekitaran headless ini menggantikan melihat:
var buf []byte
_ = chromedp.Run(ctx, chromedp.FullScreenshot(&buf, 90))
_ = os.WriteFile("failure.png", buf, 0644)
Pasangkannya dengan HTML, kerana tangkapan menunjukkan apa yang dirender dan sumber menunjukkan apa yang tiba:
var html string
_ = chromedp.Run(ctx, chromedp.OuterHTML("html", &html, chromedp.ByQuery))
Semak pemilih benar-benar terselesaikan sebelum menganggap masalah masa:
var count int
_ = chromedp.Run(ctx, chromedp.Evaluate(`document.querySelectorAll('.item').length`, &count))
Sifar bermakna masalah pemilih, dan tiada jumlah menunggu yang membetulkannya.
Dayakan pengelogan pelayar apabila anda mengesyaki halaman sendiri sedang ralat:
opts := append(chromedp.DefaultExecAllocatorOptions[:],
chromedp.Flag("enable-logging", true),
chromedp.Flag("v", "1"),
)
Dengar mesej konsol dan permintaan yang gagal, yang kerap 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 kelihatan sama dengan halaman yang pemilihnya berubah, dan hanya peristiwa rangkaian membezakannya.
Dan gunakan chromedp-proxy sebagai jalan terakhir. Ia duduk antara program anda dan pelayar dan mengelog trafik DevTools Protocol dalam kedua-dua arah. Apabila tingkah laku tidak masuk akal langsung, melihat pertukaran protokol sebenar biasanya menjelaskannya dalam satu bacaan.
Bila chromedp pilihan yang betul
Gunakan apabila anda sudah menulis Go dan mahukan satu binari statik tanpa driver untuk diedarkan, atau apabila anda memerlukan akses DevTools Protocol terus untuk sesuatu yang tidak didedahkan alat peringkat lebih tinggi.
Pertimbangkan Playwright apabila anda mahu sokongan merentas pelayar, menunggu automatik dalam setiap action, penjejakan dan tangkapan skrin apabila gagal, atau badan dokumentasi dan contoh yang lebih besar. Port Go wujud tetapi ekosistem berpusat pada JavaScript dan Python.
Pertimbangkan HTTP biasa apabila kandungan ada dalam respons awal. Lebih pantas, lebih murah, lebih mudah, dan lebih banyak web menyajikan HTML berguna daripada yang wacana cadangkan.
Senarai sumber FAQ sendiri peta yang baik untuk langkah seterusnya: repositori examples untuk actions kompleks dan tangkapan halaman penuh, rujukan cdproto untuk API protokol yang dijana, dan chromedp-proxy — proksi pengelogan CDP — untuk melihat tepat apa yang program dan pelayar katakan antara satu sama lain, alat menyahpepijat jalan terakhir dan yang benar-benar baik.
Soalan lazim
Apa itu chromedp?
Pakej Go yang memandu pelayar yang bercakap Chrome DevTools Protocol, tanpa kebergantungan luaran. Tidak seperti Selenium ia tidak memerlukan binari driver, dan tidak seperti Playwright ia tidak menghantar runtime — binari Go plus pemasangan Chrome ialah seluruh penerapan.
Mengapa saya mendapat "invalid context" dalam chromedp?
Kerana anda melaksanakan action tanpa Run. FAQ menjelaskan bahawa context chromedp tidak mempunyai pelaksana secara lalai. Actions ialah nilai yang dijalankan chromedp.Run; memanggil satu secara langsung tiada apa untuk menjalankannya.
Apa maksud "context canceled" dalam chromedp?
Biasanya bahawa sambungan pelayar hilang. FAQ menautkannya kepada pelayar ditutup secara manual atau proses dibunuh. Wajar dibezakan daripada tamat masa, kerana pembetulan berbeza — Chrome yang ranap tidak diselesaikan dengan menunggu lebih lama.
Bagaimana saya menjalankan chromedp dengan pelayar kelihatan?
Chrome berjalan headless secara lalai. Tambah chromedp.Flag("headless", false) kepada DefaultExecAllocatorOptions dan berikannya kepada NewExecAllocator, kemudian terbitkan context anda daripada peruntuk itu.
Bagaimana saya menggunakan proksi dengan chromedp?
Tambah chromedp.ProxyServer("http://host:port") kepada pilihan peruntuk. Kelayakan dalam URL tidak akan berfungsi, kerana bendera --proxy-server Chrome tidak menerimanya — gunakan senarai izinkan IP jika alamat stabil, atau kendalikan permintaan pengesahan melalui DevTools Protocol.
Bagaimana saya menjalankan chromedp dalam Docker?
Jalankan program Go di dalam imej chromedp/headless-shell, yang disyorkan projek secara jelas. Ia mengandungi binaan Chrome headless lebih kecil yang chromedp temui tanpa konfigurasi, dan mengelakkan memasang persekitaran pelayar dengan tangan.
Bagaimana saya menunggu elemen dalam chromedp?
Gunakan WaitVisible, WaitReady atau WaitNotVisible dengan pemilih, atau Poll dengan ungkapan JavaScript untuk syarat yang tiada pemilih ungkapkan. Elakkan Sleep — terlalu pendek dan goyah atau terlalu panjang dan membazir, biasanya kedua-duanya bergantung pada mesin.
Bagaimana saya mengurangkan lebar jalur apabila menggunakan chromedp?
Sekat imej, fon dan media dengan mendayakan pintasan permintaan dan menggagalkan jenis sumber itu, yang biasanya membuang kebanyakan trafik. Guna semula satu pelayar dan cipta tab daripada memperuntukkan berulang. Dan semak sama ada kandungan ada dalam HTML awal, yang dalam hal itu langkau pelayar sepenuhnya.
Penutup
Lengkung pembelajaran chromedp hampir seluruhnya model context. Setelah anda menghayati bahawa context membawa pelayar atau tab, bahawa membatalkan satu menutupnya, dan bahawa actions tidak melakukan apa-apa sehingga Run melaksanakannya, selebihnya API adalah lurus.
Tabiat yang penting sama seperti dalam sebarang automasi pelayar. Tunggu syarat daripada tidur, hadkan setiap tunggu dengan tamat masa context, dan guna semula satu pelayar merentas banyak tab daripada membayar kos permulaan berulang.
Dua perkara khusus Go wajar diingati. defer cancel() pada setiap context, atau anda membocorkan proses Chrome — dan pada Linux, chromedp membunuh paksa anak Chrome yang dimulakannya, jadi pelayar jangka panjang perlu dilancarkan berasingan dan dicapai dengan RemoteAllocator.
Dan jika anda berjalan melalui proksi bermeter, sekat jenis sumber yang tidak anda perlukan sebelum melakukan apa-apa lagi. Halaman yang dirender kosnya satu magnitudo lebih daripada HTML yang dikandungnya, dan kebanyakan itu imej yang anda tidak akan lihat.
