Volver al blog

Raspado Web Llama: Por qué el renderizado decide los datos

Olivia Patel
Olivia Patel

Senior Cybersecurity Analyst

29-Jul-2026

Resumen:

  • La brecha entre "no se puede extraer" y "extrae correctamente" es una llamada de renderizado, y esta guía la mide exactamente. Una simple solicitud GET en un catálogo de productos solo de JavaScript devuelve un marcador de posición de plantilla no renderizado; la misma URL a través de la API de Scraping Universal Scrapeless con js_render devuelve 13 spans de productos reales, y Llama extrajo correctamente los 12 productos genuinos de ellos.
  • Llama 4 es la categoría económica actual, no Llama 3. meta-llama/llama-4-scout es el Llama de generación actual más barato en el catálogo en vivo de OpenRouter, una familia diferente y más nueva que el nombre Llama 3.1 que la mayoría de los tutoriales existentes aún utilizan.
  • El modelo filtró silenciosamente una fila de plantilla rota por sí solo. La página renderizada contiene en realidad 13 spans de product-name; uno es un marcador de posición no hidratado ${product.name}, y la extracción de Llama devolvió exactamente los 12 productos reales, omitiendo el falso sin que se le indicara.
  • Este es el camino en la nube, no el local. Una guía separada en este sitio, Web Scraping with LLaMA 3, cubre la ejecución de Llama localmente a través de Ollama; esta ejecuta Llama 4 a través de una API hospedada en su lugar.
  • ¿Sin clave de API nativa de Llama aún? La solicitud idéntica pasa por OpenRouter. Esta guía la ejecutó en vivo en meta-llama/llama-4-scout y muestra la salida capturada.
  • Gratis para comenzar en el lado de la obtención. Crea tu clave de API de Scrapeless en app.scrapeless.com.

¿Puede Llama raspar sitios web?

Un punto final de Llama alojado lee texto y devuelve campos; no recupera páginas, ejecuta su JavaScript, ni mantiene una sesión. Eso es cierto si el modelo se ejecuta localmente a través de Ollama o a través de una API en la nube: los pesos del modelo no cambian nada sobre cómo funciona HTTP. Lo que difiere entre los caminos local y en la nube es solo dónde ocurre la inferencia.

Web Scraping with LLaMA 3, ya en este sitio, cubre la ruta local: llama3.1:8b a través de Ollama, combinado con Selenium contra páginas de productos. Esta guía cubre la otra mitad: un modelo Llama 4 alojado alcanzado a través de una API, sin descarga local del modelo y sin requerimientos de GPU, en un objetivo de demostración diferente elegido específicamente para probar por qué la capa de obtención es importante. Para la pregunta de categoría más amplia, el explicador de raspadores LLM cubre la herramienta LLM-como-analizador en general.

Instalar

openai abarca el camino de OpenRouter (Llama se sirve detrás de una superficie compatible con OpenAI allí), y requests cubre la capa de obtención:

bash Copy
pip install "llama-api-client==0.6.0" "openai==2.48.0" requests

Configurar

bash Copy
export LLAMA_API_KEY="tu_clave_api_llama"
export SCRAPELESS_API_KEY="sk_tu_clave_scrapeless"

Obtener una página que Llama pueda leer realmente

El objetivo de demostración es una página pública de práctica de renderizado diseñada específicamente para demostrar exactamente esta brecha: su cuadrícula de productos está vacía hasta que el JavaScript del lado del cliente la pobla, y el HTML en bruto contiene en su lugar una cadena de literal de plantilla no ejecutado. Un script muestra la diferencia y guarda la versión que vale la pena extraer:

python Copy
# fetch_rendered.py — GET simple vs renderizado del lado del servidor, misma URL
import os

import requests

URL = "https://www.scrapingcourse.com/javascript-rendering"
MARKER = 'class="product-name"'

plain = requests.get(URL, timeout=60).text
print(f"GET simple: {len(plain):,} caracteres | spans de nombre del producto: {plain.count(MARKER)}")

resp = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
    headers={
        "Content-Type": "application/json",
        "x-api-token": os.environ["SCRAPELESS_API_KEY"],
    },
    json={
        "actor": "unlocker.webunlocker",
        "input": {"url": URL, "method": "GET", "js_render": True},
    },
    timeout=120,
)
resp.raise_for_status()
rendered = resp.json().get("data", "")
print(f"renderizado:  {len(rendered):,} caracteres | spans de nombre del producto: {rendered.count(MARKER)}")

with open("pagina.html", "w", encoding="utf-8") as f:
    f.write(rendered)

La ejecución imprime product-name spans: 1 para la recuperación simple: ese único golpe es una plantilla <span class="product-name">${product.name}</span> sin ejecutar, no datos reales, y product-name spans: 13 para la versión renderizada. La diferencia entre los dos es exactamente el ciclo de vida del documento que la especificación de scripting HTML define: contenido que solo existe después de que se ejecutan scripts contra el DOM. La renderización y el enrutamiento proxy ocurren del lado del servidor en ese POST — la API Universal de Scraping es la capa de recuperación, y page.html es ahora algo de lo que un modelo puede extraer.

Implementación básica: Llama como extractor

La API oficial Llama de Meta toma salida estructurada a través de response_format={"type": "json_schema", "json_schema": {...}}, según el SDK enviado en el repositorio del cliente Python de la API Llama de Meta — no hay modo json_object desnudo en este punto final, solo salida guiada por el esquema. El catálogo actual de OpenRouter lista meta-llama/llama-4-scout como el modelo de la cuarta generación Llama más barato; en la propia API nativa de Meta, el ID de modelo equivalente lleva la forma más completa Llama-4-Scout-17B-16E-Instruct-FP8.

Nota: Este bloque necesita un LLAMA_API_KEY con crédito — el único requisito previo que esta guía no asume. La siguiente sección ejecuta la extracción idéntica en vivo a través de OpenRouter, con salida capturada.

python Copy
# extract_llama.py — extracción nativa de la API Llama (requiere LLAMA_API_KEY)
import json
import os

from llama_api_client import LlamaAPIClient

client = LlamaAPIClient(api_key=os.environ["LLAMA_API_KEY"])

page_html = open("page.html", encoding="utf-8").read()

response = client.chat.completions.create(
    model="Llama-4-Scout-17B-16E-Instruct-FP8",
    max_completion_tokens=2000,
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "Products",
            "schema": {
                "type": "object",
                "properties": {
                    "products": {
                        "type": "array",
                        "items": {
                            "type": "object",
                            "properties": {"name": {"type": "string"}, "price_usd": {"type": "number"}},
                            "required": ["name", "price_usd"],
                        },
                    }
                },
                "required": ["products"],
            },
        },
    },
    messages=[
        {"role": "system", "content": "Extrae cada producto real de esta página. Ignora cualquier marcador de posición de plantilla no renderizado."},
        {"role": "user", "content": page_html},
    ],
)

data = json.loads(response.completion_message.content.text)
print(f"extraídos {len(data['products'])} productos")

Nota la forma de la respuesta: el cliente nativo de Meta devuelve el mensaje bajo response.completion_message.content.text, no la ruta choices[0].message.content que utilizan los clientes compatibles con OpenAI.

¿Sin clave nativa? Ejecútalo a través de OpenRouter

OpenRouter ofrece Llama detrás de la misma superficie de chat-completions compatible con OpenAI que los otros modelos en esta serie. Esta es la versión que esta guía ejecutó de verdad, recuperación y extracción en un único script autónomo:

python Copy
# extract_openrouter.py — la misma extracción, ejecutada a través de OpenRouter
import json
import os

import requests
from openai import OpenAI

URL = "https://www.scrapingcourse.com/javascript-rendering"

resp = requests.post(
    "https://api.scrapeless.com/api/v2/unlocker/request",
    headers={
        "Content-Type": "application/json",
        "x-api-token": os.environ["SCRAPELESS_API_KEY"],
    },
    json={"actor": "unlocker.webunlocker", "input": {"url": URL, "method": "GET", "js_render": True}},
    timeout=120,
)
resp.raise_for_status()
page_html = resp.json().get("data", "")

client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=os.environ["OPENROUTER_API_KEY"])

completion = client.chat.completions.create(
    model="meta-llama/llama-4-scout",
    temperature=0,
    max_tokens=2000,
    response_format={"type": "json_object"},
    messages=[
        {
            "role": "system",
            "content": 'Extrae cada producto. Responde SOLO con JSON: {"products":[{"name":str,"price_usd":number}]}.',
        },
        {"role": "user", "content": page_html},
    ],
)

data = json.loads(completion.choices[0].message.content)
print(f"extraídos {len(data['products'])} productos de la página renderizada")
for row in data["products"]:
    print(json.dumps(row, ensure_ascii=False))

La ejecución en vivo extrajo exactamente los 12 productos reales, ninguno de los marcadores de posición:

text Copy
extraídos 12 productos de la página renderizada

{"nombre": "Sudadera con capucha Chaz Kangeroo", "precio_usd": 52}
{"nombre": "Sudadera con capucha Teton", "precio_usd": 70}
Ambos funcionan con la misma arquitectura de buscar-y-luego-extraer. Localmente, a través de Ollama, evita el costo por token pero necesita un host capaz de GPU y una descarga del modelo; el camino en la nube de esta guía no necesita hardware local pero cobra por token. La publicación complementaria Web Scraping with LLaMA 3 cubre la ruta local en profundidad.

P: ¿Por qué el modelo omitió uno de los productos en la página renderizada?

Porque no era un producto real. El HTML renderizado en la ejecución en vivo de esta guía contenía 13 spans de product-name, pero uno era un marcador de posición de plantilla ${product.name} no hidratado que quedó del marco del lado del cliente de la página; el modelo lo excluyó correctamente de los 12 extraídos.

P: ¿Es legal hacer scraping con Llama?

La capa de extracción no cambia las reglas de colección ni en la ruta local ni en la nube. Solo se deben obtener páginas públicas, respetar los términos del sitio y las directrices de robots estandarizadas por el Protocolo de Exclusión de Robots, mantener los volúmenes limitados y manejar cualquier dato personal bajo la ley aplicable.

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