Volver al blog

ScrapeGraphAI + Scrapeless: Apunte SmartScraperGraph a un Navegador en la Nube

Emily Chen
Emily Chen

Advanced Data Extraction Specialist

07-Aug-2026

TL;DR:

  • ScrapeGraphAI extrae con un aviso en lugar de selectores, pero la recuperación por debajo es un navegador Playwright ordinario lanzado en tu propia máquina.
  • pip install scrapegraphai te da el cliente de Playwright y no el binario del navegador, por lo que el cargador estándar falla en una instalación nueva hasta que también ejecutes playwright install chromium.
  • ChromiumLoader elige su método de obtención con getattr(self, f"ascrape_{self.backend}"), así que backend nombra un método en lugar de elegir de una lista fija — esa es la costura en la que cuelga un backend de obtención personalizado.
  • Una subclase corta con un método ascrape_scrapeless extrae a través del Navegador de Scraping Sin Desperdicios sobre una conexión CDP wss:// y no necesita que el navegador esté instalado localmente.
  • FetchNode importa ChromiumLoader por nombre, así que volver a enlazar fetch_node.ChromiumLoader es lo que hace que el gráfico use realmente tu subclase. Si omites eso, un cargador que parece correcto se ignora silenciosamente.
  • Todo el pipeline funciona en un modelo local: SmartScraperGraph con ollama/qwen2.5:0.5b devuelve pares estructurados de autor y cita sin clave de modelo en la nube.
  • Comienza en el plan gratuito de Scrapeless y mueve la obtención fuera de tu laptop.

El Navegador de Scraping Sin Desperdicios es un navegador en la nube al que te conectas en lugar de lanzar. Escucha en un punto de conexión CDP WebSocket seguro, lo que es importante aquí porque la capa de obtención de ScrapeGraphAI es Playwright por debajo y Playwright puede adjuntarse a un navegador que no inició.

ScrapeGraphAI es la parte del pipeline de la que la gente habla: describe lo que quieres en una oración, y un gráfico de nodos convierte una página en JSON estructurado sin un solo selector CSS. La parte de la que nadie habla es el primer nodo. Antes de que cualquier modelo vea algo, FetchNode tiene que producir HTML, y hace eso lanzando Chromium en cualquier máquina en la que se esté ejecutando el script. Cada tutorial en la primera página de resultados de búsqueda configura ese nodo de exactamente una manera, con un diccionario proxy, y se detiene ahí.

Esta guía se centra en la capa de obtención en su lugar: dónde vive, cuál es el punto de extensión real y cómo enrutarlos a través de un navegador en la nube. Cada bloque a continuación se ejecutó en vivo, incluida la extracción.

Dónde Vive Realmente la Capa de Obtención de ScrapeGraphAI

FetchNode es el nodo de entrada de casi todos los gráficos, y termina en una de unas pocas ramas. Si configuras browser_base, scrape_do o plasmate en la configuración del nodo, se transfiere a un cargador de proveedor en scrapegraphai/docloaders/. De lo contrario, se desplaza a ChromiumLoader, que es la ruta predeterminada y la que casi todos usan.

Esa alternativa es importante por dos razones. Los backends de obtención de proveedores ya son una forma establecida en este código base en lugar de algo que estás inventando — browser_base.py y scrape_do.py se envían en el repositorio. Y ChromiumLoader elige su propio método de obtención por nombre:

python Copy
scraping_fn = getattr(self, f"ascrape_{self.backend}")

backend no se valida contra un enum fijo. Es interpolación de cadenas en una búsqueda de atributos, por lo que cualquier método llamado ascrape_<something> se vuelve accesible configurando backend a <something>. Esa es la costura.

El ascrape_playwright predeterminado llama a p.chromium.launch(...), que inicia un proceso de navegador localmente. Cambiar eso por una conexión WebSocket a un navegador que ya se está ejecutando es un cambio de una llamada.

Prerrequisitos

  • Python 3.10 o posterior.
  • Una clave de API de Scrapeless desde el panel de control, exportada como SCRAPELESS_KEY.
  • Ollama ejecutándose localmente con un modelo descargado, si quieres seguir el paso de extracción sin una clave de modelo en la nube.
  • No se requiere ningún binario de navegador local para el camino en la nube. Solo necesitas uno si también quieres ejecutar el cargador estándar.

Instalar

bash Copy
pip install "scrapegraphai==2.1.6" "langchain-ollama==1.1.0"

Playwright llega como una dependencia, pero sus navegadores no. Esa distinción causa el primer fallo que la mayoría de las personas encuentran.

bash Copy
export SCRAPELESS_KEY="your_api_key_here"

Lo Que Realmente Recupera Una Instalación Nueva

Ejecuta el cargador estándar inmediatamente después de instalar y no alcanza la página en absoluto.

python Copy
from scrapegraphai.docloaders import ChromiumLoader

try:
    docs = ChromiumLoader(["https://quotes.toscrape.com/"], headless=True).load()
    print("chars:", len(docs[0].page_content))
except Exception as exc:
    print(f"{type(exc).__name__}: {str(exc).split(' at /')[0]}")
text Copy
RuntimeError: Failed to scrape after 1 attempts: BrowserType.launch: Executable doesn't exist

El cliente de Playwright está instalado; el Chromium que quiere lanzar no lo está. playwright install chromium lo arregla y cuesta unos pocos cientos de megabytes en cada máquina que ejecuta el gráfico — una imagen CI, un contenedor, cada laptop de desarrollo. El camino en la nube omite eso por completo, porque el navegador que impulsa ya se está ejecutando en otra parte.

Subclase ChromiumLoader, añade un método nombrado para el backend que deseas, y vuelve a enlazar self.backend después de que se haya ejecutado super().__init__.

python Copy
import os

from scrapegraphai.docloaders import ChromiumLoader

CDP = (
    "wss://browser.scrapeless.com/api/v2/browser"
    f"?token={os.environ['SCRAPELESS_KEY']}&sessionTTL=180&proxyCountry=ANY"
)


class ScrapelessLoader(ChromiumLoader):
    """Fetch through a remote CDP browser instead of launching one locally."""

    def __init__(self, urls, **kwargs):
        kwargs.pop("backend", None)
        super().__init__(urls, backend="playwright", **kwargs)
        self.backend = "scrapeless"

    async def ascrape_scrapeless(self, url: str, browser_name: str = "chromium") -> str:
        from playwright.async_api import async_playwright

        async with async_playwright() as p:
            browser = await p.chromium.connect_over_cdp(CDP)
            page = await browser.new_page()
            await page.goto(url, wait_until=self.load_state)
            html = await page.content()
            await browser.close()
            return html


loader = ScrapelessLoader(["https://quotes.toscrape.com/"])
print("dispatches to:", getattr(loader, f"ascrape_{loader.backend}").__name__)
docs = loader.load()
print("chars:", len(docs[0].page_content))
print("Einstein present:", "Einstein" in docs[0].page_content)

Dos detalles realizan el trabajo. super().__init__ se llama con backend="playwright" porque el constructor ejecuta una verificación de importación contra esa cadena, y playwright es el nombre que se resuelve; self.backend se reasigna para que el despachador en lazy_load encuentre ascrape_scrapeless. Mantén browser_name en la firma con un valor predeterminado: el despachador llama al método solo con la URL.

text Copy
dispatches to: ascrape_scrapeless
chars: 10968
Einstein present: True

La página regresa completamente renderizada desde un navegador que nunca existió en esta máquina. proxyCountry acepta un código de dos letras cuando necesitas que la solicitud salga de un país particular, y sessionTTL limita cuánto tiempo se mantiene abierta la sesión remota.

Haz que el gráfico lo use

Instanciar la subclase tú mismo demuestra que la recuperación funciona, pero el gráfico no la recogerá por sí solo. FetchNode hace from ..docloaders import ChromiumLoader y luego llama a ese nombre directamente, por lo que la clase que usa el gráfico es la que está vinculada en el espacio de nombres del módulo del nodo. Reasígalo antes de construir el gráfico.

python Copy
import scrapegraphai.nodes.fetch_node as fetch_node

fetch_node.ChromiumLoader = ScrapelessLoader

Este es el paso que decide si alguno de los anteriores tiene efecto. Una subclase que está escrita correctamente pero nunca vinculada produce un gráfico que se ejecuta, tiene éxito y silenciosamente recupera a través del navegador local todo el tiempo.

Debido a que el reemplazo mantiene la misma firma del constructor, todo FetchNode ya pasa: headless, storage_state y cualquier cosa que pongas en loader_kwargs continúa llegando intacta.

¿Listo para mover la recuperación fuera de tus propias máquinas? Crea una cuenta gratuita en Scrapeless y apunta tu primer gráfico a ello.

Ejecutar el gráfico completo en un modelo local

Con el cargador vinculado, SmartScraperGraph se comporta normalmente. Apuntar el bloque llm a Ollama mantiene toda la tubería fuera de APIs pagadas, lo que hace que la capa de recuperación sea barata para iterar.

python Copy
import os

import scrapegraphai.nodes.fetch_node as fetch_node
from scrapegraphai.docloaders import ChromiumLoader
from scrapegraphai.graphs import SmartScraperGraph

CDP = (
    "wss://browser.scrapeless.com/api/v2/browser"
    f"?token={os.environ['SCRAPELESS_KEY']}&sessionTTL=180&proxyCountry=ANY"
)


class ScrapelessLoader(ChromiumLoader):
    def __init__(self, urls, **kwargs):
        kwargs.pop("backend", None)
        super().__init__(urls, backend="playwright", **kwargs)
        self.backend = "scrapeless"

    async def ascrape_scrapeless(self, url: str, browser_name: str = "chromium") -> str:
        from playwright.async_api import async_playwright

        async with async_playwright() as p:
            browser = await p.chromium.connect_over_cdp(CDP)
            page = await browser.new_page()
            await page.goto(url, wait_until=self.load_state)
            html = await page.content()
            await browser.close()
            return html


fetch_node.ChromiumLoader = ScrapelessLoader

graph = SmartScraperGraph(
    prompt="List the quote authors on this page.",
    source="https://quotes.toscrape.com/",
    config={
        "llm": {
            "model": "ollama/qwen2.5:0.5b",
            "temperature": 0,
            "format": "json",
            "model_tokens": 4096,
        },
        "verbose": False,
        "headless": True,
    },
)
result = graph.run()
print("keys:", sorted(result))
print("authors:", [item["author"] for item in result["content"]][:4])
text Copy
keys: ['content']
authors: ['Albert Einstein', 'J.K. Rowling', 'Jane Austen', 'Marilyn Monroe']

Los autores regresan correctos y la estructura es correcta. El texto de cita de este modelo es menos confiable: un modelo de parámetro de 0.5B recorta y fusiona cadenas en una larga lista, pero la capa de recuperación entregó toda la página, y el modelo es el factor limitante más que la tubería. Mueve la clave model a un modelo local más grande o a uno alojado y el mismo gráfico produce texto más limpio a través del mismo cargador.

La salida estructurada en absoluto es el punto aquí: el gráfico lee el documento HTML analizado que el navegador remoto renderizó, no el marcado en bruto que un cliente HTTP normal habría recibido. Configurar format a json le pide al modelo una salida que se ajuste a el formato de intercambio JSON, que es lo que hace que el resultado sea directamente subscripable en lugar de una cadena que debes analizar.

Conclusión

La capa de recuperación de ScrapeGraphAI es más configurable de lo que sugieren los tutoriales, y el punto de configuración no es loader_kwargs — es la cadena backend, que se resuelve en un nombre de método. Una subclase con un método ascrape_scrapeless mueve la recuperación a un navegador en la nube, y una reasignación de fetch_node.ChromiumLoader hace que el gráfico lo use.

Verifica la reasignación primero si los resultados parecen inalterados. Una subclase que nunca se vincula falla en silencio en lugar de ruidosamente, y el síntoma es un gráfico que funciona exactamente tan bien como lo hacía antes. Confirmar el cargador por sí solo, como en el paso medio anterior, separa un problema de recuperación de un problema de modelo en una ejecución.

En cuanto a hacia dónde se dirige el control del navegador como estándar, la especificación WebDriver BiDi vale la pena seguir. La guía de Playwright y Scraping Browser cubre la conexión misma con más profundidad, los detalles del plan están en la página de precios de Scrapeless, y los parámetros de sesión se encuentran en la documentación de Scrapeless.

FAQ

Q: ¿Necesito instalar un navegador para usar la ruta en la nube?

No. pip install scrapegraphai proporciona el cliente Playwright, que es todo lo que connect_over_cdp necesita, porque el navegador que se está controlando ya se está ejecutando de forma remota. Solo necesitas playwright install chromium si también deseas ejecutar el cargador local de stock.

P: ¿Por qué mi cargador personalizado es ignorado por el gráfico?
Casi siempre porque fetch_node.ChromiumLoader nunca fue reasignado. FetchNode importa la clase por nombre y llama a ese nombre, por lo que solo cambiar la subclase no altera nada: el nodo sigue construyendo la clase original. Reasigna el atributo en el módulo antes de construir el gráfico.

P: ¿Puedo usar loader_kwargs en lugar de una subclase?

Para proxies y opciones de lanzamiento del navegador, sí: loader_kwargs fluye directamente hacia ChromiumLoader. Sin embargo, no puede redirigir la obtención a un navegador remoto, ya que el método estándar llama a chromium.launch(), que siempre inicia un proceso local. Cambiar el destino significa cambiar el método, lo que significa una subclase.

P: ¿Esto funciona con gráficos que no sean SmartScraperGraph?

Sí. La reasignación ocurre a nivel de nodo, y FetchNode es el nodo de entrada para otros tipos de gráficos también, por lo que cualquier gráfico que obtenga una URL pasa por el mismo cargador una vez que el atributo está vinculado.

P: ¿Qué controla sessionTTL?

Cuánto tiempo la sesión del navegador remoto permanece abierta, en segundos. Configúralo cómodamente por encima del tiempo que tarda una sola obtención; la sesión se cierra cuando la conexión finaliza o la ventana caduca, lo que ocurra primero.

P: ¿Puedo mantener las cookies o una sesión iniciada entre obtenciones?

storage_state ya es pasado a través de FetchNode y llega al constructor de la subclase sin cambios, por lo que el archivo de estado de almacenamiento estándar de Playwright funciona. Aplícalo al crear el contexto en el navegador remoto en lugar de al lanzarlo, ya que el navegador remoto no es lanzado por tu código.

P: ¿Es un modelo local de 0.5B lo suficientemente bueno para una extracción real?

Para páginas cortas con una forma simple, produce una estructura utilizable, como se mencionó. Las páginas más largas y los esquemas anidados son donde se degrada: el texto se recorta y los campos se fusionan. Trata un modelo local pequeño como una forma de iterar sobre la capa de obtención de manera económica, y luego cambia la clave model para ejecuciones en producción.

En Scrapeless, solo accedemos a datos disponibles públicamente y cumplimos estrictamente con las leyes, regulaciones y políticas de privacidad del sitio web aplicables. El contenido de este blog es sólo para fines de demostración y no implica ninguna actividad ilegal o infractora. No ofrecemos garantías y renunciamos a toda responsabilidad por el uso de la información de este blog o enlaces de terceros. Antes de realizar cualquier actividad de scraping, consulte a su asesor legal y revise los términos de servicio del sitio web de destino u obtenga los permisos necesarios.

Artículos más populares

Catalogar