Saltar al contenido
Empezar gratis
Volver al blog

Cómo utilizar WebScrapingAPI: Guía de la API de scraping y de Web MCP

Robert MunceanuÚltima actualización el 28 min read
Cómo utilizar WebScrapingAPI: Guía de la API de scraping y de Web MCP
En resumen: Para usar la Scraper API de WebScrapingAPI, envía una solicitud GET a https://api.webscrapingapi.com/v2 con tu api_key y una url codificada, y lee la página en la respuesta. Úsala cuando ya sabes qué URL necesitas; elige la SERP API para resultados de búsqueda, la Browser API para clics y esperas, y Web MCP para conectar agentes de IA. Esta guía también cubre el renderizado, los errores y los costes.

Tienes una lista de URL y necesitas recuperar las páginas a las que dan acceso de forma fiable, sin tener que utilizar tus propios proxies ni navegadores sin interfaz gráfica. Esa es precisamente la función que cumple la API Scraper de WebScrapingAPI. ¿Eres nuevo en este tema? Empieza por saber qué es el web scraping. Al final de esta guía, enviarás tu primera solicitud con tu clave en una variable de entorno, añadirás la visualización, la ubicación y la extracción solo cuando sea necesario para cada objetivo, gestionarás todos los errores documentados y harás un seguimiento del coste de cada llamada. También sabrás cuándo es más adecuado utilizar la API de navegador, la API SERP o Web MCP.

Inicio rápido

  • Punto final: https://api.webscrapingapi.com/v2
  • Dos parámetros obligatorios: api_key y una URL codificada url
  • Prueba gratuita: 5.000 solicitudes
  • Sin recargos: la renderización de JavaScript y la geolocalización no suponen ningún coste adicional

¿Qué es la API Scraper de WebScrapingAPI y cuándo debes utilizarla?

La API Scraper es un punto final REST que toma una URL de destino y devuelve la página en formato HTML, JSON o como campos seleccionados. WebScrapingAPI se encarga de gestionar los proxies, la rotación, la gestión del acceso, los reintentos y la renderización opcional en JavaScript. Úsala cuando ya sepas qué URL necesitas y quieras obtener el contenido de la página sin tener que gestionar una infraestructura de scraping.

Tu aplicación proporciona la URL de destino y cualquier contexto de solicitud, como un país o un formato de salida. WebScrapingAPI gestiona la capa de acceso detrás de esa solicitud: enrutamiento y rotación de proxies, estrategias de gestión de acceso, reintentos internos hacia el destino y un navegador cuando solicitas la representación. Lo que se devuelve es el cuerpo de la página, un contenedor JSON con metadatos, un DOM JSON o solo los campos que hayas seleccionado. Cuando una solicitud no se puede completar, recibes un error explícito y documentado en lugar de contenido erróneo sin aviso previo.

Todo lo que queda fuera de esa capa es responsabilidad tuya: fuentes, finalidad, análisis, validación y almacenamiento. La API elimina el trabajo relacionado con el proxy y el navegador, pero no el del proceso de procesamiento.

Todos los planes, incluido el de prueba, incluyen renderización en JavaScript, segmentación por país en todas las zonas geográficas y rotación de proxies con gestión de accesos. Los planes se diferencian por el volumen de solicitudes, la concurrencia y la asistencia técnica, no por las características.

¿Aún no tienes claro si una API gestionada es adecuada para ti? Infórmate sobre las ventajas e inconvenientes de utilizar una API de scraping en lugar de tu propia pila tecnológica. Si necesitas resultados de búsqueda, clics y tiempos de espera en una página, o registros estructurados en lugar de páginas, pasa directamente a elegir entre las cinco API de Web Access.

¿Cómo envías tu primera solicitud a la API de scraping?

Crea una cuenta, copia tu clave de API de la pestaña «Web Scraping API» del panel de control y guárdala en una variable de entorno. A continuación, envía una solicitud GET a https://api.webscrapingapi.com/v2 con api_key y una URL codificada. Deja que tu biblioteca HTTP se encargue de codificar los parámetros y nunca llames a la API desde el código del navegador.

  1. Regístrate. Crea una cuenta en WebScrapingAPI y confírmala. La versión de prueba te ofrece 5.000 solicitudes.
  2. Copia tu clave. En el panel de control, abre la pestaña «Web Scraping API» en el panel de la izquierda y copia tu clave API.
  3. Guárdala como un secreto. Introduce la clave en una variable de entorno en el servidor que realizará las llamadas, tal y como se muestra a continuación.
  4. Envía la solicitud. Utiliza uno de los ejemplos que aparecen a continuación.
export WSA_API_KEY=...   # paste your key here, never into code

Las claves deben guardarse en secretos del lado del servidor, nunca en el código del navegador, aplicaciones móviles, repositorios públicos, registros, capturas de pantalla o cuadernos compartidos. Cualquier persona que tenga la clave puede agotar las solicitudes de tu plan.

curl

curl --get "https://api.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "url=https://example.com" \
  --data-urlencode "render_js=1" \
  --data-urlencode "country=us"

--get con --data-urlencode indica a curl que incluya cada parámetro en la cadena de consulta y lo codifique con el símbolo de porcentaje por ti.

Python (Python 3 con pip install requests)

Este pequeño cliente es en el que se basa el resto de la guía. Lee la clave de WSA_API_KEY, deja que Requests codifique la cadena de consulta mediante params= (consulta la guía de inicio rápido de Requests) y vuelve a intentar solo los errores que merecen la pena reintentar.

"""Minimal Scraper API client: env-var key, safe retries, optional JSON envelope.

Usage:
  export WSA_API_KEY=...            # never hardcode the key
  python wsa_scrape.py https://example.com
  python wsa_scrape.py https://example.com --render --country us --json
  python wsa_scrape.py https://example.com --dry-run   # print the request, no call
"""
import argparse
import json
import os
import sys
import time

import requests

ENDPOINT = "https://api.webscrapingapi.com/v2"
RETRYABLE = {429, 500, 503}          # back off and retry
FINAL = {400, 401, 403, 406}         # fix the request or the account; do not retry


def build_params(url, render=False, country=None, json_response=False, extract_rules=None):
    params = {"url": url}
    if render:
        params["render_js"] = "1"
    if country:
        params["country"] = country.lower()
    if json_response:
        params["json_response"] = "1"
    if extract_rules:
        # The API expects a JSON-stringified object; requests URL-encodes it.
        params["extract_rules"] = json.dumps(extract_rules, separators=(",", ":"))
    return params


def scrape(url, api_key, max_attempts=4, **opts):
    params = {"api_key": api_key, **build_params(url, **opts)}
    for attempt in range(1, max_attempts + 1):
        resp = requests.get(ENDPOINT, params=params, timeout=130)
        usage = {k: v for k, v in resp.headers.items() if k.lower().startswith("wsa-")}
        if resp.ok:
            return resp, usage
        if resp.status_code in FINAL:
            raise SystemExit(f"{resp.status_code}: {resp.text[:300]}")
        if resp.status_code == 422 and not opts.get("render"):
            # Scrape accepted but not completed (e.g. a challenge page). One rendered retry.
            opts["render"] = True
            params = {"api_key": api_key, **build_params(url, **opts)}
            continue
        if resp.status_code in RETRYABLE or resp.status_code == 422:
            time.sleep(min(2 ** attempt, 30))
            continue
        raise SystemExit(f"Unexpected {resp.status_code}: {resp.text[:300]}")
    raise SystemExit(f"Gave up after {max_attempts} attempts")


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("url")
    ap.add_argument("--render", action="store_true")
    ap.add_argument("--country")
    ap.add_argument("--json", dest="json_response", action="store_true")
    ap.add_argument("--dry-run", action="store_true")
    a = ap.parse_args()
    opts = dict(render=a.render, country=a.country, json_response=a.json_response)

    if a.dry_run:
        prepared = requests.Request("GET", ENDPOINT,
                                    params={"api_key": "REDACTED", **build_params(a.url, **opts)}).prepare()
        print(prepared.url)
        return

    api_key = os.environ.get("WSA_API_KEY")
    if not api_key:
        sys.exit("Set WSA_API_KEY first (dashboard -> Web Scraping API tab).")

    resp, usage = scrape(a.url, api_key, **opts)
    print("status:", resp.status_code)
    print("usage headers:", usage)
    if a.json_response:
        env = resp.json()
        print("initial-status-code:", env.get("initial-status-code"), "cost:", env.get("cost"), "type:", env.get("type"))
        print(env.get("body", "")[:300])
    else:
        print(resp.text[:300])


if __name__ == "__main__":
    main()

El --dry-run opción muestra la solicitud exacta sin enviarla, con la clave ocultada. Aquí la tienes con un destino complicado que contiene un espacio, un ?, y un &:

python wsa_scrape.py 'https://example.com/a b?x=1&y=2' --render --country US --json --dry-run
https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2Fa+b%3Fx%3D1%26y%3D2&render_js=1&country=us&json_response=1

Node.js (Node 18 o posterior, integrado fetch y URLSearchParams)

// Build a Scraper API request URL in Node 18+ without sending it.
// URLSearchParams handles encoding of the target URL and the extract_rules JSON.
const ENDPOINT = "https://api.webscrapingapi.com/v2";

export function buildScrapeUrl(apiKey, target, opts = {}) {
  const params = new URLSearchParams({ api_key: apiKey, url: target });
  if (opts.renderJs) params.set("render_js", "1");
  if (opts.country) params.set("country", opts.country);
  if (opts.extractRules) params.set("extract_rules", JSON.stringify(opts.extractRules));
  return `${ENDPOINT}?${params}`;
}

const target = "https://example.com/search?q=web scraping&page=2";
const url = buildScrapeUrl("REDACTED", target, {
  renderJs: true,
  country: "us",
  extractRules: { title: { selector: "h1", output: "text" } },
});
console.log(url);

// Round-trip check: the API sees exactly the original target and rules.
const parsed = new URL(url).searchParams;
console.log("url param decodes to original:", parsed.get("url") === target);
console.log("extract_rules parses:", JSON.parse(parsed.get("extract_rules")).title.selector);

// Live call (server-side only): uncomment with WSA_API_KEY set.
// const res = await fetch(buildScrapeUrl(process.env.WSA_API_KEY, "https://example.com"));
// console.log(res.status, res.headers.get("wsa-call-credit-usage"), (await res.text()).slice(0, 200));

Salida:

https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2Fsearch%3Fq%3Dweb+scraping%26page%3D2&render_js=1&country=us&extract_rules=%7B%22title%22%3A%7B%22selector%22%3A%22h1%22%2C%22output%22%3A%22text%22%7D%7D
url param decodes to original: true
extract_rules parses: h1

Por qué es importante la codificación. La URL de destino se transmite dentro de la cadena de consulta de otra URL. Si la pegas https://example.com/search?q=web scraping&page=2 tal cual, el & termina el url parámetro antes de tiempo, y page=2 llega a la API como un parámetro independiente. El rastreo sigue «funcionando», pero en la página equivocada. La codificación en porcentaje, definida en el RFC 3986, convierte ? en %3F y & en %26 , de modo que todo el destino se conserva. Deja que una biblioteca se encargue de ello: URLSearchParams en Node, params= en Requests o --data-urlencode en curl. La comprobación de ida y vuelta anterior demuestra que la API recibe exactamente la cadena con la que empezaste.

¿Prefieres una biblioteca de cliente? WebScrapingAPI mantiene SDK publicados para Node.js, Python, Java, PHP y Rust, además de una integración con Scrapy. Para el análisis y el rastreo más allá de la llamada a la API, consulta estos patrones de scraping en Python más allá de la llamada a la API.

Documentación: Enviar una solicitud.

¿Qué parámetros de la API de Scraper necesitas realmente?

Solo son obligatorios «api_key» y «url». Añade «render_js» para páginas renderizadas por el cliente, «country», «city» y «state» o «asn» para la ubicación, «extract_rules» o «json_response» para una salida estructurada, «async» para páginas lentas y «method» con «body» para POST, PUT o PATCH. El valor predeterminado de timeout es de 120 000 milisegundos, que es también el límite máximo.

Tabla 1: Parámetros de la API de Scraper

Parámetro

Tipo

Qué hace

Úsalo cuando

api_key (obligatorio)

cadena

Autentica la solicitud

Siempre; léela desde una variable de entorno

url (obligatorio)

cadena

Página de destino; codifícala en formato URL

Siempre (excepto cuando se utiliza snapshot_id)

render_js

booleano

Muestra la página en un navegador

Faltan datos en el HTML sin formato

country

cadena

País proxy de dos letras

El contenido varía según el país

city / state

cadena

Ubicación más concreta; state para us/au ciudades

Contenido a nivel de ciudad; requiere country

asn

cadena

Red (número de sistema autónomo)

Contenido específico de la red

extract_rules

cadena JSON

Devuelve los campos seleccionados en formato JSON

Necesitas campos, no páginas

json_response

booleano

Envuelve la salida con encabezados, estado, coste y metadatos

Pipelines que almacenan metadatos

json_dom

booleano

Devuelve un árbol JSON similar al DOM

Rara vez; es preferible utilizar reglas de extracción

method / body

cadena

GET, POST, PUT, PATCH y carga útil (beta)

El destino espera un envío de formulario o JSON

timeout

entero (ms)

Se interrumpe tras N ms; valor por defecto y máximo 120 000

Es mejor que falle rápido

async

booleano

Pone en cola el rastreo y devuelve un response_id

Objetivos lentos, lotes grandes

snapshot_id

cadena

Recupera un resultado en cola (en lugar de url)

Segundo paso de un trabajo asíncrono

Los parámetros booleanos aceptan 1 o true para activar una función y 0 o false para desactivarla, por lo que render_js=1 y render_js=true significan lo mismo. snapshot_id sustituye url en lugar de unirlo: solo se envía al recoger el resultado de una tarea asíncrona.

POST, PUT y PATCH se dirigen al mismo /v2 punto final con una body carga útil, y la documentación los indica como beta. Pruébalos con un destino «echo» inofensivo, como https://httpbin.org/post antes de dirigirlos a un formulario real. Si prefieres que se detecte rápidamente un error antes que esperar hasta dos minutos por un destino lento, configura timeout un número menor de milisegundos.

Empieza con cada nuevo objetivo utilizando los dos parámetros obligatorios, fíjate en lo que devuelve y añade opciones de una en una. Cada opción debe ganarse su lugar corrigiendo algo que hayas detectado en la respuesta sin formato.

Documentación: Parámetros de la API.

¿Cuándo debes activar la representación de JavaScript?

Activa render_js=1 solo cuando los datos que necesitas no aparezcan en el HTML sin formato porque la página los genera con JavaScript. Realiza primero una solicitud sin renderizar y comprueba si están los campos de tu objetivo. El renderizado no cuesta créditos adicionales en la API de Scraper, pero es más lento que una solicitud HTTP sin formato, así que utilízalo por objetivo.

La prueba requiere una sola solicitud. Elige un valor que puedas ver en un navegador normal, como un precio, recupera la página sin renderizar y búscalo en el cuerpo de la respuesta. Si está ahí, omite el renderizado. Si falta, la página lo genera con JavaScript tras la carga, así que vuelve a intentarlo con render_js=1.

Decide por objetivo, no por proyecto, y guarda la configuración junto a cada objetivo en tu configuración. Las páginas de listados de un sitio pueden enviar HTML completo, mientras que sus páginas de productos cargan los precios desde un script.

La renderización también interactúa con los errores. La documentación menciona una página de error devuelta tras render_js se haya desactivado como una de las causas de una respuesta 422. Por eso, la política de reintentos de esta guía realiza exactamente un reintento con renderización tras un 422 antes de darse por vencida. También puedes combinar la renderización con json_dom=1 si quieres que la página renderizada se presente como un árbol JSON.

Sin embargo, la representación tiene un límite. Si los datos solo aparecen después de esperar a que aparezca un elemento específico, hacer clic en un botón, desplazarse, escribir en un campo o hacer una captura de pantalla, has ido más allá de lo que hace una sola solicitud de la API de Scraper. Esa es la función de la API del navegador.

¿Cómo se extrae contenido específico de una ubicación mediante la geolocalización?

Añade un código de país de dos letras, como «us», «de» o «gb». Limita aún más la búsqueda con la ciudad (en minúsculas y sin espacios), el estado para ciudades de EE. UU. o Australia, o «asn» para seleccionar una red. La geolocalización no supone ningún coste adicional en créditos. La disponibilidad de los objetivos de ciudad y ASN puede variar según el plan y la zona geográfica, así que recurre al nivel de país cuando sea necesario.

Los precios, el stock, el idioma y los resultados de búsqueda suelen variar según la ubicación del visitante. El country parámetro redirige tu solicitud a través de un proxy en ese país, y city, state, y asn la limita aún más. city siempre necesita country, y state sirve para desambiguar ciudades en us o au.

Para confirmar la ruta antes de darla por buena, consulta un servicio de eco de IP. Esta solicitud pide una salida en Berlín y devuelve la dirección IP que vio el destino:

curl --get "https://api.webscrapingapi.com/v2" \
  --data-urlencode "api_key=$WSA_API_KEY" \
  --data-urlencode "url=https://httpbin.org/ip" \
  --data-urlencode "country=de" \
  --data-urlencode "city=berlin"

La segmentación a nivel de país está disponible para todas las zonas geográficas en todos los planes. La segmentación más precisa es menos segura: la disponibilidad exacta puede depender del plan actual, la zona geográfica y la ruta. Por lo tanto, considera la ciudad y el ASN como sugerencias, no como garantías. Si una de ellas no está disponible, vuelve a intentarlo a nivel de país y confirma las ubicaciones que necesita tu carga de trabajo durante el periodo de prueba.

Google es el único lugar en el que no deberías recurrir a country. La documentación indica que no se debe combinar el parámetro propio de Google gl con los de la API de Scraper country en la misma solicitud. Para los resultados de búsqueda, utiliza la API SERP, que incluye la ubicación y el idioma como parte de la propia búsqueda.

¿Cómo se pueden obtener solo los campos que necesitas con las reglas de extracción?

Pasa extract_rules como un objeto convertido a cadena JSON que asigne nombres de salida a selectores CSS o XPath. Cada regla puede devolver texto, HTML, un atributo, una tabla o campos anidados, con todos los resultados de cada coincidencia y limpios para eliminar los espacios en blanco. A continuación, la API devuelve JSON con una propiedad por regla en lugar de la página completa.

La forma más breve asigna un nombre a un selector CSS y devuelve el texto de la primera coincidencia:

{"title": "h1"}

La forma de objeto de regla añade opciones. Úsala cuando necesites un atributo, HTML, todas las coincidencias o el análisis de tablas:

{
  "title": {"selector": "h1", "output": "text"},
  "links": {"selector": "a", "output": "@href", "all": 1}
}

Las opciones son:

  • selector: un selector CSS o una expresión XPath. Los selectores que comienzan por / se tratan como XPath a menos que se establezca selector_type a css o xpath.
  • output: text (el valor por defecto), html, @attr para un atributo como @href, table_json, table_array, o un objeto de regla anidado.
  • all: 1 devuelve todas las coincidencias, 0 solo la primera.
  • clean: 1 elimina los espacios en blanco iniciales y finales, así como los saltos de línea, del texto.

Las reglas anidadas gestionan tarjetas, filas o bloques de resultados de búsqueda repetidos. Seleccionas el elemento exterior con all: 1, y a continuación se definen los campos que se van a extraer del interior de cada coincidencia. La respuesta envuelve cada valor en una lista, por lo que se obtendrá "title": ["..."] en lugar de una cadena sin formato.

Previsualiza tus selectores en una copia guardada de la página antes de gastar créditos. Guarda el HTML del objetivo una vez y, a continuación, prueba tus reglas localmente hasta que devuelvan el resultado esperado. El script que aparece a continuación es una aproximación local para comprobar selectores CSS con Beautiful Soup, no con el propio motor de la API, pero admite las opciones de reglas que se muestran aquí. Instálalo con pip install beautifulsoup4.

El fixture (fixture.html):

<html><body>
  <h1>  Example Store  </h1>
  <div class="result"><h2>Blue Mug</h2><a href="/p/blue-mug">view</a><span class="price">$12.00</span></div>
  <div class="result"><h2>Red Mug</h2><a href="/p/red-mug">view</a><span class="price">$14.50</span></div>
  <table id="specs"><tr><th>Size</th><th>Material</th></tr><tr><td>350 ml</td><td>Ceramic</td></tr></table>
</body></html>

El script de vista previa (preview_rules.py):

"""Preview extract_rules selectors locally on saved HTML before spending credits.

This is a local approximation for checking CSS selectors, not the API's own engine.
It supports the rule options used here: selector, output (text, html, @attr, nested), all, clean.
"""
import json
import sys
from bs4 import BeautifulSoup

RULES = {
    "title": {"selector": "h1", "output": "text", "clean": 1},
    "products": {
        "selector": ".result",
        "all": 1,
        "output": {
            "name": {"selector": "h2", "output": "text"},
            "url": {"selector": "a", "output": "@href"},
            "price": {"selector": ".price", "output": "text"},
        },
    },
}


def apply_rule(node, rule):
    if isinstance(rule, str):
        rule = {"selector": rule}
    matches = node.select(rule["selector"])
    if not rule.get("all"):
        matches = matches[:1]
    out = rule.get("output", "text")
    values = []
    for m in matches:
        if isinstance(out, dict):
            values.append({k: first(apply_rule(m, r)) for k, r in out.items()})
        elif out == "html":
            values.append(str(m))
        elif out.startswith("@"):
            values.append(m.get(out[1:]))
        else:
            text = m.get_text()
            values.append(text.strip() if rule.get("clean") else text)
    return values


def first(v):
    return v[0] if v else None


html = open(sys.argv[1] if len(sys.argv) > 1 else "fixture.html").read()
soup = BeautifulSoup(html, "html.parser")
print(json.dumps({k: apply_rule(soup, r) for k, r in RULES.items()}, indent=2))
print("extract_rules param:", json.dumps(RULES, separators=(",", ":"))[:80], "...")

Resultado:

{
  "title": [
    "Example Store"
  ],
  "products": [
    {
      "name": "Blue Mug",
      "url": "/p/blue-mug",
      "price": "$12.00"
    },
    {
      "name": "Red Mug",
      "url": "/p/red-mug",
      "price": "$14.50"
    }
  ]
}
extract_rules param: {"title":{"selector":"h1","output":"text","clean":1},"products":{"selector":".re ...

Una vez que la vista previa se vea bien, pasa el mismo RULES diccionario a build_params(..., extract_rules=RULES) en el cliente de Python, que se encarga de serializarlo y codificarlo por ti. La mayoría de los errores se producen al crear la cadena de consulta a mano. Si el valor no es un objeto convertido en cadena, la API rechaza la solicitud con un error de validación:

{"status": "Failure", "status_code": 400, "error": "Key `extract_rules` must be a stringified object."}

Las reglas, el esquema y la actualización de los selectores cuando cambia un sitio web son responsabilidad tuya.

Documentación: Reglas de extracción.

¿Qué incluye la respuesta JSON completa y cuándo debes utilizarla?

Añade json_response=1 para envolver el resultado en JSON con los encabezados del destino, el código de estado inicial, el cuerpo (de tipo html, json o b64), el coste en créditos y los microdatos extraídos y los metadatos JSON-LD. Úsalo cuando tu proceso almacene metadatos o necesite el código de estado real del destino. De lo contrario, analiza el cuerpo sin procesar.

Por defecto, la API de Scraper devuelve directamente el cuerpo de la página, que es lo que buscan la mayoría de los analizadores de HTML. La envoltura JSON está pensada para procesos que necesitan más información sobre cada consulta: qué estado devolvió realmente el destino, cuánto costó y qué datos estructurados contenía la página.

La envoltura tiene seis campos: headers (los encabezados de respuesta del destino), cost (créditos de la solicitud), initial-status-code (el primer código de estado del destino), body (el contenido de la página), type (html, json, o b64), y metadata (microdatos extraídos y JSON-LD).

Este analizador comprueba el estado del destino antes de almacenar nada, decodifica los cuerpos binarios y lee el coste. La entrada es un archivo de muestra que refleja el conjunto de campos documentado, no una salida real:

"""Parse a json_response=1 envelope. sample_envelope.json mirrors the documented field set;
it is a SAMPLE, not live output."""
import base64
import json
import sys
from bs4 import BeautifulSoup

env = json.load(open(sys.argv[1] if len(sys.argv) > 1 else "sample_envelope.json"))

status = env["initial-status-code"]
if status >= 400:
    raise SystemExit(f"Target answered {status}; inspect before storing")

body = env["body"]
if env.get("type") == "b64":               # binary targets arrive base64-encoded
    body = base64.b64decode(body)

print("cost (credits):", env["cost"])
print("content-type:", env["headers"].get("content-type"))
print("json-ld blocks:", len(env["metadata"]["json-ld"]))
if env.get("type") == "html":
    print("title:", BeautifulSoup(body, "html.parser").title.get_text())

Salida en la muestra:

cost (credits): 1
content-type: text/html; charset=UTF-8
json-ld blocks: 0
title: Example Domain

También existe json_dom=1, que devuelve la página como un árbol JSON similar al DOM. Si quieres campos específicos, las reglas de extracción suelen ser más sencillas que recorrer todo el árbol.

No necesitas el sobre para realizar el seguimiento del coste. Cada respuesta lleva encabezados personalizados con un WSA- prefijo:

  • WSA-Scraped-Resolved-Url: la URL final tras las redirecciones
  • WSA-Scraped-Headers: los encabezados de respuesta del destino
  • WSA-Client-Cookies: las cookies establecidas por JavaScript tras la carga de la página
  • WSA-Call-Credit-Usage: los créditos que ha consumido esta solicitud
  • WSA-Current-Credit-Usage: total de créditos utilizados tras esta solicitud

Registra los dos encabezados de créditos en cada llamada. El cliente de Python anterior ya recoge todos los WSA- encabezados en su usage diccionario. Tras una semana de ejecuciones, esos registros te indicarán qué objetivos son los más costosos y qué plan necesitas. Para la parte de almacenamiento y validación del mismo trabajo, consulta «Cómo crear tu primer flujo de datos fiable».

¿Cómo se ejecutan de forma asíncrona los rastreos largos o lentos?

Envía la solicitud con async=true. La API pone en cola el rastreo y devuelve inmediatamente un response_id. Más tarde, llama al mismo punto final /v2 con api_key y snapshot_id establecidos en ese ID para recuperar la página completada. Realiza consultas con retrasos crecientes en lugar de mantener las conexiones abiertas, y almacena el ID junto con el trabajo.

Una solicitud sincrónica mantiene abierta la conexión hasta que se recibe la página, durante un máximo de 120 segundos. Esto está bien para unas pocas URL, pero resulta poco práctico para destinos lentos, lotes grandes o trabajadores que no pueden esperar dos minutos por solicitud. La modalità asíncrona divide la llamada en «enviar» y «recoger»:

"""Queue a long scrape with async=true, then poll by snapshot_id.

Needs WSA_API_KEY. Without it, prints the two request shapes (key redacted)."""
import os
import time
import requests

ENDPOINT = "https://api.webscrapingapi.com/v2"
key = os.environ.get("WSA_API_KEY")
target = "https://example.com/"

if not key:
    for p in ({"api_key": "REDACTED", "url": target, "async": "true"},
              {"api_key": "REDACTED", "snapshot_id": "<response_id>"}):
        print(requests.Request("GET", ENDPOINT, params=p).prepare().url)
    raise SystemExit("dry run: set WSA_API_KEY to run live")

job = requests.get(ENDPOINT, params={"api_key": key, "url": target, "async": "true"}, timeout=30).json()
snapshot_id = job["response_id"]
for delay in (2, 4, 8, 16, 30, 30):
    time.sleep(delay)
    r = requests.get(ENDPOINT, params={"api_key": key, "snapshot_id": snapshot_id}, timeout=60)
    if r.ok and r.text.lstrip().startswith("<"):
        print(r.text[:200])
        break
    print("not ready:", r.status_code, r.text[:120])

Sin una clave, el script muestra las dos estructuras de solicitud que enviaría:

https://api.webscrapingapi.com/v2?api_key=REDACTED&url=https%3A%2F%2Fexample.com%2F&async=true
https://api.webscrapingapi.com/v2?api_key=REDACTED&snapshot_id=%3Cresponse_id%3E
dry run: set WSA_API_KEY to run live

El bucle de sondeo es deliberadamente conservador. Deja pasar un tiempo entre intentos y trata todo lo que no sea una página HTML completada como «aún no lista». En producción, guarda el response_id con el registro del trabajo tan pronto como lo obtengas, para que, si se reinicia un trabajador, no se pierdan los resultados completados. Recoge las instantáneas sin demora, en lugar de dar por sentado que esperarán indefinidamente.

¿Cómo debería gestionar tu código los errores de la API de Scraper y los límites de frecuencia?

Trata los códigos 400, 401, 403 y 406 como correcciones que debes aplicar a tu solicitud o cuenta, no como reintentos. Retrocede e intenta de nuevo con los códigos 429, 500 y 503. Un 422 significa que la API aceptó la solicitud pero no pudo completar el rastreo; por ejemplo, una página de autenticación servida como HTTP 200, así que vuelve a intentarlo una vez con renderización antes de darte por vencido.

Tabla 2: Códigos de estado y cómo debe actuar tu código

Código

Significado

Acción

400

Solicitud o parámetros no válidos, o no se ha podido extraer la URL

Corrige la solicitud; no vuelvas a intentarlo a ciegas

401

Clave ausente o no válida, o no queda saldo disponible

Comprueba la variable de entorno y planifica el uso

403

El plan no incluye la función solicitada

Comprueba las funciones del plan

406

Formato de respuesta no compatible

Corrija las opciones de salida

422

Aceptado pero no completado (por ejemplo, una página de autenticación servida como HTTP 200)

Reintentar una vez con render_js=1, y a continuación registra

429

Demasiadas solicitudes, o demasiadas en paralelo

Retrasa la solicitud; limita la concurrencia según tu plan

500 / 503

Error del servidor o mantenimiento

Espera un poco y vuelve a intentarlo

La scrape() función del cliente de Python implementa esta tabla. Los errores definitivos (400, 401, 403, 406) se detienen inmediatamente con el texto de la respuesta, ya que volver a enviar la misma solicitud fallará de la misma manera. Los errores que admiten reintentos (429, 500, 503) esperan con un retroceso exponencial, con un límite de 30 segundos, hasta un máximo de cuatro intentos. Un 422 en una solicitud sin procesar desencadena un reintento con procesamiento. Un error 422 que persista se trata entonces como cualquier otro error que se pueda reintentar, con un retroceso progresivo hasta que se agoten los intentos.

Un error 401 merece una segunda revisión, ya que abarca dos problemas diferentes. La falta de una clave devuelve esto:

{"error":"Access denied. No key `api_key` provided."}

El mismo código también indica que tu cuenta ha agotado su cuota de crédito, así que comprueba el uso de tu plan antes de rotar las claves.

En el caso del 429, la solución suele estar en la concurrencia, no en la velocidad. Los planes permiten entre 5 y 500 solicitudes simultáneas, así que adapta el tamaño de tu grupo de trabajadores a tu plan en lugar de dejar que crezca hasta que la API te devuelva un error. La referencia del código 429 de MDN explica el estado en sí. Si un destino sigue fallando tras los reintentos, prueba estas soluciones para los bloqueos antes de cambiar de herramientas.

Documentación: Respuestas y errores.

¿Cuánto cuesta una solicitud a la API de Scraper?

Los planes se clasifican según el número de solicitudes mensuales: Starter cuesta 19 $ por 7.000, Basic 49 $ por 20.000, Standard 99 $ por 45.000, el plan «Advanced» cuesta 149 $ por 100 000, el «Premium» 249 $ por 180 000 y el «Pro» 599 $ por 450 000. La versión de prueba incluye 5 000 solicitudes. La representación en JavaScript y la geolocalización no suponen ningún coste adicional en ningún plan.

Tabla 3: Planes de la API de Scraper (precios a fecha de octubre de 2026)

Plan

Precio mensual

Solicitudes

Solicitudes simultáneas

≈ por cada 1.000 solicitudes

Prueba

0

5 000

5

—

Básico

19 $

7 000

20

2,71 $

Básico

49 $

20 000

20

2,45

Estándar

99 $

45 000

50

2,20 $

Avanzado

149 $

100 000

100

1,49 $

Premium

249 $

180 000

200

1,38 $

Pro

599 $

450 000

500

1,33

El precio efectivo por cada 1.000 solicitudes se reduce aproximadamente a la mitad al pasar del plan Starter al Pro, por lo que el volumen es más importante que las funciones a la hora de elegir un plan. La concurrencia también es importante: si tu proceso necesita 100 solicitudes en curso a la vez, eso establece un límite mínimo independientemente del volumen mensual.

Para saber cuánto estás gastando realmente, utiliza el coste por llamada que indica la API: el cost campo de la envoltura JSON o el WSA-Call-Credit-Usage encabezado. Prueba tu combinación objetivo real durante el periodo de prueba, anota esos valores y extrapólalos a un mes. Así sabrás qué plan se adapta mejor, mucho mejor que cualquier estimación.

¿Deberías utilizar la API Scraper, la API Browser, la API SERP, la API Crawl o la API Data?

Utiliza la API Scraper cuando tengas URL y necesites recuperar la página. Utiliza la API Browser cuando la página requiera tiempos de espera, clics, introducción de texto o capturas de pantalla. Utiliza la API SERP para resultados de búsqueda, la API Crawl para un conjunto delimitado de páginas relacionadas y la API Data para registros estructurados de fuentes compatibles.

WebScrapingAPI comercializa cinco API de acceso web. Se solapan menos de lo que sugieren sus nombres, ya que cada una parte de una entrada diferente.

Tabla 4: ¿Qué API de acceso web se adapta mejor a la tarea?

API

Envías

Recibes

Elígelo cuando

Modelo de precios

API de scraper

URL + opciones

Cuerpo de la página, JSON o campos

Conoces las URL

Planes de solicitudes mensuales

API del navegador

URL + esperas y acciones

HTML renderizado, JSON, campos, captura de pantalla

La página requiere interacción o un estado específico

Presupuesto

API de SERP

Consulta + contexto del motor de búsqueda y del mercado

Resultados de búsqueda estructurados

Se empieza por una búsqueda

Planes mensuales (Google desde 28 $)

API de rastreo

Ámbito de la fuente

Una colección de páginas delimitada

Una respuesta abarca las páginas relacionadas

Cita

API de datos

Solicitud de destino para una fuente compatible

Registros estructurados

Una de las 116 fuentes compatibles cumple los requisitos

Planes de Amazon a partir de 59 $; otras fuentes previa cotización

Responde a estas preguntas en orden y detente en la primera respuesta afirmativa:

  1. ¿Partes de una consulta de búsqueda? Utiliza la API SERP. Devuelve resultados estructurados por motor de búsqueda, ubicación e idioma, por lo que no tienes que analizar tú mismo las páginas de resultados.
  2. ¿Tu fuente es una de las admitidas por la API de datos y quieres registros en lugar de páginas? Utiliza la API de datos. Abarca 116 fuentes públicas compatibles, y WebScrapingAPI se encarga de mantener los analizadores.
  3. ¿Necesitas un conjunto definido de páginas relacionadas en lugar de una URL cada vez? Utiliza la API de rastreo para la recopilación limitada de varias páginas.
  4. ¿La página requiere tiempos de espera, clics, introducción de texto, pasos de navegación o una captura de pantalla? Utiliza la API Browser, que ejecuta un navegador alojado tras una solicitud REST.
  5. De lo contrario, utiliza la API Scraper, render_js=1 solo si el HTML sin formato carece de tus datos.

Las API también se combinan bien. Un flujo de trabajo habitual utiliza la API SERP para descubrir las URL de los resultados, hace que tu aplicación seleccione las relevantes y, a continuación, recupera esas páginas con la API Scraper o la API Browser. Los precios de la API Browser y la API Crawl se establecen mediante presupuesto. La API de Scraper, la API de SERP y la fuente de Amazon de la API de datos cuentan con planes mensuales publicados, a partir de octubre de 2026.

Para ver la comparación completa, incluyendo lo que gestiona cada API y lo que permanece en tu aplicación, compara las cinco API de Web Access.

¿Cómo conecta Web MCP los agentes de IA a la API Scraper?

Web MCP es un servidor alojado de Model Context Protocol disponible en https://mcp.webscrapingapi.com/mcp. Su agente se conecta con un token MCP dedicado y obtiene web_read y web_read_more a través de la API de Scraper o la API de Browser, además de web_search a través de la API de SERP. Utiliza tus planes y asignaciones existentes, y el token no añade cuota alguna.

Web MCP no es un producto independiente que haya que comprar. Es una capa de conexión sobre el acceso a las API que tu cuenta ya tiene, sin necesidad de instalar ningún paquete ni de ejecutar ningún servidor.

Lo que necesitas. Necesitas una cuenta de WebScrapingAPI con acceso a la API SERP, la API Web Scraping o la API Browser. También necesitas un cliente MCP que admita el transporte HTTP Streamable con un encabezado Authorization: Bearer , según el esquema de tokens definido en RFC 6750. Los clientes que solo admiten el inicio de sesión mediante OAuth no pueden conectarse.

Configuración. Como titular de la cuenta, abre MCP en el panel de control, copia la URL del servidor y selecciona «Crear token». El token solo se muestra una vez, así que guárdalo inmediatamente en el almacén de secretos de tu cliente o en un gestor de contraseñas. Cada cuenta puede tener hasta diez tokens activos, y si se pierde un token, debe revocarse y sustituirse. La clave API de su producto no es un token de MCP y no debe incluirse en la configuración de conexión. Los clientes que acepten un mcpServers objeto admiten una configuración como esta:

{
  "mcpServers": {
    "webscrapingapi": {
      "url": "https://mcp.webscrapingapi.com/mcp",
      "headers": { "Authorization": "Bearer ${WSA_MCP_TOKEN}" }
    }
  }
}

Sustituye el marcador de posición por tu token de MCP, que encontrarás en Panel de control → MCP. Algunos clientes lo leen desde una variable de entorno, mientras que otros tienen un campo para el token, y la sintaxis de interpolación varía según el cliente. Establece el tiempo de espera de la herramienta del cliente por encima de los 60 segundos.

Tabla 5: Herramientas web de MCP

Herramienta

¿

Requisitos

Uso

web_search

Resultados orgánicos de Google (1-10, por defecto 5)

API de SERP

Límite de la API de SERP

web_read

Una página pública en formato Markdown

API de Scraper o API de navegador

Créditos de la API de Scraper (por defecto) o ancho de banda de la API de navegador

web_read_more

Siguiente fragmento de la misma captura

Igual que web_read

Sin nuevas solicitudes, sin coste adicional

Tu cliente solo detecta las herramientas que admite tu cuenta. Sin acceso a la API de SERP, el agente puede seguir leyendo las URL que le facilites, pero no puede realizar búsquedas.

Enrutamiento y facturación. Las lecturas utilizan la API de Scraper cuando tu cuenta la tiene; de lo contrario, se utiliza la API del navegador, y un usuario avanzado puede elegir una de ellas mediante el backend argumento. Las lecturas de la API de Scraper consumen créditos y son estáticas por defecto. Cuando el agente establece render_js: true, se considera una nueva solicitud facturada. Las lecturas de la API de navegador siempre se visualizan y consumen ancho de banda. Una solicitud fallida o una cuota agotada nunca cambia a otro producto.

Límites. Cada resultado de la herramienta tiene un límite de 16 KiB, por lo que las páginas largas se reciben en fragmentos que se recogen con web_read_more. Las páginas con más de 10 MiB de HTML devuelven un error, y las capturas se conservan hasta una hora. El tiempo de espera de conexión es de 55 segundos. Cada token permite cuatro llamadas simultáneas a la herramienta y cuatro sesiones activas; una sesión finaliza tras 15 minutos de inactividad, una hora o 1.000 solicitudes.

Para obtener una visión más amplia de cómo los agentes utilizan los datos web en tiempo real, consulta estos patrones de acceso web de los agentes.

Documentación: Introducción a Web MCP y Uso y errores.

¿Cuáles son los límites de la API de Scraper y en qué casos no es la herramienta adecuada?

La API de Scraper recupera una página por solicitud, por lo que no hará clics, no escribirá, no se desplazará ni iniciará sesión por ti. Las solicitudes caducan a los 120 segundos, la concurrencia está limitada según el plan y la cobertura por ciudad o ASN varía. No puede hacer que una página sea legal para su recopilación. Elige otra API o herramienta cuando esos límites te resulten un obstáculo.

  • Sin interacción. Una solicitud devuelve una página. Espera un selector; los clics, la introducción de texto, el desplazamiento y las capturas de pantalla son funciones de la API del navegador.
  • No permite realizar búsquedas. Para obtener resultados de motores de búsqueda, utiliza la API SERP en lugar de extraer las páginas de resultados.
  • Una URL cada vez. Si la tarea abarca un conjunto definido de páginas relacionadas, la API de rastreo gestiona ese ámbito por ti.
  • Tiempo de espera de 120 segundos. Los objetivos lentos y los lotes grandes deben procesarse mediante solicitudes asíncronas.
  • Concurrencia según el plan. Desde 5 solicitudes paralelas en la versión de prueba hasta 500 en el plan Pro. Adapta el número de trabajadores en consecuencia.
  • La cobertura geográfica varía. La segmentación a nivel de país es amplia, pero la disponibilidad a nivel de ciudad y ASN depende del plan, la ubicación geográfica y la ruta.
  • Las páginas con CAPTCHA pueden seguir dando error. La API gestiona el acceso por ti, pero algunos destinos devuelven un código de error 422. No garantiza la resolución de CAPTCHAs, así que ten en cuenta que pueden producirse fallos ocasionales en los destinos más difíciles.

También existe una línea de responsabilidad que ninguna API elimina. Tú defines qué fuentes son aptas y para qué se utilizan los datos. Utiliza el servicio únicamente para páginas públicas de las que tengas permiso para recopilar datos, revisa los términos de cada sitio web, sigue las normas sobre datos personales y respeta las políticas de robots.

Para conocer los hábitos que mantienen un scraper en buen estado durante meses, consulta estas prácticas recomendadas sobre el ciclo de vida con código funcional. ¿Estás valorando otras opciones además de WebScrapingAPI? Nuestra guía para comparar APIs de scraping de otros proveedores abarca un ámbito más amplio.

Preguntas frecuentes

¿Cómo se utiliza una API de scraper?

Envía una solicitud HTTP al punto final de la API con tu clave API y la URL que desees; a continuación, lee la página a partir de la respuesta. Con WebScrapingAPI, se trata de una solicitud GET a https://api.webscrapingapi.com/v2 con la clave API y una URL codificada. Añade parámetros de representación, ubicación o extracción solo cuando el objetivo los requiera.

¿Cómo se utiliza una API de scraping web con Python?

Instala Requests, introduce tu clave en una variable de entorno y pasa «api_key» y la URL en el argumento «params» para que Requests las codifique. Comprueba el código de estado, reintenta las respuestas 429 y 5xx con retrasos progresivos, y analiza el cuerpo con tu analizador HTML o solicita a la API datos en formato JSON mediante «extract_rules».

¿Hay una prueba gratuita de la API WebScrapingAPI Scraper?

Sí. La versión de prueba de la API Scraper incluye 5.000 solicitudes y es totalmente gratuita al principio, con cinco solicitudes simultáneas. Se incluyen la renderización en JavaScript y la segmentación por país, por lo que puedes probar los objetivos que te interesan antes de elegir un plan de pago. Los planes de pago empiezan en 19 $ al mes por 7.000 solicitudes.

¿La representación en JavaScript consume créditos adicionales?

No. En la API Scraper, «render_js=1» y la segmentación geográfica no consumen créditos adicionales en ningún plan. Sin embargo, la renderización es más lenta que una simple recuperación de datos, así que actívala solo para aquellas páginas cuyos datos no estén presentes en el HTML inicial. Comprueba el encabezado «WSA-Call-Credit-Usage» para confirmar qué ha consumido cada llamada.

¿Qué es una API de extracción de datos web?

Es un servicio que recupera páginas web por ti y devuelve su contenido o campos seleccionados en un formato utilizable. El proveedor gestiona proxies, reintentos y navegadores, y tú envías una URL y las opciones. La API Scraper de WebScrapingAPI devuelve HTML, un contenedor JSON o campos seleccionados mediante reglas de extracción CSS o XPath.

¿Se pueden extraer datos de cualquier sitio web?

Técnicamente, se pueden recuperar muchas páginas públicas, pero no todas deberían recuperarse. Algunos contenidos están protegidos por inicio de sesión, otras páginas requieren interacción con el navegador y las condiciones de uso del sitio o la legislación sobre datos personales pueden restringir la recopilación. Utiliza la API Scraper para las páginas públicas de las que tienes permiso para recopilar datos, y la API Browser cuando las páginas requieran interacción.

¿Cómo se obtienen datos de un sitio web que no tiene API?

Recupera sus páginas y extrae los campos tú mismo, o deja que una API de scraping se encargue de la recuperación. Con la API de Scraper, envía la URL de la página junto con las reglas de extracción (extract_rules) que asocian los nombres de los campos a selectores CSS, y obtendrás un resultado en formato JSON. Previsualiza primero los selectores en una copia guardada de la página para evitar solicitudes innecesarias.

¿WebScrapingAPI resuelve los CAPTCHA?

La API de scraping gestiona el enrutamiento de proxies, la rotación y las estrategias de acceso, pero no garantiza la resolución de CAPTCHAs. Si un sitio de destino devuelve una página de desafío con un código HTTP 200, la API puede clasificar el scraping como un error 422 en lugar de enviarte contenido erróneo. Vuelve a intentarlo una vez con renderización y, a continuación, revisa el sitio de destino.

¿Qué necesito para conectar un agente de IA con Web MCP?

Necesitas una cuenta de WebScrapingAPI con acceso a la API de Scraper, la API de navegador o la API de SERP, un token MCP específico del panel de control y un cliente MCP que admita Streamable HTTP con un encabezado «bearer». Dirige el cliente a https://mcp.webscrapingapi.com/mcp. Los clientes que solo admiten el inicio de sesión mediante OAuth no pueden conectarse.

¿Web MCP consume mis créditos de Scraper API?

Sí, cuando lee páginas a través de la Scraper API. Cada web_read es una solicitud normal que se contabiliza, y volver a intentarlo con renderización supone otra solicitud. web_read_more continúa la misma captura sin un nuevo cargo. Las búsquedas utilizan tu asignación de la SERP API, y crear un token de MCP no añade cuota.

Empieza a extraer datos con 5.000 solicitudes gratuitas

La API de Scraper gestiona los proxies, la rotación, los reintentos y la representación de JavaScript en una sola solicitud. La representación y la geolocalización no consumen créditos adicionales, y las solicitudes fallidas no se facturan. Elige un plan cuando la prueba te muestre lo que necesitas.

¿Estás configurando un agente de IA? Añade la API SERP para que Web MCP pueda buscar además de leer. Su periodo de prueba incluye 100 búsquedas en Google.

Acerca del autor

Robert Munceanu, Desarrollador full-stack @ WebScrapingAPI

Robert Munceanu

Desarrollador full-stack

Robert Munceanu es desarrollador full stack en WebScrapingAPI, donde colabora en todas las áreas del producto y ayuda a crear herramientas y funciones fiables que respaldan la plataforma.

Extracción de datos web con AWS Lambda: guía para Python y Java 2026
Guías

Extracción de datos web con AWS Lambda: guía para Python y Java 2026

TL;DR: En resumen: el web scraping con AWS Lambda funciona mejor cuando cada invocación es breve, está delimitada y se puede reintentar de forma independiente. Empieza con HTTP directo, AWS SAM y S3, y añade SQS, contenedores, renderización en navegador, proxies o una capa de recuperación gestionada solo cuando la carga de trabajo demuestre que los necesita.

Suciu Dan33 min read
Leer artículo
Cómo utilizar GoSpider: rastrear, depurar URL y extraer datos
Guías

Cómo utilizar GoSpider: rastrear, depurar URL y extraer datos

TL;DR: En resumen: GoSpider es un rastreador de línea de comandos diseñado para descubrir URL, no un extractor completo de datos estructurados. Esta guía sobre cómo utilizar GoSpider muestra cómo realizar un rastreo limitado, gestionar correctamente los resultados, pasar los datos de Colly a CSV y seguir una ruta de diagnóstico para respuestas 403 o páginas que requieren renderización con JavaScript.

Suciu Dan24 min read
Leer artículo
Cómo raspar Redfin: Guía Python de Datos Inmobiliarios
Guías

Cómo raspar Redfin: Guía Python de Datos Inmobiliarios

TL;DR: Redfin expone puntos finales de API ocultos que devuelven JSON estructurado para los listados de propiedades, lo que permite omitir por completo el frágil análisis HTML. Esta guía te guía a través de la construcción de un raspador de Python que extrae datos de alquiler y venta, busca por ubicación, supervisa los nuevos listados a través de mapas de sitio XML y exporta resultados limpios a CSV o JSON.

Suciu Dan14 min read
Leer artículo

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.