Skip to main content
Glama

web-speed-agent

PyPI version Python License: GPL v3

Automatización de navegador local + integración con Web Speed API para extracción web autenticada.

Dirige un agente de IA a cualquier sitio web —incluidos aquellos que requieren inicio de sesión— y obtén datos limpios y estructurados. Las credenciales permanecen en tu máquina. Solo el HTML extraído se envía al servidor.

pip install web-speed-agent
playwright install chromium

¿Quieres usar esto con Claude, Gemini u otros clientes de IA?

Consulta la Guía de instalación del servidor MCP: es la forma más sencilla de permitir que los agentes de IA inicien sesión y extraigan datos mediante lenguaje natural.


Cómo funciona

Your machine                           Web Speed server
─────────────────────────────────      ──────────────────────────
Playwright browser (local)
  ↓ navigates, logs in, clicks
  ↓ gets page HTML
  ↓ (no passwords sent)
agent.extract(html)         ────────→  Advanced extraction engine
                            ←────────  Structured JSON

Las credenciales nunca abandonan tu máquina. El servidor solo ve el HTML.


Related MCP server: Agent Identity MCP Server

Inicio rápido

import asyncio
from web_speed_agent import Agent

async def main():
    agent = Agent(api_key="wsp_...")       # or set WEBSPEED_API_KEY env var

    # Public pages — no browser needed
    result = await agent.map("https://techcrunch.com/some-article/")
    print(result["article"]["sections"])

    # Authenticated pages — browser runs locally
    agent.store_credential("mysite", "me@example.com", "mypassword")

    async with agent.browser(session_name="mysite") as browser:
        page = await browser.new_page()
        await page.goto("https://mysite.com/login")

        username, password = agent.get_credential("mysite")
        await page.fill('[name="email"]', username)
        await page.fill('[name="password"]', password)
        await page.click('button[type="submit"]')
        await page.wait_for_load_state("networkidle")

        # Now on a logged-in page — extract it
        html = await page.content()
        result = await agent.extract(html, page_type="listing")
        print(result["listing"]["items"])

asyncio.run(main())

Obtén una clave API en getwebspeed.io.


Instalación

Requisitos: Python 3.10+, una clave API de Web Speed

pip install web-speed-agent
playwright install chromium
export WEBSPEED_API_KEY="wsp_..."

Conceptos básicos

Agente

La clase principal. Gestiona credenciales, sesiones de navegador y llamadas a la API.

from web_speed_agent import Agent

# API key from argument
agent = Agent(api_key="wsp_...")

# API key from environment variable (recommended)
# export WEBSPEED_API_KEY="wsp_..."
agent = Agent()

# Use as async context manager (auto-closes HTTP client)
async with Agent() as agent:
    ...

Extracción de páginas públicas

No se necesita navegador para páginas que no requieren inicio de sesión:

# Fetch + extract in one call
result = await agent.map("https://example.com/article")

# With JavaScript rendering (for heavy SPAs)
result = await agent.map("https://example.com/spa", js=True)

Extracción de páginas autenticadas

Utiliza una sesión de navegador local. El navegador se ejecuta en tu máquina:

async with agent.browser(session_name="mysite") as browser:
    page = await browser.new_page()
    await page.goto("https://mysite.com/dashboard")
    html = await page.content()

result = await agent.extract(html)

El session_name guarda las cookies en ~/.webspeed/sessions/<name>/ para que las ejecuciones posteriores omitan el paso de inicio de sesión.


Gestión de credenciales

Las credenciales se almacenan en el llavero de tu sistema (macOS Keychain, Administrador de credenciales de Windows, secret-tool de Linux). Nunca se envían a los servidores de Web Speed.

# Store once
agent.store_credential("mysite", "me@example.com", "mypassword")

# Retrieve anywhere
username, password = agent.get_credential("mysite")

# Remove
agent.delete_credential("mysite")

Salida de extracción

El servidor devuelve datos estructurados conscientes del tipo de página:

# Article
result = await agent.extract(html, page_type="article")
# result["page_type"]    → "article"
# result["title"]        → "Article Title"
# result["author"]       → "Jane Smith"
# result["published_date"] → "2026-05-06"
# result["article"]["sections"] → [{"heading": "...", "paragraphs": [...]}]
# result["article"]["links"]    → [{"text": "...", "url": "..."}]

# Product
result = await agent.extract(html, page_type="product")
# result["product"]["name"]         → "Wireless Headphones"
# result["product"]["price"]        → "$99.99"
# result["product"]["availability"] → "In Stock"
# result["product"]["rating"]       → "4.5"
# result["product"]["specs"]        → {"Battery": "30h", ...}

# Listing (search results, category pages)
result = await agent.extract(html, page_type="listing")
# result["listing"]["items"] → [{"title": "...", "url": "...", "price": "..."}]

# Auto-detect (default)
result = await agent.extract(html)
# result["page_type"] → "article" | "product" | "listing" | "other"

Todos los resultados incluyen engine: "advanced" — entre un 60 y un 85% más eficiente en tokens que el HTML sin procesar.


Ejemplos

Monitor de precios

import asyncio
from web_speed_agent import Agent

async def check_price(url: str, site_name: str) -> str:
    async with Agent() as agent:
        agent.store_credential(site_name, "me@example.com", "password", overwrite=True)

        async with agent.browser(session_name=site_name) as browser:
            page = await browser.new_page()

            # Login
            await page.goto(f"https://{site_name}.com/login")
            user, pwd = agent.get_credential(site_name)
            await page.fill('[name="email"]', user)
            await page.fill('[name="password"]', pwd)
            await page.click('button[type="submit"]')
            await page.wait_for_load_state("networkidle")

            # Check product
            await page.goto(url)
            await page.wait_for_load_state("networkidle")
            html = await page.content()

        result = await agent.extract(html, page_type="product")
        return result.get("product", {}).get("price", "unknown")

price = asyncio.run(check_price("https://example.com/product/123", "example"))
print(f"Current price: {price}")

Leer un panel privado

import asyncio
from web_speed_agent import Agent

async def get_dashboard_data():
    async with Agent() as agent:
        async with agent.browser(session_name="analytics") as browser:
            page = await browser.new_page()

            # Login (first run only — session persists after)
            creds = agent.get_credential("analytics")
            if not creds:
                agent.store_credential("analytics", "me@company.com", "password")
                creds = agent.get_credential("analytics")

            await page.goto("https://analytics.company.com/login")
            await page.fill('[name="email"]', creds[0])
            await page.fill('[name="password"]', creds[1])
            await page.click('button[type="submit"]')
            await page.wait_for_load_state("networkidle")

            # Navigate to dashboard
            await page.goto("https://analytics.company.com/dashboard")
            await page.wait_for_selector(".metrics-table", timeout=10000)
            html = await page.content()

        result = await agent.extract(html)
        return result

asyncio.run(get_dashboard_data())

Scraping de varias páginas mientras estás conectado

import asyncio
from web_speed_agent import Agent

async def scrape_inbox():
    async with Agent() as agent:
        async with agent.browser(session_name="webmail") as browser:
            page = await browser.new_page()

            # Login
            await page.goto("https://mail.example.com/login")
            user, pwd = agent.get_credential("webmail")
            await page.fill('[name="username"]', user)
            await page.fill('[name="password"]', pwd)
            await page.click('[type="submit"]')
            await page.wait_for_load_state("networkidle")

            # Scrape multiple pages
            emails = []
            for page_num in range(1, 4):
                await page.goto(f"https://mail.example.com/inbox?page={page_num}")
                await page.wait_for_load_state("networkidle")
                html = await page.content()
                result = await agent.extract(html, page_type="listing")
                emails.extend(result.get("listing", {}).get("items", []))

        return emails

asyncio.run(scrape_inbox())

Integración con agentes de IA (MCP)

El servidor MCP incluido permite que Claude Desktop, Gemini CLI y cualquier agente compatible con MCP utilicen el SDK directamente. El agente puede iniciar sesión, navegar, hacer clic y extraer, todo a través del lenguaje natural.

Inicia el servidor MCP:

WEBSPEED_API_KEY="wsp_..." python3 agent_mcp_server.py

Añadir a Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "web-speed-agent": {
      "command": "python3",
      "args": ["/path/to/agent_mcp_server.py"],
      "env": {
        "WEBSPEED_API_KEY": "wsp_..."
      }
    }
  }
}

Añadir a Gemini CLI (~/.gemini/settings.json):

{
  "mcpServers": {
    "web-speed-agent": {
      "command": "python3.11",
      "args": ["/path/to/agent_mcp_server.py"],
      "env": {
        "WEBSPEED_API_KEY": "wsp_...",
        "PYTHONPATH": "/path/to/web-speed-agent"
      }
    }
  }
}

Luego dile al agente:

"Guarda mis credenciales para united — usuario me@example.com, contraseña mi_contraseña"

"Inicia sesión en united.com y búscame el vuelo más barato de SFO a JFK el próximo viernes"

Herramientas MCP disponibles:

Herramienta

Descripción

store_credential

Guardar inicio de sesión en el llavero del sistema

login

Abrir navegador + iniciar sesión

navigate

Ir a una URL en la sesión activa

extract_page

Obtener datos estructurados de la página actual

click

Hacer clic en un botón o enlace

fill_field

Escribir en un campo de formulario

submit_form

Enviar un formulario

close_browser

Finalizar la sesión del navegador

account_info

Comprobar el saldo de créditos de la API


Referencia de la API

Agent

Agent(
    api_key: str | None = None,
    server_url: str | None = None,
    config_dir: str = "~/.webspeed",
    headless: bool = True,
)

Parámetro

Descripción

api_key

Clave API de Web Speed. Utiliza la variable de entorno WEBSPEED_API_KEY si no se proporciona.

server_url

Sobrescribir la URL del servidor API. Por defecto: https://api.getwebspeed.io.

config_dir

Directorio para configuración, sesiones y registros. Por defecto: ~/.webspeed.

headless

Ejecutar el navegador sin interfaz gráfica. Por defecto: True.


agent.browser()

agent.browser(
    session_name: str | None = None,
    headless: bool | None = None,
    proxy: str | None = None,
) -> ManagedBrowser

Devuelve un gestor de contexto asíncrono. Dentro del bloque, llama a .new_page() para obtener una Page de Playwright.

Parámetro

Descripción

session_name

Persistir cookies en ~/.webspeed/sessions/<name>/. None = sin persistencia.

headless

Sobrescribir el headless de la instancia para esta sesión.

proxy

URL del proxy, p. ej. "socks5://localhost:1080".

Los nombres de sesión deben ser alfanuméricos + guiones/guiones bajos, máximo 64 caracteres.


agent.extract()

await agent.extract(
    html: str,
    page_type: str = "auto",
) -> dict

Envía HTML a la API de Web Speed. Cuesta 1 crédito.

Parámetro

Descripción

html

Cadena HTML sin procesar (p. ej. desde page.content()).

page_type

"article", "product", "listing" o "auto".


agent.map()

await agent.map(
    url: str,
    js: bool = False,
) -> dict

Obtiene y extrae una URL pública a través del servidor. No se necesita navegador local. Cuesta 1 crédito.

Parámetro

Descripción

url

URL de la página. Debe ser http:// o https://.

js

Renderizar JavaScript antes de extraer.


agent.account()

await agent.account() -> dict

Devuelve: credits, tier, status, lifetime (total/aciertos/fallos).


agent.store_credential()

agent.store_credential(
    site: str,
    username: str,
    password: str,
    overwrite: bool = False,
) -> None

Guarda en el llavero del sistema. Lanza CredentialError si la credencial existe y overwrite=False.


agent.get_credential()

agent.get_credential(site: str) -> tuple[str, str] | None

Devuelve (username, password) o None si no se encuentra.


agent.delete_credential()

agent.delete_credential(site: str) -> None

Elimina la credencial del llavero.


Excepciones

from web_speed_agent import (
    WebSpeedError,          # Base exception
    AuthenticationError,    # Invalid/missing API key
    InsufficientCreditsError, # No credits remaining
    APIError,               # API returned 4xx/5xx
    RateLimitError,         # 429 Too Many Requests
    CredentialError,        # Keychain error
    BrowserError,           # Playwright error
    NetworkError,           # Timeout or DNS failure
    PlaywrightNotInstalledError, # Run: playwright install chromium
)
from web_speed_agent import Agent, InsufficientCreditsError, NetworkError

try:
    result = await agent.extract(html)
except InsufficientCreditsError:
    print("Out of credits — top up at getwebspeed.io")
except NetworkError as e:
    print(f"Connection failed: {e}")

Configuración

Variables de entorno

Variable

Descripción

WEBSPEED_API_KEY

Clave API (recomendado sobre el archivo de configuración)

WEBSPEED_SERVER_URL

Sobrescribir la URL del servidor (debe ser https://)

Archivo de configuración

~/.webspeed/config.yaml — creado automáticamente en la primera ejecución. Permisos establecidos en 0o600 (solo propietario).

api:
  server_url: https://api.getwebspeed.io
  timeout: 30

browser:
  headless: true

Archivos de sesión

Las sesiones de navegador persistentes se almacenan en ~/.webspeed/sessions/<name>/storage.json.

  • Permisos: 0o600 (solo propietario)

  • Contiene: cookies, localStorage, sessionStorage

  • Seguro de eliminar: el agente volverá a autenticarse en la siguiente ejecución


Seguridad

Qué abandona tu máquina

Cuando llamas a agent.extract(html), el HTML de la página se envía a la API de Web Speed para su procesamiento. Todo lo demás permanece local.

Datos

A dónde van

Credenciales de inicio de sesión

Nunca abandonan tu máquina (solo llavero del sistema)

Cookies / sesión del navegador

Nunca abandonan tu máquina (Playwright local)

HTML de la página

Enviado por HTTPS a la API de Web Speed para extracción

JSON extraído

Devuelto a ti

Limpieza de HTML (activada por defecto)

Antes de que se transmita cualquier HTML, el SDK lo limpia automáticamente de forma local:

  • Bloques <script> y <style> en línea eliminados

  • Campos de formulario ocultos con nombres relacionados con la autenticación (csrf, token, nonce, session, etc.) tienen sus valores vaciados

  • Atributos de contenido <meta> sensibles borrados

  • Comentarios HTML eliminados

El contenido visible (texto, enlaces, tablas, encabezados, datos de productos) permanece intacto.

# Default: scrubbing is on
result = await agent.extract(html)

# Turn off only if the page has no sensitive data
result = await agent.extract(html, scrub=False)

# Or scrub manually and inspect before sending
from web_speed_agent import scrub
clean_html = scrub(raw_html)
print(clean_html)  # inspect what will be sent
result = await agent.extract(clean_html, scrub=False)

Manejo de datos del lado del servidor

  • HTML procesado solo en memoria — nunca escrito en disco, nunca registrado, nunca almacenado en caché

  • Páginas protegidas por autenticación nunca almacenadas en caché — las páginas que requieren inicio de sesión están explícitamente excluidas del registro compartido

  • Los registros de uso solo almacenan: un hash de tu clave API, un hash de la URL (o "sdk-extract"), marca de tiempo y tipo de página detectado — sin contenido

  • Sin HTML sin procesar en respuestas de error — las excepciones se desinfectan antes de devolver cualquier error

Otras protecciones

  • Credenciales almacenadas en el llavero del sistema, nunca en archivos, nunca enviadas a servidores

  • Archivos de sesión escritos con permisos 0o600 (lectura/escritura solo para el propietario)

  • Directorio de configuración creado con permisos 0o700

  • TLS siempre verificadoverify=True en todas las llamadas HTTP, no se puede desactivar

  • HTTPS forzadoserver_url debe comenzar con https://, HTTP plano rechazado

  • Prevención de recorrido de rutas — nombres de sesión validados contra una lista blanca [a-zA-Z0-9_-]

  • Sin registro de credenciales — las contraseñas nunca aparecen en registros o mensajes de error


Licencia

GNU General Public License v3.0 — ver LICENSE.

El uso de la API de Web Speed está sujeto a los Términos de servicio de Web Speed.

A
license - permissive license
A
quality
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Provides an MCP-native agent browser that enables autonomous agents to perceive and interact with web pages through stealth browsing, identity borrowing, and WAAP detection.
    9
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables MCP-capable runtimes to read agent message rooms, sign and post public messages, and create or verify Ed25519 contribution proofs for Technocore.
    MIT

View all related MCP servers

Related MCP Connectors

  • Agent-first web hosting: deploy sites, apps, databases and domains over MCP.

  • Hosted AgentLux MCP server for marketplace, identity, creator, services, and social flows.

  • MCP Server for agents to onboard, pay, and provision services autonomously with InFlow

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dominic-Pi-Sunyer/web-speed-agent'

If you have feedback or need assistance with the MCP directory API, please join our Discord server