chromedp, MIT lisanslı, saf Go ile yazılmış bir kütüphanedir ve Chrome DevTools Protocol üzerinden gerçek bir Chrome tarayıcısını kontrol eder. Sayfanın JavaScript’i çalıştıktan sonra DOM’u bir Go programının içinden okur; ayrı bir WebDriver’a ya da Node çalışma zamanına ihtiyaç duymaz. Go modülü uygulamanın içine derlenir, ancak çalıştırılabilir sistem yine de yaşam döngüsü Go context’leriyle yönetilen harici bir Chrome yürütülebilir dosyasına ihtiyaç duyar.
Kurulum tek bir go get komutu ve benim ayrıca sağlamam gereken bir Chrome’dan ibaretti: bir allocator oluşturmak, bir context türetmek, Run fonksiyonuna bir işlem listesi vermek. Bu macOS arm64 makinede, diskte sıcak durumda duran headless shell ile, ilk script sonucuna giden yeni bir süreç için medyan süre 102 ms oldu. Bu yalnızca yerel bir tabandır; başlangıcın hiçbir zaman darboğaz olmadığına dair genel bir iddia değildir. Yüklemeden 800 milisaniye sonra bir bağlantı enjekte eden bir testte, dört okuma stratejisinden ikisi bağlantı oluşmadan önce döndü.
Tarayıcı gerçekte vardı, içerik de oradaydı; sorun kodun onu beklememesiydi. chromedp’nin bana öğrettiği en faydalı şey bu boşluk oldu ve bu bir bug değil — “sayfayı render ettim” ile “aslında istediğim şeyi bekledim” arasındaki fark. Headless tarayıcılarla ilgili yerleşik anlatı bu ayrımı yok sayar. Veriyi alıp almamanızı belirleyen şey tarayıcı değil, bekleme stratejisidir. Buradaki tüm sayılar, kontrolü tamamen bende olan yerel bir fixture’dan geliyor; ground truth her çalıştırmadan önce kaydedildi ve ham özetler benchmark reposundaki chromedp klasöründe duruyor.
chromedp aslında nedir
chromedp yığını bilinçli olarak kısadır. Selenium sunucusu yok. WebDriver ara katmanı yok. Altta saklanan Node runtime yok. Go programınız bir Chrome örneğine WebSocket açar ve onunla doğrudan CDP üzerinden konuşur; bu, JavaScript katmanı çıkarıldığında Puppeteer’ın kullandığı kablo protokolüne kabaca benzer.
Depo 27 Temmuz 2026’da kontrol ettiğimde 13.212 yıldız ve 178 açık issue’a sahipti; lisansı MIT. Test ettiğim sürüm v0.16.0; bu, depodaki en yeni tag. Karışıklık yaratmaması için özellikle not düşeyim: GitHub’un Releases sayfası hâlâ v0.15.1’i (2026-04-01’de yayımlanmış) son release nesnesi olarak gösteriyor, ancak go get github.com/chromedp/chromedp@latest komutu v0.16.0’a çözülüyor. Go modülleri ile GitHub release nesneleri burada birbirinden kopmuş durumda. Kırık değil, ama ne çalıştırdığınızı anlamaya çalışırken can sıkıcı.
Zihinsel model tamamen Go context’leri üzerine kurulu. Chrome’u başlatmayı bilen bir allocator context’i oluşturursunuz, ondan bir browser context’i türetirsiniz ve ardından chromedp.Run(ctx, actions...) çağrısına bir Action listesi verirsiniz. Bir browser context’inin child context’i yeni bir sekmedir. Bir context’i iptal ederseniz, temsil ettiği şey de ortadan kalkar. Go concurrency ile hiç uğraşmadıysanız bile bu yapı çok tanıdık gelir; uğraştıysanız da Go’da web scraping’e başlama rehberimiz chromedp’nin godoc’una kıyasla çok daha yumuşak bir giriş sunar.
Baştan koyulması gereken bir sınır daha var: chromedp size render edilmiş bir DOM verir. Yapılandırılmış veri vermez. O DOM’dan çekeceğiniz her şey — alanlar, tablolar, fiyatlar — sizin yazıp bakımını yapacağınız koddur. Bu bir driver’dır, tam bir scraper framework’ü değil.
İçerde çalışan mekanizma
chromedp’de her şey bir Action’dır ve Run bu işlemleri hedefe karşı sırayla çalıştırır. Navigate, Click, Evaluate, OuterHTML, WaitVisible — hepsi aynı interface, hepsi birbiriyle birleştirilebilir, hepsi aslında Go tipleri giymiş CDP komutlarıdır. Kütüphanenin en iyi tasarım kararı budur; çünkü üzeri süslenmiş API ile ham protokol aynı düzlemde yaşar.
Bu önemli, çünkü bu süsleme bilerek incedir. chromedp, cdproto üzerine kuruludur; bu paket DevTools Protocol yüzeyini kapsayan, üretilmiş tipli Go binding’lerden oluşur ve chromedp godoc iki katmanı yan yana belgeler. Kolaylık sağlayan action yoksa, aynı Run içinde alan çağrısına inersiniz — network.Enable(), page.CaptureScreenshot(), runtime.Evaluate() — ve devam edersiniz. “Kullanışlı API” ile “gerçek API” arasında duvar yoktur; bu da her tarayıcı driver’ında doğru değildir.
Günlük kullanımdaki kararların çoğu wait action’larda verilir ve insanların düşündüğünden daha fazlası vardır:
| Wait action | Neyi bekler |
|---|---|
WaitReady(sel) | düğüm DOM’a eklenene kadar |
WaitVisible(sel) | düğüm gerçekten görünür olana kadar |
WaitNotPresent(sel) / WaitNotVisible(sel) | ters halleri, özellikle spinner’lar için yararlı |
Poll(js, res) | JavaScript koşulunu aralıklarla değerlendirip doğru olana kadar bekler |
Süreç yönetimi de bilinmesi gereken başka bir mekanizma parçasıdır; çünkü programınızın geride çalışan bir tarayıcı bırakıp bırakmayacağını belirler. chromedp, Chrome’u Go’nun exec.CommandContext mekanizmasıyla başlatır. Bu context’i iptal etmek süreci öldürür. Bu tek uygulama detayı hem iyi davranışı hem de testte karşıma çıkan keskin kenarı açıklar.
Kurulum: bir Go binary’si ve sizin sağlamanız gereken bir Chrome
go get github.com/chromedp/chromedp komutu sorunsuz biçimde v0.16.0’a çözüldü ve bağımlılık ağacında cgo import’u yok. Dolayısıyla sıkça tekrar edilen “saf Go, harici bağımlılık yok” ifadesi Go modülü için doğrudur.
Ama çalışma zamanı için doğru değildir. chromedp, harici bir Chrome’u kontrol eder; makinede Chrome yoksa çalışma anında hemen başarısız olur. Yaptığım her ölçüm, tam yürütülebilir dosyayı chromedp.ExecPath üzerinden verdi ve bunu Chrome for Testing 151.0.7922.10 headless shell’e işaret etti. Bu bir eleştiri değil — tarayıcıyı kontrol etmek için bir tarayıcı gerekir — ancak “harici bağımlılık yok” ile “binary’nizin yanına 155 MB’lık bir Chrome da paketlemelisiniz” bambaşka dağıtım hikâyeleridir ve README’de yalnızca biri görünür.
İkinci bir kurulum tuzağı bana zaman kaybettirdi ve kod yazmadan önce bilmeye değer. chromedp issue #1591, Go 1.25+ go test çalıştırıcısının NewExecAllocator başlatılırken süreci iptal ettiğini bildiriyor; aynı kod derlenmiş binary olarak gayet iyi çalışıyor. Bu yüzden her ölçümde go build ile bir probe binary oluşturdum ve hiçbir şeyi go test üzerinden sürmedim. Burada Go sürümü 1.26.5, macOS arm64 idi. İlk chromedp deneyiminiz Chrome başlarken ölen bir test dosyasıysa, önce o issue’yu okuyun; suçu kendi kodunuza yüklemeyin.
Uygulamalı kullanım: aynı sayfayı okumanın dört yolu, ikisi boş dönüyor

Fixture, 127.0.0.1 üzerinde çalışan yerel bir sunucu ve DOM’a hangi anda girdiklerine göre farklılık gösteren üç içerik sınıfından oluşuyor: servis edilen baytların içinde duran statik bir <a>, ilk parse sırasında inline bir <script> tarafından oluşturulan bir <a>, ve yükleme olayından sonra yapılandırılabilir bir milisaniye gecikmeyle setTimeout üzerinden oluşturulan bir <a>. Script ile üretilen iki bağlantının marker ve href değerleri JavaScript içinde string parçalarından birleştiriliyor; yani servis edilen baytların hiçbir yerinde bitişik bir literal bulunmuyor. Dolayısıyla “bulundu” sonucu, Chrome’un JavaScript çalıştırdığını kanıtlar; HTML okunduğunu değil.
Recall, Go probe’un içinde değil, önceden kaydedilmiş ground-truth marker’lara karşı Python’da hesaplanıyor; böylece probe cevabı önceden bilerek hile yapamaz. Her strateji üç kez çalıştı; bulunan kümeler üç çalıştırmada da aynıydı.
| Okuma stratejisi | Statik HTML bağlantısı | Parse sırasında enjekte edilen | Yüklemeden 800 ms sonra enjekte edilen | Süre |
|---|---|---|---|---|
Navigate + okuma, bekleme yok | bulundu | bulundu | kaçırıldı | 317 ms |
WaitReady("body") | bulundu | bulundu | kaçırıldı | 107 ms |
WaitVisible("#delayed-injected") | bulundu | bulundu | bulundu | 912 ms |
| Marker görünene kadar Poll | bulundu | bulundu | bulundu | 972 ms |
İki satır üç bağlantıdan ikisini geri getiriyor. Naif okuma, Navigate load event’inde döndüğü için üçüncü bağlantı henüz ortada olmadığından başarısız oluyor. WaitReady("body") daha sinsi bir nedenle, üstelik pratikte daha kötü bir biçimde kaçırıyor: body load anında DOM’a ekli oluyor, dolayısıyla bekleme hemen sağlanmış sayılıyor ve siz sorumluluk sahibi davrandığınızı sanıyorsunuz. 107 ms’de döndü; bekleme yapmayan yoldan bile hızlıydı; ama size yine eksik sayfayı verdi.
Mekanizmayı varsaymak yerine doğrulamak için enjekte etme gecikmesini taradım ve iki uç stratejiyi yeniden çalıştırdım (recall-summary.json):
| Yüklemeden sonra enjekte etme gecikmesi | Beklemesiz okuma görür mü | WaitVisible görür mü | WaitVisible süresi |
|---|---|---|---|
| 0 ms | evet (yarış durumu) | evet | 109 ms |
| 100 ms | hayır | evet | 208 ms |
| 400 ms | hayır | evet | 519 ms |
| 800 ms | hayır | evet | 911 ms |
| 1500 ms | hayır | evet | 1625 ms |
WaitVisible’ın geçen süresi bu fixture’da enjeksiyon gecikmesini takip ediyor — 100’den 208’e, 400’den 519’a, 800’den 911’e, 1500’den 1625’e — bu da erken okumak yerine düğüm görünene kadar beklediğinin kanıtı. 0 ms satırı sınır durumudur: setTimeout(…, 0) anlık okuma öncesinde çalışabilir, bu yüzden beklemesiz yol onu yakalayabilir. Bu taramada 100 ms ve üzerindeki tüm durumlarda beklemesiz yol her seferinde kaçırdı.
Üretimde aynı zamanlama hatası, geçerli HTML verip sıfır çıkarılmış satırla birlikte 0 koduyla çıkışa yol açabilir; eğer pipeline çıktı adedini kontrol etmiyorsa bu sessizce olur. Bu, burada ölçülen bir olay değil, fixture davranışının desteklediği makul bir hata modudur. Render etmek işin yalnızca yarısıdır; okuma işlemi, istenen veriye bağlı uygulama düzeyinde bir koşulu beklemelidir.
WaitReady ve WaitVisible sıralı bir üstünlük yarışı değil; farklı sorulara cevap verirler
Sık kullanılan ifade, WaitVisible’ın WaitReady’den “daha güvenilir” olduğu yönündedir. Bu, zarar verecek kadar belirsiz bir ifadedir. DOM’a ekli ama display: none ile gizlenmiş bir düğümde ikisi açık biçimde ayrışır (waitsem-summary.json, üç özdeş çalıştırma):
| Target düğüm | Action | Sonuç | Süre |
|---|---|---|---|
ekli, display:none | WaitReady | döner | ~6 ms |
ekli, display:none | WaitVisible | zaman aşımına uğrar, context deadline exceeded | 4000 ms |
| görünür düğüm | varsayılan sorguyla WaitVisible | döner | 4–12 ms |
| görünür düğüm | ByID ile WaitVisible | döner | 1–2 ms |
| görünür düğüm | ByQuery ile WaitVisible | döner | 1 ms |
WaitReady, düğümün DOM’a ekli olduğu anlamına gelir. WaitVisible, görünür olduğu anlamına gelir. Yanlış olanı sorarsanız ya hiç render olmamış içeriğin yanından geçip gidersiniz ya da zaten görünmeyecek bir düğüm için tüm timeout süreniz boyunca beklersiniz. Deadline davranışının kendisi temizdir — tam 4 saniyede düzgün bir context deadline exceeded, takılma yok, zombie durum yok — ve bu, bazı driver’ların yapabildiğinden fazlasıdır.
Raporlanmış bir tuzak burada ortaya çıkmadı. Issue #440, WaitVisible("#id")’nin varsayılan sorguyla takılı kaldığını bildiriyor; bu durum v0.16.0’da tekrarlanmadı — varsayılan sorgu, ByID ve ByQuery görünür düğümde her çalıştırmada döndü. Tekrarlanmadı demek, düzeldi demek değildir: bu yalnızca tek bir sayfada, tek bir selector biçiminde denendi; issue’yu kapatmaya yetmez.
Atladığınız defer cancel() çatıyı ayakta tutuyor
Return değerleri değil, süreç sayıları önemli. Her yaşam döngüsü çalıştırmasında benzersiz bir --user-data-dir kullanıldı ve renderer çocukları filtrelenerek pgrep ile gerçek Chrome browser süreçleri sayıldı. Her yol üç kez çalıştı (lifecycle-summary.json).
| Çıkış yolu (macOS, her biri 3 çalıştırma) | Başlatılan chrome-headless-shell’e ne oldu | Zamanlama |
|---|---|---|
| Context’i ve allocator’ı iptal et | yok oldu | 13, 13 ve 12 milisaniye |
| Go sürecinden iptal etmeden çık | programınızdan sağ kurtuldu — probe çıkmadan önce sıfır browser süreci, sonrasında bir tane; üçte üç orphan | — |
İptal etmek temizdir, hızlıdır ve exec.CommandContext’in vaat ettiği şey tam olarak budur. (Tüm orphan’lar harness tarafından sonradan zorla öldürüldü; host temizlendi.)
Bu bilinen, belgelenmiş ve platforma özel bir davranıştır — ölçüm bana ait, keşif değil. chromedp takip sistemi bunu birkaç açıdan ele aldı: #774 FreeBSD’de aynı çıkmama davranışını anlatır, #752 macOS’ta asılı kalan Chromium süreçlerini rapor eder, #562 ve #1566 ise mekanizmayı açıklar. Benim eklediğim şey, süreç sayımı ve karşılaştırmanın iki tarafındaki zamanlamadır; bu nitel raporların vermediği bilgidir.
Mekanizmanın kendisi build-tag temelli bir hikâyedir ve bilinmeye değer. v0.16.0 kaynak kodunda allocate_linux.go, çocuk süreç üzerinde Pdeathsig = SIGKILL ayarlayarak Linux’ta çekirdek düzeyinde bir parent-death sinyali kullanır. macOS’un derlediği allocate_other.go ise bu çağrıyı no-op yapar. darwin üzerinde buna eşdeğer bir sinyal yoktur; bu yüzden programınız çıktığında Chrome’u öldürecek bir şey kalmaz. Bu sırada godoc metni genel bir vaat gibi okunur — varsayılan komut “Go programı çıktığında açık tüm tarayıcılara SIGKILL gönderir” der — ancak Linux kapsamı yalnızca gidip okumanız gereken build-tag’li kaynakta yer alır. Buna dokümantasyonun gereğinden fazla söz vermesi demek adildir; chromedp bug’ı demek değildir.
Pratik sonuç her iki durumda da aynıdır: macOS’ta defer cancel() yük taşıyan bir parçadır. Atlayın ve her çalıştırma bir browser süreci sızdırır. Linux’u test etmedim, dolayısıyla oradaki orphan sonucunu genellemiyorum — kaynak kod Linux’un farklı davrandığını ima ediyor ve ima ölçüm değildir.
Soğuk başlangıç, eşzamanlılık ve dağıtım kararını belirleyen sıkıcı ama önemli şeyler

Yeni süreç, allocator, context, localhost gezintisi ve ilk Evaluate birleşiminde beş süreç boyunca medyan süre 102 ms oldu; aralık 98–111 ms idi (coldstart-summary.json). Bu macOS arm64 fixture’ında ve diskte sıcak headless shell varken başlangıç süresi, gecikmeli içeriği bekleme süresine kıyasla küçüktü. Konteynerler, soğuk dosya sistemleri, CI, serverless ortamlar ve üretim gezintileri ölçülmedi.
Eşzamanlılıkta chromedp size iki şekil sunar — birkaç child context’e sahip tek bir browser ya da birkaç bağımsız browser. Dört gezinti, üçer kez çalıştırıldı (concurrency-summary.json):
| Mod | Toplam süre (p50) | Aralık | Tepe Chrome browser süreci |
|---|---|---|---|
| Paylaşılan browser, 4 child context | 214 ms | 209–219 | 1 |
| 4 ayrı browser | 264 ms | 261–278 | 4 |
Ölçülen bulgu süreç sayısıdır: dört basit yerel gezinti için tek Chrome browser süreci, ayrı kurulumda ise dört süreç. Toplam süre aralıkları örtüşmedi ama yine de bu bir throughput benchmark’ı değil, yön gösteren bir sonuç. RSS ve PSS ölçülmedi; dolayısıyla bu test bir bellek tasarrufu kanıtlamaz.
Küçük hata-yolu probu burada dayanıklılık iddiasını destekleyecek kadar ayrıntılı değil: taslak, her koşulun HTTP statüsü, navigation hatası, event mi yoksa harness mantığı mı tarafından yüzeye çıkarıldığını belirtmiyor. 500 / dead-link davranışını, tam API sonucu ve ham artefakt yayımlanana kadar raporlanmamış kabul edin.
Test edilmediği için bu sayıların kapsamı dışında kalanlar: Linux yaşam döngüsü davranışı, N=4’ün ötesinde eşzamanlılık veya gerçek sayfa başı iş yükü, bellek farkları (RSS değil süreç saydım), ağ müdahalesi ve istek yakalama, ayrıca #168 ve #1593 içindeki açık WaitReady timeout raporları — bunlar aralıklı timeout’ları anlatır; benim ölçtüğüm şey ise bekleme semantiğidir, farklı bir soru. Tek makine, tek Chrome sürümü.
chromedp bu tezgâhtaki tek Go CDP driver’ı değil: rod, aynı fixture, harness, host ve Chrome sürümüyle aynı oturumda test edildi ve kendi incelemesine sahip.
Artıları ve eksileri
Artıları:
- Gerçek CDP erişimi — kolaylık sağlayan action’lar ve ham
cdprotoalan çağrıları aynıRuniçinde birlikte kullanılabilir; bu yüzden API tavanına çarpmazsınız. - Test edilen macOS fixture’ında 102 ms p50 ve 98–111 ms aralığında yerel soğuk döngü tabanı.
cancelçağrısı Chrome’u her çalıştırmada yaklaşık 13 ms içinde temiz biçimde sonlandırır.- Child context’ler N sekme için tek browser sürecini paylaşır (ayrı browser’larda 4 süreç yerine 1 süreç).
- Testlerde deterministiktir — recall kümeleri, bekleme semantiği ve yaşam döngüsü sonuçları her üç tekrar arasında birebir aynıydı.
- Temiz deadline yönetimi: ulaşılmaz bir koşulda
WaitVisible, takılmak yerine tam 4 saniyede düzgün bircontext deadline exceededdöndürdü. - Saf Go modülü (cgo yok), MIT lisanslı; yine de çalışma zamanında harici bir Chrome yürütülebilir dosyası gerekir.
Eksileri:
- Çalışma zamanında harici Chrome ister; “bağımlılık yok” algısı yalnızca Go modülü için geçerlidir.
WaitReady("body"), doğruymuş gibi hissettiren ama yükleme sonrası içeriği sessizce kaçıran bir tuzaktır — eksik sayfayla 107 ms’de döndü.- Naif
Navigate+ okuma yolu, yüklemeden yaklaşık ≥100 ms sonra enjekte edilen her şeyi deterministik biçimde ve hatasız biçimde kaçırır. - macOS’ta
cancelolmadan çıkmak browser’ı orphan bırakır (3/3). Bilinen ve platforma özgü, ama kolayca tökezletir. - godoc’taki çıkışta SIGKILL ifadesi evrensel gibi görünür; oysa mekanizma Linux’a özel build-tag’li kaynakta yer alır.
- Go 1.25+ üzerinde
go test, allocator başlangıcını iptal edebilir (#1591); bunun yerine binary derleyin. - DOM döner, yapılandırılmış veri değil — istediğiniz her alan, sizin yazıp bakımını yapacağınız bir ayrıştırma kodudur.
- En yeni tag (v0.16.0), en yeni GitHub Release nesnesinden (v0.15.1) ileride; bu da sürüm kontrolünü kısa süreliğine kafa karıştırıcı yapıyor.
chromedp kimler için, kimler uzak durmalı
Servisiniz zaten Go ile yazılmışsa ve içine gerçek bir tarayıcı gömmek istiyorsanız, chromedp neredeyse en doğal seçimdir. Yönetilecek bir Node süreci yok, ayakta tutulacak bir WebDriver sunucusu yok, bir adet derlenmiş binary ve sizin dağıttığınız ya da kurduğunuz bir Chrome var. Context modeli Go’nun concurrency primitifleriyle o kadar doğrudan eşleşir ki tarayıcı yaşam döngüleri, kod tabanınızdaki her şey gibi defer disipliniyle yönetilir. Ve kolaylık sağlayan API’nin kapsamadığı bir şeye ihtiyaç duyarsanız — CDP ağ event’leri, hassas page-lifecycle hook’ları, protokol seviyesinde hileler — kütüphaneden çıkmadan cdproto katmanına inersiniz.
Ayrıca neyi bekleyeceğinizi açıkça kontrol etmek istediğinizde de iyi bir uyum sağlar. Wait action’lar heuristik değil, ilkeldir; söylediklerini yaparlar. Doğru olanı seçmenin artık sizin işiniz olduğunu kabul ettiğiniz anda bu bir avantajdır.
Ekibiniz Go yazmıyorsa uzak durun — asıl maliyet kütüphane değil, dile bağlılıktır. Sizin yerinize doğruyu tahmin eden otomatik bekleme ergonomisi istiyorsanız yine uzak durun; çünkü chromedp tahmin etmez, tam olarak istediğinizi yapar ve sayfanın o anda neye sahip olduğunu geri verir. Eğer aslında ihtiyacınız DOM değil de yapılandırılmış kayıtlar ise, uzak durun ya da ciddi bütçe ayırın: her alan sizin yazdığınız, test ettiğiniz ve site değiştikçe onardığınız bir selector’dır. “Bana şu 500 URL’den veriyi çıkar” tüm gereksinim buysa, Go içinde browser orkestrasyonu kurmak bu iş için epey ağır bir mekanizmadır. Browser otomasyon rehberimiz, bu mekanizmanın ne zaman işe yaradığını ve ne zaman yaramadığını anlatır.
Alternatifler ve kendi yığınımızın konumu
Tarayıcı driver kategorisinde rod başka bir Go CDP driver’ıdır; Playwright ve Puppeteer ise Playwright ile Puppeteer karşılaştırmamızda ele aldığımız Node tarafı seçeneklerdir. Bekleme sözleşmeleri birbirinin yerine geçmez. Playwright, birçok eylemden önce actionability için otomatik bekleme yapar; ancak bu, eylemden sonra uygulama verisinin ne zaman tamamlandığını söylemez. chromedp size daha düşük seviyeli bekleme primitifleri verir ve hem actionability’yi hem de uygulama düzeyindeki hazır olma koşullarını çağırana bırakır. Daha geniş alanı tarıyorsanız, test ettiğimiz açık kaynak scraper’ların dökümü bu çizginin öbür tarafındaki statik crawler’ları ve extraction kütüphanelerini kapsar.
İlgili inceleme: Browserless incelemesi.
Yönetilen bir extraction servisi farklı bir kategoridir. Tarayıcı ve selector kontrolünü dış kaynaklı render ve şema şekillendirmeyle takas eder. Çıktı DOM değil de yapılandırılmış kayıtlar olduğunda bu yararlı olabilir; tarayıcının Go servisinizin kontrolünde kalması gerektiğinde ise chromedp daha uygun olur. Biz Thunderbit adlı böyle bir servis geliştiriyoruz, ancak bu fixture üzerinde onu çalıştırmadık; dolayısıyla bu test chromedp ile eşdeğerlik, gecikme, extraction kalitesi veya maliyet karşılaştırması sunmaz.
Web Veri Çıkarma için Thunderbit’i Deneyin
Sonuç
chromedp kullanılmalı mı? Evet — eğer Go yazıyorsanız ve kontrolünüz altında gerçek bir tarayıcı istiyorsanız. Bu yerel fixture’da ilk script sonucuna p50 olarak 102 ms’de ulaştı, iptal sonrası Chrome’u yaklaşık 13 ms’de temizledi ve dört eşzamanlı sekme için tek browser süreci kullandı. Bunlar sınırları belli gözlemlerdir, evrensel performans vaatleri değil; asıl cazibe, kolaylık katmanı yetmediğinde Go’dan doğrudan CDP erişimi elde etmenizdir.
Yine de iddiaları doğru ölçekleyin; çünkü ünü iki şeyi abartıyor. “Saf Go, bağımlılık yok” ifadesi modülü anlatır; çalışma zamanında ise bir Chrome binary’si dağıtıp yönetirsiniz. Ve “headless browser kullan, dinamik içeriği alırsın” yalnızca beklemeniz istediğiniz düğüme kilitliyse doğrudur — naif okuma ve WaitReady("body") bana, yüklemeden 800 ms sonra enjekte edilen içeriği içermeyen bir sayfa verdi; üstelik her seferinde sessizce. macOS’ta defer cancel() bir stil tercihi değildir; onu atlarsanız her çalıştırmada bir browser sızdırırsınız. Bu bilinen bir platform davranışıdır ama yine de sizin çözmeniz gerekir. Bu üç şeyi doğru yaptıktan sonra chromedp, ölçtüğüm en öngörülebilir browser driver’larından biridir. Yanlış yaparsanız sessizce başarısız olur; bir scraper için en kötü başarısızlık biçimi budur.
Web Veri Çıkarma için Thunderbit’i Deneyin Get Started Free
SSS
chromedp gerçekten JavaScript ile render edilen içeriği görebilir mi?
Evet — ama yalnızca istediğiniz düğüme bağlı bir bekleme ile. Bir bağlantının load event’inden 800 ms sonra enjekte edildiği fixture’da Navigate + okuma onu kaçırdı, WaitReady("body") de kaçırdı; buna karşılık o düğüm üzerinde WaitVisible ve bir JavaScript poll’u üçer çalıştırmanın üçünde de başarıyla yakaladı. Gecikmeyi taradığımda, beklemesiz okuma 100 ms ve üzerindeki her ayarda düğümü kaçırdı. Render etmek gerekir; doğru beklemek ise bunu yeterli kılar.
WaitReady ile WaitVisible arasındaki fark nedir?
WaitReady, düğüm DOM’a eklenene kadar bekler. WaitVisible, gerçekten görünür olana kadar bekler. DOM’a ekli ama display: none olan bir düğümde WaitReady yaklaşık 6 ms’de dönerken WaitVisible 4 saniyelik context deadline’a kadar bekledi ve temiz bir context deadline exceeded döndürdü. Hiçbiri “daha güvenilir” değildir — farklı sorulara yanıt verirler ve yanlış olanı seçmek asıl tuzaktır.
chromedp ile gerçekten defer cancel() kullanmam gerekir mi?
macOS’ta evet. Context ve allocator iptal edildiğinde başlatılan Chrome her çalıştırmada 12–13 ms içinde temizlendi; Go süreci iptal etmeden çıktığında ise üç çalıştırmanın tamamında geride orphan bir browser süreci kaldı. Bu bilinen, platforma özgü davranıştır — chromedp takip sistemi Linux dışı sistemlerdeki benzer çıkmama desenini belgeliyor ve bunu çözmek için kullanılan parent-death kill mekanizması Linux’a özel build-tag’li kaynakta yer alıyor. Linux’u test etmedim; dolayısıyla orphan sonucunu macOS kapsamlı kabul edin.
chromedp için Chrome’u ayrıca kurmam gerekir mi?
Evet. Go modülünün kendisi saf Go’dur ve cgo içermez; ama harici bir tarayıcıyı kontrol eder ve onsuz hemen başarısız olur. Ben chromedp.ExecPath üzerinden açıkça Chrome for Testing 151.0.7922.10 headless shell sağladım. Artı tarafı, başlangıç maliyeti küçüktür: ilk script sonucuna giden tam bir soğuk döngü, beş yeni süreçte medyan 102 ms idi; aralık 98–111 ms.
Sekmeler arasında tek browser mı paylaşmalıyım, yoksa ayrı browser’lar mı açmalıyım? Browser süreç sayısını azaltmak hedefse child context’leri tercih edin. Dört localhost gezintisi tek browser üzerinden çalıştırıldığında 1 Chrome browser süreci kullanıldı; dört ayrı browser ise 4 süreç kullandı. Toplam süre de paylaşılan kurulumu destekledi (214 ms’ye karşı 264 ms medyan), fakat dört küçük yerel sayfa bir throughput benchmark’ı değildir. Bellek ölçülmedi. Daha güçlü oturum izolasyonu, farklı proxy’ler veya daha küçük bir çökme etkisi istiyorsanız ayrı browser’lar yine doğru seçenek olabilir; bu takaslar bu testin kapsamı dışındaydı.


