Raspado Web Llama: Por qué el renderizado decide los datos
Senior Cybersecurity Analyst
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_renderdevuelve 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-scoutes 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-scouty 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
pip install "llama-api-client==0.6.0" "openai==2.48.0" requests
Configurar
bash
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
# 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_KEYcon 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
# 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
# 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
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.



