Volver al blog

Texto Web Limpio para RAG: Un Pipeline de Recuperación, Extracción y Fragmentación

Isabella Garcia
Isabella Garcia

Web Data Collection Specialist

10-Jun-2026

Puntos Clave:

  • La calidad de RAG es la calidad del corpus. Las respuestas de recuperación son tan buenas como el texto que indexaste, y la mayoría de las fallas en la tubería se remontan a páginas que nunca se representaron, elementos de navegación que se incrustaron o trozos que se dividieron a mitad de pensamiento.
  • La recuperación es la etapa poco confiable. Las páginas modernas se renderizan con JavaScript y son verificadas por bots; un simple GET HTTP devuelve una carcasa vacía o una página de desafío, y esos datos basura fluyen silenciosamente a tu tienda vectorial.
  • Un POST devuelve la página renderizada. El desbloqueador web de Scrapeless toma una URL y devuelve el HTML completamente renderizado como {"code": 200, "data": "<html…>"}; el manejo de renderizado y anti-bot se ejecuta del lado del servidor.
  • La extracción es sustracción. Elimina scripts, estilos, navegación y pies de página antes de leer el texto; lo que queda es la prosa digna de ser incrustada.
  • Trozos con superposición, mantén la procedencia. Ventanas de palabras fijas con superposición preservan el contexto a través de los límites, y cada trozo debe llevar su URL de origen; la recuperación sin procedencia no se puede auditar.
  • Gratis para empezar. Las nuevas cuentas de Scrapeless incluyen créditos de prueba gratuitos; regístrate en app.scrapeless.com.

Resumen de la tubería

Un sistema RAG recupera trozos de texto y los alimenta a un modelo; todo lo que sigue hereda lo que se indexó. Esta guía construye el lado de ingesta de extremo a extremo:

  1. Recuperar — obtener HTML completamente renderizado para una lista de URL a través del desbloqueador web, de modo que las páginas construidas con JavaScript y los sitios verificados por bots devuelvan contenido real.
  2. Extraer — eliminar los elementos visuales y conservar la prosa.
  3. Trocear — dividir en ventanas de palabras superpuestas con procedencia, listas para cualquier modelo de incrustación y tienda vectorial.

La salida es corpus.jsonl: un trozo por línea con su URL de origen y posición — el formato neutral que acepta cualquier flujo de trabajo de incrustación. Las etapas 2 y 3 son transformaciones puras; solo la etapa 1 toca la red.


Por qué la etapa de recuperación falla primero

Tres modos de falla dominan la ingesta de texto web, y los tres son invisibles hasta que la calidad de recuperación disminuye:

  • Renderizado del lado del cliente. El HTML que un GET simple devuelve es una carcasa de carga; el artículo llega más tarde a través de JavaScript. Tu extractor lee un <div id="root"> vacío e indexa nada.
  • Intersticiales anti-bot. Las páginas de desafío devuelven HTTP 200 con prosa de "verificando tu navegador", lo que se incrusta maravillosamente y se recupera con confianza.
  • Soft-404s. URL muertas que muestran una página estilizada de "no encontrado", de nuevo con un estado 200.

La solución es recuperar a través de infraestructura que renderice y limpie esas capas del lado del servidor. El desbloqueador web Universal Scraping API hace exactamente eso: un POST por URL, HTML renderizado de vuelta.


Requisitos previos

  • Una cuenta de Scrapeless y clave API — regístrate en app.scrapeless.com.
  • Python 3.10+ con requests y beautifulsoup4.
  • Una lista de URL que tienes derecho a ingerir (ver la nota sobre la obtención a continuación).
bash Copy
export SCRAPELESS_API_KEY=tu_token_api_aqui

Etapa 1 — Recuperar páginas renderizadas

Un POST por URL a la endpoint del desbloqueador. La respuesta es JSON con el documento renderizado en data:

python Copy
# fetch.py — Lista de URL -> páginas/*.html (completamente renderizadas)
import os
import pathlib

import requests

ENDPOINT = "https://api.scrapeless.com/api/v1/unlocker/request"
HEADERS = {
    "Content-Type": "application/json",
    "x-api-token": os.environ["SCRAPELESS_API_KEY"],
}

URLS = [
    "https://www.scrapeless.com/es/blog/best-llm-scrapers-2026",
    "https://www.scrapeless.com/es/blog/google-ai-overview-scraper-api-2026",
]

pathlib.Path("pages").mkdir(exist_ok=True)
for url in URLS:
    resp = requests.post(
        ENDPOINT,
        headers=HEADERS,
        json={
            "actor": "unlocker.webunlocker",
            "input": {"url": url, "type": "html", "redirect": True, "method": "GET"},
        },
        timeout=120,
    )
    resp.raise_for_status()
    html = resp.json()["data"]
    name = url.rstrip("/").rsplit("/", 1)[-1] + ".html"
    pathlib.Path("pages", name).write_text(html, encoding="utf-8")
    print(f"{url} -> pages/{name} ({len(html):,} bytes)")

Una recuperación saludable genera cientos de kilobytes de documento renderizado por página de artículo. Unos pocos kilobytes generalmente significan una carcasa o un intersticial, lo que vale la pena verificar antes de que llegue al índice.

Obtén tu clave API en el plan gratuito: app.scrapeless.com


Etapas 2 y 3 — Extraer la prosa, trocear con procedencia

La extracción es sustracción: elimina los elementos que nunca son prosa, luego lee el texto de lo que queda. El agrupamiento es una ventana de palabras fija con superposición, y cada fragmento conserva su URL de origen y posición:

python Copy
# build_corpus.py — pages/*.html -> corpus.jsonl (fragmentos con procedencia)
import json
import pathlib

from bs4 import BeautifulSoup

CHUNK_WORDS = 220      # tamaño de la ventana
OVERLAP_WORDS = 40     # llevados al siguiente fragmento

STRIP_TAGS = ["script", "style", "noscript", "nav", "header", "footer", "aside", "form", "svg"]


def extract_text(html: str) -> str:
    soup = BeautifulSoup(html, "html.parser")
    for tag in soup(STRIP_TAGS):
        tag.decompose()
    root = soup.find("article") or soup.find("main") or soup.body or soup
    text = root.get_text(" ", strip=True)
    return " ".join(text.split())


def chunk(words: list[str]):
    step = CHUNK_WORDS - OVERLAP_WORDS
    for start in range(0, max(len(words) - OVERLAP_WORDS, 1), step):
        yield start, " ".join(words[start:start + CHUNK_WORDS])


total = 0
with open("corpus.jsonl", "w", encoding="utf-8") as out:
    for page in sorted(pathlib.Path("pages").glob("*.html")):
        text = extract_text(page.read_text(encoding="utf-8"))
        words = text.split()
        for start, body in chunk(words):
            out.write(json.dumps({
                "source": page.stem,
                "word_offset": start,
                "n_words": len(body.split()),
                "text": body,
            }) + "\n")
            total += 1
        print(f"{page.name}: {len(words):,} palabras")

print(f"{total} fragmentos -> corpus.jsonl")

Lo que sale, una línea por fragmento:

json Copy
// muestra ilustrativa — esquema de una ejecución en vivo de build_corpus.py; texto abreviado
{
  "source": "best-llm-scrapers-2026",
  "word_offset": 180,
  "n_words": 220,
  "text": "…el actor devuelve la respuesta más sus citas como campos estructurados…"
}

Desde aquí, cualquier flujo de trabajo de incrustación toma el relevo: lee corpus.jsonl, incrusta text, almacena el vector con source y word_offset como metadatos. Los campos de procedencia son lo que te permite rastrear una mala recuperación de vuelta a la página y a la posición de donde vino.


Abastecimiento responsable

Un corpus de entrenamiento o recuperación hereda peso legal de sus fuentes. Ingiere solo páginas de acceso público que tengas derecho a usar; verifica los términos de servicio y las directrices de robots de cada sitio antes de agregarlo a la lista de URL; mantén el volumen de solicitudes modesto por host; y trata el texto con derechos de autor como algo que recuperas y atribuyes en lugar de republicar. Cuando un corpus incluye contenido de terceros, mantener intacto el campo source es la diferencia entre una cita y una copia.


FAQ

P: ¿Por qué no usar simplemente requests.get en las URL?

Para páginas estáticas funciona. Para sitios renderizados por JavaScript o verificados por bots, devuelve contenedores y pantallas intermedias que envenenan el índice silenciosamente; el desbloqueador existe justamente para esos casos.

P: ¿Qué tamaño deberían tener los fragmentos?

La ventana 220/40 aquí es un valor predeterminado razonable para modelos de incrustación de clase transformadora de oraciones. Ajusta según el contexto de tu modelo y tu granularidad de recuperación; mantén algo de superposición para que las ideas que cruzan un límite sobrevivan.

P: ¿Cómo puedo detectar una mala recuperación antes de que llegue al índice?

Verificaciones de tamaño y contenido: las páginas de artículos renderizadas son grandes (las páginas del ejemplo trabajado llegan a cientos de kilobytes), y el texto extraído de unas pocas cientos de palabras de una página que debería ser un artículo es una bandera roja que vale la pena registrar.

P: ¿Puede esto recuperar páginas detrás de potentes proveedores anti-bots?

La tarea del desbloqueador es eliminar las capas anti-bots del lado del servidor. Donde una página específica aún no pueda ser desbloqueada, trátala como no recuperable y déjala por fuera; una página faltante es recuperable, un índice envenenado no lo es.

P: ¿Necesito un proxy?

No. La salida y renderización se manejan dentro del actor; el POST que envías es toda la integración.

P: ¿Dónde entran las incrustaciones y el almacenamiento de vectores?

Después de corpus.jsonl, con cualquier pila que ya uses. Este pipeline se detiene deliberadamente en fragmentos limpios y etiquetados con procedencia; el formato que toda herramienta de incrustación acepta.


Conclusión: limpio dentro, limpio fuera

El pipeline de ingestión se reduce a tres archivos cortos: recuperar HTML renderizado a través del desbloqueador, restar el chrome, agrupar con superposición y procedencia. Nada de esto es glamoroso, y todo esto decide si las respuestas de recuperación provienen de texto real de artículos o de un contenedor de carga que se coló en el índice. Dirige la lista de URL a las fuentes que tu asistente debería conocer, programa la ejecución y conserva las páginas crudas; son el rastro de auditoría.

¿Listo para construir tu pipeline de ingestión RAG?

Únete a nuestra comunidad para reclamar un plan gratuito y conectarte con desarrolladores que están construyendo pipelines de datos: Discord · Telegram.
Regístrate en app.scrapeless.com para obtener créditos de prueba gratuitos y apunta la etapa de recuperación a las páginas que necesita tu corpus de recuperación. Consulta precios para conocer los niveles actuales.

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