Reseña de chromedp: un navegador real sigue necesitando la condición adecuada de espera

Última actualización: August 17, 2026
Reseña de chromedp: un navegador real sigue necesitando la condición adecuada de espera
Resumen con IA
chromedp es una librería pura de Go, con licencia MIT, que controla un Chrome real mediante el Chrome DevTools Protocol. Lee el DOM después de que se ejecuta el JavaScript de la página desde dentro de un programa en Go, sin depender de un WebDriver aparte ni de un runtime de Node. El módulo de Go se compila dentro de la aplicación, pero el sistema ejecutable sigue requiriendo un binario externo de Chrome, cuya vida útil se gestiona mediante contextos de Go. Instalarlo fue cuestión de un go get y de aportar yo mismo un Chrome: construir un allocator, derivar un context y pasarle a Run una lista de acciones.

chromedp es una librería pura de Go, con licencia MIT, que controla un Chrome real a través del Chrome DevTools Protocol. Lee el DOM después de que se ejecuta el JavaScript de la página desde dentro de un programa en Go, sin depender de un WebDriver aparte ni de un runtime de Node. El módulo de Go se compila dentro de la aplicación, pero el sistema ejecutable sigue requiriendo un binario externo de Chrome, cuya vida útil se gestiona mediante contextos de Go.

Instalarlo fue cuestión de un go get y de aportar yo mismo un Chrome: construir un allocator, derivar un context y pasarle a Run una lista de acciones. En este equipo macOS arm64, con un headless shell ya caliente en disco, un proceso nuevo hasta el primer resultado del script tuvo una mediana de 102 ms. Ese es un dato local, no una afirmación general de que el arranque nunca sea un cuello de botella. En un fixture que inserta un enlace 800 milisegundos después de la carga, dos de las cuatro estrategias de lectura devolvieron el resultado antes de que el enlace existiera.

El navegador era real, el contenido estaba ahí y el código simplemente no esperaba lo suficiente. Esa brecha es lo más valioso que me enseñó chromedp, y no es un bug: es la diferencia entre "rendericé la página" y "esperé exactamente a lo que necesitaba", una distinción que la cultura popular sobre navegadores headless suele simplificar demasiado. La estrategia de espera, no el navegador, es lo que decide si obtienes los datos. Todos los números de aquí salen de un fixture local que controlo, con la verdad de referencia registrada antes de cualquier ejecución, y los resúmenes crudos están en la carpeta chromedp de nuestro repositorio de benchmark.

Qué es realmente chromedp

La pila de chromedp es deliberadamente simple. Sin servidor Selenium. Sin capa puente de WebDriver. Sin runtime de Node oculto detrás. Tu programa en Go abre un WebSocket con una instancia de Chrome y habla CDP directamente con ella, que es más o menos el mismo protocolo de comunicación que usa Puppeteer, pero sin JavaScript.

Cuando lo revisé el 27 de julio de 2026, el repositorio tenía 13.212 estrellas y 178 incidencias abiertas, bajo licencia MIT. La versión que probé es v0.16.0, que es la etiqueta más reciente del repositorio. Conviene señalar algo para evitar confusiones: la página de Releases de GitHub sigue mostrando v0.15.1 (publicada el 2026-04-01) como el último objeto de release, mientras que go get github.com/chromedp/chromedp@latest resuelve a v0.16.0. Aquí los módulos de Go y los objetos de release de GitHub se han desalineado. No está roto, pero sí resulta molesto cuando intentas averiguar qué estás ejecutando.

El modelo mental es puro Go de principio a fin: construyes un allocator context (que sabe cómo arrancar Chrome), derivás de él un browser context y luego llamas a chromedp.Run(ctx, actions...) con una lista de Actions. Un contexto hijo de un browser context equivale a una pestaña nueva. Si cancelas un contexto, desaparece lo que representa. Si has trabajado con concurrencia en Go, esto te resultará familiar de inmediato; si no, nuestra guía para empezar con web scraping en Go es una entrada mucho más amable que el godoc de chromedp.

Un límite importante desde el principio: chromedp te entrega un DOM ya renderizado. No te entrega datos estructurados. Todo lo que extraigas de ese DOM —campos, tablas, precios— es código que tú escribes y mantienes. Es un driver, no un framework de scraping.

La mecánica interna

Todo en chromedp es una Action, y Run ejecuta una secuencia de ellas en orden contra un target. Navigate, Click, Evaluate, OuterHTML, WaitVisible... todas siguen la misma interfaz, todas se pueden combinar, todas son simplemente comandos CDP envueltos en tipos de Go. Esa uniformidad es la mejor decisión de diseño de la librería, porque hace que las comodidades y el protocolo bruto vivan al mismo nivel.

Y eso importa, porque la capa cómoda es fina a propósito. chromedp se apoya en cdproto, un conjunto generado de bindings tipados en Go que cubren toda la superficie del DevTools Protocol, y el godoc de chromedp documenta ambas capas una al lado de la otra. Cuando no existe la acción de conveniencia, bajas a la llamada del dominio —network.Enable(), page.CaptureScreenshot(), runtime.Evaluate()— dentro del mismo Run. No hay muro entre "la API bonita" y "la API real", algo que no puede decirse de todos los drivers de navegador.

Las acciones de espera son donde se toma la mayoría de las decisiones del día a día, y hay más de las que la gente suele usar:

Acción de esperaSobre qué bloquea
WaitReady(sel)hasta que el nodo esté adjunto al DOM
WaitVisible(sel)hasta que el nodo esté realmente visible
WaitNotPresent(sel) / WaitNotVisible(sel)las inversas, útiles para spinners
Poll(js, res)evaluar una condición JavaScript a intervalos hasta que sea verdadera

La gestión de procesos es la otra pieza de mecánica que conviene conocer, porque determina si tu programa deja un navegador suelto detrás. chromedp arranca Chrome mediante exec.CommandContext de Go. Cancelar ese contexto mata el proceso. Ese único detalle de implementación explica tanto el buen comportamiento como la arista más incómoda que encontré en las pruebas.

La instalación es un binario de Go más un Chrome que debes aportar tú

go get github.com/chromedp/chromedp resolvió sin problemas a v0.16.0 y sin dramatismo, y el árbol de dependencias no incluye imports de cgo. Así que la afirmación "Go puro, sin dependencias externas" que verás repetida es cierta... pero solo respecto al módulo de Go.

No es cierta respecto al runtime. chromedp controla un Chrome externo, y sin Chrome en la máquina una ejecución falla de inmediato. Todas las mediciones que hice suministraron el ejecutable exacto mediante chromedp.ExecPath, apuntando a un Chrome for Testing headless shell 151.0.7922.10. No es una crítica —controlar un navegador requiere un navegador—, pero "sin dependencias externas" y "debes distribuir 155 MB de Chrome junto a tu binario" son historias de despliegue muy distintas, y solo una de ellas aparece en el README.

Hay una segunda trampa de configuración que me costó tiempo y conviene conocer antes de escribir código. El issue #1591 de chromedp informa que el runner de go test en Go 1.25+ cancela NewExecAllocator durante el arranque; el mismo código funciona bien como binario compilado. Yo construí un binario de prueba con go build y lo usé en todas las mediciones, en lugar de pasar nada por go test. El Go aquí era 1.26.5, macOS arm64. Si tu primera experiencia con chromedp es un archivo de test que muere durante el arranque de Chrome, lee ese issue antes de culpar a tu propio código.

Práctica: cuatro formas de leer la misma página, dos vacías

Measured results chart: Which read strategy saw each link?

El fixture es un servidor local en 127.0.0.1 que sirve tres clases de contenido que solo difieren en cuándo entran al DOM: un <a> estático en los bytes servidos, un <a> creado por un <script> inline durante el parse inicial, y un <a> creado por setTimeout un número configurable de milisegundos después del evento load. Los marcadores y hrefs de los dos enlaces creados por script se ensamblan en JavaScript a partir de fragmentos de cadena, de modo que no existe ningún literal contiguo en los bytes servidos. Por tanto, un "encontrado" prueba que Chrome ejecutó JavaScript, no que alguien leyó HTML.

El recall se calcula en Python frente a marcadores de verdad de referencia registrados antes de la prueba, no dentro del binario de Go, así que el probe no puede hacer trampa conociendo la respuesta. Cada estrategia se ejecutó tres veces; los conjuntos de encontrados fueron idénticos en las tres ejecuciones.

Estrategia de lecturaEnlace en HTML estáticoInyectado durante el parseInyectado 800 ms después de la cargaTiempo transcurrido
Navigate + lectura, sin esperaencontradoencontradono encontrado317 ms
WaitReady("body")encontradoencontradono encontrado107 ms
WaitVisible("#delayed-injected")encontradoencontradoencontrado912 ms
Poll hasta que aparezca el marcadorencontradoencontradoencontrado972 ms

Dos filas regresan con dos enlaces de tres. La lectura ingenua falla porque Navigate devuelve al llegar al evento load y el tercer enlace todavía no existe. WaitReady("body") falla por una razón más sutil y, en la práctica, peor: body ya está adjunto en load, así que la espera se satisface al instante y te hace sentir que hiciste lo correcto. Devolvió el resultado en 107 ms, más rápido que la ruta sin espera, y te dio la misma página incompleta.

Para confirmar el mecanismo en lugar de asumirlo, barrí el retraso de inyección y volví a ejecutar ambos extremos (recall-summary.json):

Retraso de inyección tras la cargaLa lectura sin espera lo veWaitVisible lo veTiempo de WaitVisible
0 mssí (condición de carrera)109 ms
100 msno208 ms
400 msno519 ms
800 msno911 ms
1500 msno1625 ms

El tiempo transcurrido de WaitVisible sigue el retraso de inyección en este fixture —100 a 208, 400 a 519, 800 a 911, 1500 a 1625—, evidencia de que bloqueó hasta que el nodo apareció en lugar de leer demasiado pronto. La fila de 0 ms es el límite: setTimeout(…, 0) puede dispararse antes de la lectura inmediata, así que la ruta sin espera puede atraparlo. A partir de 100 ms en adelante en este barrido, la ruta sin espera lo perdió en todas las ejecuciones.

En producción, el mismo error de timing puede producir HTML válido con cero filas extraídas y salir con código 0 si el pipeline no comprueba la cardinalidad de la salida. Ese es un modo de fallo plausible respaldado por el comportamiento del fixture, no un incidente medido aquí. Renderizar es solo la mitad del requisito; la lectura debe esperar una condición de nivel de aplicación vinculada a los datos deseados.

WaitReady y WaitVisible no compiten: responden preguntas distintas

La forma habitual de expresarlo es que WaitVisible es "más fiable" que WaitReady. Eso es lo bastante impreciso como para ser perjudicial. En una página con un nodo adjunto al DOM pero estilizado con display: none, ambos divergen claramente (waitsem-summary.json, tres ejecuciones idénticas):

Nodo objetivoAcciónResultadoTiempo
adjunto, display:noneWaitReadydevuelve~6 ms
adjunto, display:noneWaitVisibleexpira, context deadline exceeded4000 ms
nodo visibleWaitVisible, consulta por defectodevuelve4–12 ms
nodo visibleWaitVisible, ByIDdevuelve1–2 ms
nodo visibleWaitVisible, ByQuerydevuelve1 ms

WaitReady significa adjunto. WaitVisible significa visible. Si preguntas lo incorrecto, o bien te saltas contenido que nunca llegó a renderizarse, o bien te quedas bloqueado durante todo tu timeout en un nodo que nunca iba a ser visible. El comportamiento ante el deadline es limpio: un context deadline exceeded correcto exactamente a los 4 s, sin cuelgue ni estado zombi, que es más de lo que consiguen algunos drivers.

Un problema reportado no apareció. El issue #440 dice que WaitVisible("#id") se cuelga con la consulta por defecto, y eso no se reprodujo en v0.16.0: la consulta por defecto, ByID y ByQuery devolvieron el nodo visible en todas las ejecuciones. Que no se reproduzca no es lo mismo que estar arreglado: es una forma de selector en una sola página, lo cual no cierra el issue.

El defer cancel() que omitiste está sosteniendo el techo

Importan los conteos de procesos, no los valores de retorno. Cada ejecución de ciclo de vida usó un --user-data-dir único y contó procesos reales de browser Chrome con pgrep, filtrando los hijos renderer. Cada ruta se ejecutó tres veces (lifecycle-summary.json).

Ruta de salida (macOS, 3 ejecuciones cada una)Qué pasó con el chrome-headless-shell lanzadoTiempo
Cancelar el context y el allocatordesaparece13, 13 y 12 milisegundos
Salir del proceso de Go sin cancelarsobrevive a tu programa — cero procesos del navegador antes, uno después de salir del probe; tres orfanatos de tres ejecuciones

Cancelar es limpio, rápido y exactamente lo que promete exec.CommandContext. (A todos los huérfanos se les hizo force-kill después mediante el harness; el host quedó limpio.)

Esto es un comportamiento conocido, documentado y acotado por plataforma — la medición es mía, el hallazgo no. El tracker de chromedp lo ha cubierto desde varios ángulos: #774 describe el mismo no-cierre en FreeBSD, #752 informa de procesos Chromium colgados en macOS, y #562 junto con #1566 explican el mecanismo. Lo que yo añadí es el conteo de procesos y el tiempo a ambos lados del contraste, algo que esos reportes cualitativos no aportan.

El mecanismo en sí responde a una historia de build tags que merece conocerse. En el código fuente de v0.16.0, allocate_linux.go establece Pdeathsig = SIGKILL en el proceso hijo, así que Linux recibe una señal de muerte al morir el padre a nivel de kernel. allocate_other.go, que es lo que compila macOS, convierte esa llamada en una operación vacía. No existe una señal equivalente en darwin, así que nada mata a Chrome cuando tu programa termina. Mientras tanto, el texto del godoc suena como una promesa general —el comando por defecto "envía SIGKILL a cualquier navegador abierto cuando el programa Go finaliza"—, pero el acotamiento a Linux vive solo en código marcado por build tags que tendrías que ir a leer. Decir que la documentación sobrepromete es justo; decir que es un bug de chromedp no lo es.

La consecuencia práctica es la misma en cualquier caso: en macOS, defer cancel() es estructural. Si lo omites, cada ejecución deja un proceso de navegador huérfano. No probé Linux, así que no estoy generalizando el resultado de huérfanos allí; el código fuente sugiere que Linux se comporta distinto, y una implicación no es una medición.

Arranque en frío, concurrencia y las cosas aburridas que deciden tu despliegue

Measured results chart: Cold start and two concurrency shapes

102 ms fue la mediana desde proceso nuevo, allocator, context, navegación a localhost y primer Evaluate en cinco procesos, con un rango de 98 a 111 ms (coldstart-summary.json). En este fixture macOS arm64 con un headless shell ya caliente en disco, el arranque fue pequeño en comparación con la espera de contenido diferido. No se midieron contenedores, sistemas de archivos fríos, CI, entornos serverless ni navegación en producción.

En cuanto a concurrencia, chromedp te ofrece dos formas: un solo navegador con varios contextos hijos (pestañas), o varios navegadores independientes. Cuatro navegaciones, tres ejecuciones cada una (concurrency-summary.json):

ModoTiempo total (p50)RangoMáximo de procesos de navegador Chrome
Navegador compartido, 4 contextos hijos214 ms209–2191
4 navegadores separados264 ms261–2784

El hallazgo medido es el conteo de procesos: un proceso de navegador Chrome frente a cuatro para estas cuatro navegaciones triviales en local. Los rangos de tiempo no se solaparon, pero siguen siendo direccionales, no un benchmark de throughput. No se midieron RSS ni PSS, así que este test no demuestra ahorro de memoria.

El pequeño probe de rutas de error no es lo bastante detallado aquí como para sostener una afirmación de robustez: el borrador no identifica si cada condición salió a la superficie por estado HTTP, error de navegación, evento o lógica del harness. Considera el manejo de 500/enlace muerto como no informado hasta que se publique el resultado exacto de la API y el artefacto crudo.

No probado, y por tanto fuera de lo que cubren estos números: el comportamiento del ciclo de vida en Linux, concurrencia más allá de N=4 o con trabajo real por página, diferencias de memoria (conté procesos, no RSS), interceptación de red y captura de solicitudes, y los informes abiertos de timeout de WaitReady en #168 y #1593 — esos describen timeouts intermitentes, mientras que lo que yo medí es la semántica de la espera, que es otra pregunta. Una máquina, una versión de Chrome.

chromedp no es el único driver CDP en Go que pasó por este banco: rod se probó con el mismo fixture, el mismo harness, el mismo host y la misma versión de Chrome en la misma sesión, y tiene su propia reseña.

Pros y contras

Pros:

  • Acceso real a CDP: las acciones de conveniencia y las llamadas de dominio crudas de cdproto se combinan en el mismo Run, así que nunca te quedas sin techo de API.
  • Línea base de arranque en frío local de 102 ms p50, con un rango de 98–111 ms en el fixture macOS probado.
  • Cancelar reabsorbe Chrome en ~13 ms, de forma consistente, en cada ejecución.
  • Los contextos hijos comparten un solo proceso de navegador para N pestañas (1 proceso frente a 4 en navegadores separados).
  • Determinista en pruebas: los conjuntos de recall, la semántica de espera y los resultados de ciclo de vida fueron idénticos en tres repeticiones cada uno.
  • Manejo limpio de deadlines: WaitVisible ante una condición inalcanzable devolvió un context deadline exceeded correcto exactamente a los 4 s en lugar de colgarse.
  • Módulo puro de Go (sin cgo), con licencia MIT; aun así, en runtime sigue necesitando un ejecutable externo de Chrome.

Contras:

  • Requiere un Chrome externo en runtime; la fama de "sin dependencias" solo se refiere al módulo de Go.
  • WaitReady("body") es una trampa que parece correcta y se salta silenciosamente el contenido posterior a la carga: devolvió en 107 ms con una página incompleta.
  • La ruta ingenua Navigate + lectura pierde todo lo que se inyecte ~100 ms o más después de la carga, de forma determinista y sin error.
  • En macOS, salir sin cancel() deja el navegador huérfano (3/3 ejecuciones). Es un comportamiento conocido y acotado por plataforma, pero muy fácil de pisar.
  • La redacción del godoc sobre SIGKILL al salir suena universal cuando el mecanismo real vive en código con build tags solo para Linux.
  • go test en Go 1.25+ puede cancelar el arranque del allocator (#1591); mejor compilar un binario.
  • Devuelve un DOM, no datos estructurados: cada campo que quieras extraer es código de parseo que tú escribes y mantienes.
  • La etiqueta más reciente (v0.16.0) va por delante del último objeto de GitHub Release (v0.15.1), lo que vuelve algo confuso verificar versiones por un momento.

Para quién es chromedp y quién debería pasarlo por alto

Si tu servicio ya está en Go y necesitas un navegador real dentro de él, chromedp está muy cerca de ser la opción obvia. Sin proceso Node que vigilar, sin servidor WebDriver que mantener vivo, un único binario compilado más un Chrome que tú distribuyes o instalas. El modelo de context encaja tan directamente con las primitivas de concurrencia de Go que la vida útil del navegador acaba gobernada por la misma disciplina de defer que el resto del código. Y si necesitas algo que la API de conveniencia no cubre —eventos de red CDP, hooks precisos del ciclo de vida de la página, trucos a nivel de protocolo—, bajas a cdproto sin salir de la librería.

También encaja bien cuando quieres control explícito sobre la espera. Las acciones de espera son primitivas, no heurísticas; hacen exactamente lo que dicen, lo cual es una ventaja una vez aceptas que elegir bien ahora es tu responsabilidad.

Pásalo por alto si tu equipo no programa en Go: el compromiso con el lenguaje es el coste real, no la librería. Pásalo por alto si quieres una experiencia de auto-espera que adivine correctamente por ti, porque chromedp no adivina; hace exactamente lo que le pides y devuelve lo que la página tenía en ese momento. Pásalo por alto, o presupuestalo con seriedad, si lo que de verdad necesitas son registros estructurados y no un DOM: cada campo es un selector que escribes, pruebas y reparas cuando el sitio cambia. Y si tu único requisito es "consígueme los datos de estas 500 URLs", montar orquestación de navegador en Go es mucha maquinaria para esa petición. Nuestra guía de automatización de navegadores explica cuándo esa maquinaria vale la pena y cuándo no.

Alternativas, incluido dónde encaja nuestra propia pila

Dentro de la categoría de drivers de navegador, rod es otro driver CDP para Go, mientras que Playwright y Puppeteer son opciones del lado de Node, cubiertas en nuestro análisis de Playwright vs. Puppeteer. Sus contratos de espera no son intercambiables. Playwright hace auto-wait para la actionability antes de muchas acciones; eso tampoco le dice cuándo los datos de la aplicación han terminado de llegar después de la acción. chromedp te da primitivas de espera de nivel más bajo y deja tanto la actionability como las condiciones de preparación a nivel de aplicación en manos del llamador. Si estás revisando el panorama más amplio, nuestro resumen de scrapers open source que hemos probado cubre rastreadores estáticos y bibliotecas de extracción al otro lado de esta línea.

Reseña relacionada: Reseña de Browserless.

Un servicio de extracción gestionado es otra categoría. Cambia control del navegador y de los selectores por renderizado subcontratado y modelado de esquema. Eso puede ser útil cuando el entregable son registros estructurados en vez de un DOM, mientras que chromedp encaja mejor cuando el navegador debe seguir bajo el control de tu servicio en Go. Nosotros construimos Thunderbit, uno de esos servicios, pero no lo ejecutamos contra este fixture; por tanto, esta prueba no respalda ninguna comparación de equivalencia, latencia, calidad de extracción o coste con chromedp.

Prueba Thunderbit para extracción de datos web

Veredicto

¿Deberías usar chromedp? Sí, si programas en Go y quieres un navegador real bajo tu control. En este fixture local alcanzó el primer resultado del script en 102 ms p50, retiró Chrome en unos 13 ms tras la cancelación y usó un solo proceso de navegador para cuatro pestañas concurrentes. Son observaciones acotadas, no promesas universales de rendimiento; el atractivo duradero es el acceso directo a CDP desde Go cuando la capa de conveniencia ya no alcanza.

Solo dimensiona bien las afirmaciones, porque la reputación exagera dos cosas. "Go puro, sin dependencias" describe el módulo; en runtime sigues teniendo que distribuir y gestionar un binario de Chrome. Y "usa un navegador headless y obtendrás el contenido dinámico" solo es cierto cuando tu espera está anclada al nodo que quieres: una lectura ingenua y WaitReady("body") me devolvieron una página sin el contenido que se había inyectado 800 ms después de la carga, en silencio, cada vez. En macOS, defer cancel() no es una preferencia de estilo; si lo omites, fugas un navegador por ejecución, algo que es comportamiento conocido de la plataforma pero sigue siendo tu problema. Haz bien esas tres cosas y chromedp es uno de los drivers de navegador más predecibles que he medido. Hazlas mal y fallará en silencio, que es la peor forma en que puede fallar un scraper.

Prueba Thunderbit para extracción de datos web Get Started Free

Preguntas frecuentes

¿chromedp realmente ve contenido renderizado por JavaScript? Sí, pero solo con una espera vinculada al nodo que quieres. En un fixture donde un enlace se inyectaba 800 ms después del evento load, Navigate más una lectura lo perdió y WaitReady("body") también lo perdió, mientras que WaitVisible sobre ese nodo y un poll de JavaScript sí lo recuperaron, en tres de tres ejecuciones cada uno. Al barrer el retraso, la lectura sin espera falló en todos los valores desde 100 ms en adelante. Renderizar es necesario; esperar correctamente es lo que lo vuelve suficiente.

¿Cuál es la diferencia entre WaitReady y WaitVisible? WaitReady bloquea hasta que el nodo esté adjunto al DOM. WaitVisible bloquea hasta que sea realmente visible. En un nodo adjunto pero estilizado con display: none, WaitReady devolvió en unos 6 ms mientras que WaitVisible esperó hasta el deadline del contexto de 4 segundos y devolvió un context deadline exceeded limpio. Ninguno es "más fiable": responden preguntas distintas, y elegir el incorrecto es la verdadera trampa.

¿De verdad necesito defer cancel() con chromedp? En macOS, sí. Cancelar el context y el allocator reabsorbió el Chrome lanzado en 12–13 ms en cada ejecución; salir del proceso de Go sin cancelar dejó un proceso de navegador huérfano en las tres ejecuciones. Es un comportamiento conocido y acotado por plataforma: el tracker de chromedp documenta el mismo patrón de no-cierre en otros sistemas no Linux, y la muerte por parent-death que lo maneja vive en código con build tags solo para Linux. No probé Linux, así que trata el resultado de huérfanos como específico de macOS.

¿chromedp necesita Chrome instalado por separado? Sí. El módulo de Go en sí es puro Go y no usa cgo, pero controla un navegador externo y falla de inmediato si no hay uno. Yo proporcioné explícitamente un headless shell de Chrome for Testing 151.0.7922.10 mediante chromedp.ExecPath. La ventaja es que el coste de arranque es pequeño: un ciclo completo en frío hasta el primer resultado del script tuvo una mediana de 102 ms en cinco procesos nuevos, con un rango de 98–111 ms.

¿Conviene compartir un navegador entre pestañas o lanzar navegadores separados? Si lo que quieres es minimizar el número de procesos del navegador, conviene usar contextos hijos. Cuatro navegaciones locales a través de un solo navegador usaron 1 proceso de Chrome; cuatro navegadores separados usaron 4. El tiempo total también favoreció la configuración compartida (214 ms frente a 264 ms de mediana), pero cuatro páginas locales triviales no constituyen un benchmark de throughput. No se midió memoria. Los navegadores separados pueden seguir siendo la opción correcta cuando necesitas aislamiento de sesión más fuerte, distintos proxies o un radio de explosión menor en caso de fallo; esas compensaciones quedaron fuera de esta prueba.

Ke
Ke
CTO en Thunderbit | Científico de datos sénior y experto en ML Con casi una década de experiencia en aprendizaje automático y ciencia de datos, Ke Shen es exalumno de la Universidad de Columbia y antiguo científico de datos sénior en Walmart Labs. Con una sólida experiencia, reconocida por sus pares, en Python, R, Java y estadística, comparte conocimientos probados en el campo sobre cómo llevar algoritmos complejos de IA desde la teoría hasta una arquitectura lista para producción.
Tabla de contenidos
Thunderbit · Agente de datos web con IA

Extrae datos de cualquier página en 1 clic

Con la confianza de más de 250,000 usuarios
plan gratuito disponible
De la página web a la hoja de cálculo
Describe lo que necesitas: el agente de IA de Thunderbit lo extrae y lo exporta a Excel, Google Sheets, Airtable o Notion. Empieza gratis.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week