Saltar al contenido
crawlforgeEnglish

Documentación · renderizada del repositorio

Esta página es el manual del repositorio, renderizado: el mismo fichero que acompaña al código. Una sola versión, para que la web no pueda decir una cosa y el manual otra.

Referencia de reglas →

Si aún no la tienes instalada →

Manual de CrawlForge (CLI)

Versión en inglés: read the manual in English — la inglesa es la que manda. Esta traducción puede ir por detrás; si las dos discrepan, la buena es la otra.

La herramienta es hoy línea de comandos. Una interfaz gráfica está prevista y no existe todavía.

Esto no repite el --help, que ya está completo y en inglés. Esto es qué comando usar en cada situación real, con los sitios de verdad.


0. Antes de empezar

El binario ya está instalado y en el PATH:

crawlforge --version

Si no responde, se reinstala desde el repositorio:

cd ~/Desarrollos/proyectos/proyectos-mini/crawlforge
cargo build --release -p crawlforge && cp target/release/crawlforge ~/.cargo/bin/

La salida de la herramienta está en inglés. Es el idioma de origen; los demás son traducciones, y se piden con --lang en los comandos que leen un rastreo (report, inspect, diff, portfolio, rules):

crawlforge rules  --lang es
crawlforge report crawl.sqlite --lang es
--lang Idioma
en Inglés — el de por defecto y el de origen
es Español
de Alemán
fr Francés
it Italiano
nl Neerlandés
tr Turco
pt Portugués
ja Japonés
pl Polaco

Se acepta una región o una codificación y se ignora (pt-BR, es_ES.UTF-8): hay un solo portugués, un solo español. Un idioma cuya traducción aún no está escrita se acepta y sale en inglés —números incluidos— hasta que lo esté; un valor desconocido es un error que lista estos códigos. La ayuda (--help) y los errores de argumentos van siempre en inglés.

Para no repetirlo en cada comando, en tu ~/.zshrc. La variable fija también el idioma de crawl, audit y list, que no tienen --lang; ahí un valor desconocido se ignora y la salida sigue en inglés:

export CRAWLFORGE_LANG=es

1. El flujo de un día normal

Cuatro comandos. Si solo lees una sección del manual, que sea esta.

# 1. Rastrear
crawlforge crawl https://tusitio.com/

# 2. Ver qué ha salido, aquí mismo
crawlforge report crawl-tusitio-com.sqlite --lang es

# 3. Llevárselo a alguien que no usa terminal
crawlforge export crawl-tusitio-com.sqlite --format xlsx --out auditoria.xlsx

# 4. Un informe para pegar en un ticket o mandar por correo
crawlforge report crawl-tusitio-com.sqlite --format html --out informe.html --lang es

Cada comando termina diciéndote cuál es el siguiente, con la orden lista para copiar. No hace falta memorizar nada.

Los ficheros que aparecen

Fichero Qué es
crawl-<sitio>.sqlite El rastreo. Todo está aquí: URLs, enlaces, imágenes, hallazgos, cabeceras. Es un SQLite normal; se puede abrir con cualquier visor.
crawl-<sitio>.prev.sqlite El rastreo anterior, apartado automáticamente al repetir. Es lo que diff necesita. No lo borres.

Un rastreo se puede volver a mirar meses después sin volver a rastrear: report, export y diff trabajan sobre el fichero, no sobre la red.


2. Recetas

Rastrear un sitio entero

crawlforge crawl https://cliente.com/

Descubre los sitemaps, respeta robots.txt y para cuando agota el sitio. Va con 5 peticiones simultáneas, que es prudente para un WordPress. En un sitio propio y bien dimensionado:

crawlforge crawl https://tusitio.com/ --concurrency 10

No subas la concurrencia en sitios de terceros ni en hosting compartido. Un servidor con limitación por IP empieza a devolver 503 tras varios rastreos seguidos, y a partir de ahí las mediciones no valen nada: lo que estás midiendo es su defensa, no tu sitio.

Solo una parte del sitio

# Solo el blog
crawlforge crawl https://cliente.com/ --include "/blog/"

# Todo menos el área de administración y las URLs de respuesta a comentarios
crawlforge crawl https://cliente.com/ --exclude "/wp-admin/" --exclude "\?replytocom="

Son expresiones regulares sin anclar: una cadena normal funciona como «contiene». --exclude gana sobre --include, y lo excluido queda registrado como excluido en el informe: no desaparece sin dejar rastro.

Una prueba rápida, sin rastrear las 176.000 URLs

crawlforge crawl https://tumedio.com/ --max-urls 500

Ojo con esto: un rastreo truncado no puede juzgar el grafo de enlaces. La herramienta lo sabe y silencia las reglas que dependen de tenerlo completo —páginas huérfanas, profundidad excesiva— en lugar de inventarse el veredicto. Si el informe no las menciona, no es que estén bien; es que no se han podido evaluar.

Auditar antes de desplegar

Sobre la carpeta compilada, sin subir nada a ningún sitio:

crawlforge audit ./dist --base https://tusitio.com/

--base es obligatorio y por un motivo: los canonical absolutos del sitio se comparan contra esa URL. Con una falsa, la auditoría de indexabilidad no significa nada, y la herramienta avisa si el 80% de los canonicals contradicen lo que le has dicho.

Es el modo más rápido de todos —no hay red— y es el que se usa en CI.

Comparar dos rastreos: ¿el despliegue ha empeorado algo?

crawlforge diff crawl-sitio.prev.sqlite crawl-sitio.sqlite --lang es

Esto es lo que Screaming Frog no te da. Un rastreo es una foto; el diff te dice si lo de ayer sigue igual. Como puerta de CI, que falle si aparece algo grave:

crawlforge diff antes.sqlite despues.sqlite --fail-on high

Sale con código distinto de cero si aparece un hallazgo nuevo de severidad high o peor. Un --fail-on INDEX-NOINDEX vigila una regla concreta.

El resumen enseña tres ejemplos por grupo. Para la lista entera, nombra una regla o un tipo de cambio:

crawlforge diff antes.sqlite despues.sqlite --rule HTTP-404-INTERNAL   # todos los nuevos y resueltos
crawlforge diff antes.sqlite despues.sqlite --change url_removed       # todas las URLs que se fueron

Los tipos son url_added, url_removed, status_changed, title_changed, meta_description_changed, canonical_changed, indexability_lost, indexability_gained, issue_appeared, issue_resolved, robots_txt_changed y sitemap_changed.

Para conservar un diff, guárdalo con --out cambios.sqlite. Se vuelve a abrir con el mismo comando y solo ese fichero: el resumen sale como era, en el idioma que pidas, y --rule y --change también funcionan sobre él:

crawlforge diff cambios.sqlite --lang es
crawlforge diff cambios.sqlite --change status_changed

Un diff guardado solo se lee: --fail-on es para el momento de comparar, no para después.

Un sitio protegido con contraseña (staging)

CRAWLFORGE_AUTH='usuario:contraseña' crawlforge crawl https://pre.cliente.es/

También se acepta en la URL como atajo, aunque así queda en el historial del shell:

crawlforge crawl https://usuario:contraseña@pre.cliente.es/

En los dos casos, la credencial no se guarda en el fichero de rastreo y no viaja a ningún host que no sea el de la semilla, ni siquiera si el sitio enlaza a otro dominio. Por eso resume también necesita la variable: el fichero no la lleva dentro a propósito.

Se aplica también al robots.txt y a los sitemaps — si no, un staging protegido devolvería 401 al pedirlos y el rastreo se comportaría de forma rara sin decir por qué.

Una lista exacta de URLs

crawlforge list urls.txt

Un fichero con una URL por línea. Sirve para revisar un conjunto concreto —las 40 landings de una campaña— y es también el modo que hace justa una comparación con otra herramienta: las dos reciben exactamente el mismo conjunto.

Una lista puede mezclar sitios. Se audita cada sitio que aparece en el fichero —host y puerto—, así que en una misma lista caben páginas de todos los blogs de una cartera. Una página de cualquiera de ellos que está enlazada pero no en la lista se registra sin pedirse, igual que si fuera del primero, y una página rota de cualquiera de ellos es un 404 interno.

Un enlace de un sitio de la lista a otro sigue saliendo del sitio de la página: no cuenta entre sus enlaces internos, y un nofollow en él no es INDEX-NOFOLLOW-INTERNAL. Los dos sitios son tuyos, pero para un buscador son dos sitios.

La credencial de CRAWLFORGE_AUTH sigue saliendo de la primera URL: solo se manda a su host. El informe nombra todos los sitios en el título (Auditoría de 2 sitios: …), y el panel de cartera también.

Se ha cortado el rastreo

crawlforge resume crawl-tumedio-com.sqlite

Sigue exactamente donde se quedó, con la configuración guardada en el propio fichero. No repite lo ya rastreado. Un rastreo terminado no se puede reanudar: para eso se relanza.

Con una excepción: un rastreo que el sitio frenó. Si hay páginas que tras los reintentos siguen respondiendo 429 Too Many Requests (un Cloudflare o un cortafuegos pidiendo al rastreador que vaya más despacio), no se dan por rotas. Quedan pendientes, el resumen dice cuántas y las reglas que necesitan el sitio entero no se evalúan. resume sobre ese fichero terminado pide solo esas páginas, cuando el límite se haya levantado. Si se repite, rastrea con menos --concurrency o deja pasar el user-agent de CrawlForge en el cortafuegos del sitio. Cuando el 429 dice cuánto esperar (Retry-After), el rastreador espera eso antes de reintentar y mientras tanto no lanza ninguna petición nueva a ese host; si pide más de dos minutos, la página queda pendiente directamente, sin reintentos.

Rastreos que se repiten: guarda la configuración

cp docs/crawl-config.example.yaml cliente.yaml   # y edítalo
crawlforge crawl https://cliente.com/ --config cliente.yaml

El fichero describe el sitio, la línea de comandos describe la ejecución: los flags ganan sobre el YAML. Un campo mal escrito es un error, no una opción ignorada en silencio.

Que el editor lo revise mientras escribes. Las claves, sus tipos y valores por defecto, y todos los IDs de regla y severidades que acepta rules: se publican como JSON Schema, docs/crawl-config.schema.json. La primera línea del ejemplo apunta a él a un servidor de lenguaje YAML (VS Code con la extensión YAML de Red Hat, Zed, Neovim, JetBrains), así que una copia suya ya completa las claves y subraya las erratas:

# yaml-language-server: $schema=https://raw.githubusercontent.com/dcarrero/crawlforge/main/docs/crawl-config.schema.json

crawlforge config schema imprime el esquema del binario que tienes, para una copia sin conexión o para dárselo a un asistente que escriba el fichero por ti: crawlforge config schema > crawl-config.schema.json y apunta la línea a esa ruta. El esquema escribe los IDs de regla en mayúsculas; CrawlForge también los acepta en minúsculas.

Cada sitio con su criterio: desactivar reglas, cambiar severidades

No todos los clientes tienen el mismo listón. Un blog cuya plantilla escribe meta descriptions largas a propósito no necesita oírlo cada semana, y un enlace externo roto puede ser low en un sitio y high en otro. Se dice en el YAML del sitio:

rules:
  disable: [META-DESC-TOO-LONG, CONTENT-H1-MULTIPLE]
  severity:
    HTTP-404-EXTERNAL: low

El rastreo sigue evaluando todas las reglas y guardando todos los hallazgos con la severidad del catálogo. El bloque se guarda dentro del fichero de rastreo, y report, inspect, export, diff y portfolio lo aplican al leerlo. Cambiar de idea no cuesta nada: se edita el YAML y se vuelve a leer el mismo fichero con --config, sin rastrear otra vez.

crawlforge crawl https://cliente.com/ --config cliente.yaml   # guarda el bloque
crawlforge report cliente.sqlite                             # lo aplica
crawlforge report cliente.sqlite --config otro.yaml          # aplica el de otro.yaml
  • --config en un comando de lectura sustituye al bloque guardado en esa ejecución. Solo se usa rules:; el resto del fichero se sigue comprobando por si hay erratas. Un fichero sin rules: enseña todos los hallazgos, y así se ve lo que la configuración oculta.
  • Toda salida que aplica una configuración lo dice: «Configuración de reglas (guardada en el fichero de rastreo): 2 reglas desactivadas, 1 severidad cambiada». La hoja Summary del XLSX la detalla y el export a CSV la imprime en el terminal. Los exports dejan fuera los hallazgos de las reglas desactivadas y escriben la severidad cambiada.
  • diff evalúa los dos rastreos con la misma configuración: la de --config si se da, y si no la guardada en el rastreo posterior. De lo contrario, desactivar una regla entre dos rastreos haría pasar sus hallazgos por «resueltos». Si los dos ficheros guardan configuraciones distintas, el diff lo dice. --fail-on juzga la severidad cambiada.
  • portfolio lee cada sitio con su propia configuración guardada y nombra los sitios que tienen una: un sitio que desactivó una regla no es un sitio sin ese problema.
  • Un ID de regla o una severidad desconocidos son un error que los nombra. Los IDs se reconocen sin distinguir mayúsculas.

Ver qué comprueba la herramienta

crawlforge rules --lang es                    # el catálogo entero, en tabla
crawlforge rules INDEX-ORPHAN-PAGE            # la ficha de una regla concreta, por su ID
crawlforge rules --lang es --category canonical
crawlforge rules --lang es --detail           # con la explicación completa de cada una
crawlforge rules --format json                # el catálogo entero como datos, en todos los idiomas

El JSON es para lo que consuma el catálogo como datos — un script de CI que comprueba que un ID de regla sigue existiendo, o una página generada del catálogo en vez de copiarlo. Lleva siempre todos los idiomas traducidos (name.en, name.es…; inglés y español siempre, los demás cuando estén traducidos), y su sobre dice la versión del catálogo y el número de reglas.


3. Cómo se lee el resultado

En el terminal

report sin más da el resumen: cuántas URLs, cuántas indexables, y los hallazgos agrupados por severidad. Es para responder «¿cómo está esto?» en diez segundos.

El XLSX

crawlforge export crawl.sqlite --format xlsx --out auditoria.xlsx

Catorce hojas, cada una con la cabecera congelada y el autofiltro puesto. Los códigos de estado son números de verdad, así que un filtro «mayor que 399» funciona como esperas. Una celda de estado vacía es una URL que se registró y nunca se pidió: un enlace interno fuera del alcance del rastreo, o uno externo con la comprobación apagada.

Está comprobado que abre limpio en Microsoft Excel 16, sin aviso de reparación.

El SQLite, si quieres ir más allá

Es la ventaja de que el formato no sea propietario. Cualquier pregunta que el informe no responda, la responde SQL.

Las tres tablas que se usan el 90% de las veces:

Tabla Qué guarda Clave
urls Toda URL vista: url, status_code, depth, content_type, response_time_ms id
pages Lo extraído del HTML: title, h1, word_count, canonical, is_indexable url_id → urls.id
issues Un hallazgo por fila: rule_id, severity url_id → urls.id
# Las URLs que fallan
sqlite3 crawl-cliente-com.sqlite \
  "SELECT url, status_code FROM urls WHERE status_code >= 400 ORDER BY status_code DESC LIMIT 20;"

# Qué páginas dispararon una regla concreta
sqlite3 crawl-cliente-com.sqlite \
  "SELECT u.url FROM issues i JOIN urls u ON u.id = i.url_id
   WHERE i.rule_id = 'CONTENT-H1-MISSING' LIMIT 20;"

# Las páginas más lentas
sqlite3 crawl-cliente-com.sqlite \
  "SELECT url, response_time_ms FROM urls ORDER BY response_time_ms DESC LIMIT 10;"

Hay vistas ya preparadas para las preguntas habituales: v_orphans, v_broken_links, v_indexable_pages y v_issue_summary.

v_broken_links sigue además las redirecciones hasta donde acaban, así que un enlace a tu propio /go/producto que redirige a una tienda que devuelve 404 sale como roto. En esas filas via es la URL que se enlazó de verdad, la que hay que reescribir, y hops cuántos saltos hicieron falta; las dos van a NULL cuando el enlace apunta directamente a lo roto.

Dos avisos que ahorran un rato: la columna es status_code, no status, y está a NULL en toda URL que se registró sin pedirse: un enlace interno al que el rastreo no llegó, o uno externo si va --no-external-check.


3.bis Cómo se lee un informe

El resumen es una línea por regla, ordenado por severidad. Tres cosas que conviene saber para interpretarlo:

La cuota del sitio

medium  META-TITLE-TOO-LONG  173,654  (80% of the site)

Cuando una regla afecta al 40% o más de las páginas, la línea añade su cuota. Es la diferencia entre una lista de páginas que arreglar una a una y un problema sistémico: 2.193 imágenes rotas son 2.193 arreglos; 173.654 títulos largos al 80% del sitio son una plantilla o un patrón de publicación, y se arregla en un sitio.

Los problemas de plantilla

high  ASSET-IMG-EMPTY-ALT-LINK  13 template issues (567 pages) + 90 more findings
      e.g. https://ejemplo.com/a · https://ejemplo.com/b

Cuando el mismo defecto aparece por la misma causa en muchas páginas —el logo de la cabecera, un enlace del pie, el <h4> de la firma del autor— se cuenta como un problema, con ejemplos. Las filas siguen todas en el fichero: lo que cambia es el recuento del informe, no lo que se guarda.

Ver todas las URLs de una regla

El resumen nunca enumera. Para la lista completa:

crawlforge report crawl.sqlite --rule HTTP-404-INTERNAL

Sale ordenada, con los grupos de plantilla primero y su causa al lado. Es el comando que sustituye al «… y 26 más» que no llevaba a ninguna parte.

La ficha de una URL: ¿quién enlaza aquí?

La pregunta que más se repite en una auditoría tiene su propio comando:

crawlforge inspect crawl.sqlite 'https://cliente.com/pagina/'

Vale también la ruta sola (/pagina/), el dominio sin esquema, y con o sin la barra final. Si te equivocas, el error sugiere las URLs más parecidas del fichero en vez de decir solo «no está».

La ficha enseña el estado HTTP, lo extraído (título, meta description, H1, palabras, canonical, indexabilidad), los hallazgos de esa URL, su cadena de redirecciones si redirige, sus imágenes, y —la sección estrella— quién enlaza a esa página: deduplicado por página que enlaza, con su texto de ancla, si es nofollow y desde qué región (los enlaces de contenido primero, el ruido de nav y footer después). Salida real, recortada:

$ crawlforge inspect crawl-tumedio-com.sqlite '/nueva-piscina-municipal-abre-en-junio'

── Inlinks (24) ─────────────────────────────
  By region: unknown 13 · main 11 · 0 nofollow
  Linking pages, content links first:
    main     "la nueva piscina abre en junio" — https://tumedio.com/obras-del-polideportivo-terminadas/
    main     "la piscina municipal" — https://tumedio.com/presupuesto-municipal-2026/
    unknown  (no anchor text) ×4 — https://tumedio.com/tag/deportes/

── Outlinks (156: 108 internal, 48 external) ─
     200  https://tumedio.com/quienes-somos/ "¿Quiénes somos?"

Los salientes enseñan el código de estado del destino con los rotos primero: la ficha de una página es también su triaje de enlaces rotos. Y si la URL inspeccionada es una imagen, la ficha dice en qué páginas se usa — la dirección contraria a la sección de imágenes.

Cada lista corta en 20 filas y el corte dice el comando exacto que la completa (--limit all; también acepta un número). --lang es la traduce, y --format md con --out ficha.md produce una ficha para pegar en un ticket:

crawlforge inspect crawl.sqlite '/pagina/' --format md --out ficha.md

3.ter Escenarios completos

Auditoría de un cliente, de principio a fin

crawlforge crawl https://cliente.com/
crawlforge report crawl-cliente-com.sqlite --lang es          # ¿qué tiene?
crawlforge report crawl-cliente-com.sqlite --rule CONTENT-H1-MISSING   # ¿dónde?
crawlforge inspect crawl-cliente-com.sqlite '/esa-pagina/' --lang es   # ¿quién enlaza aquí?
crawlforge export crawl-cliente-com.sqlite --format xlsx --out cliente.xlsx
crawlforge report crawl-cliente-com.sqlite --format html --out cliente.html --lang es

El .xlsx es para trabajar; el .html es para enviar.

Vigilar un despliegue

crawlforge crawl https://cliente.com/                         # antes de publicar
# … se publica …
crawlforge crawl https://cliente.com/                         # el anterior pasa a .prev.sqlite
crawlforge diff crawl-cliente-com.prev.sqlite crawl-cliente-com.sqlite --lang es

Y en un pipeline de CI, sobre la carpeta compilada y sin red:

crawlforge audit ./dist --base https://cliente.com/ --out nuevo.sqlite
crawlforge diff referencia.sqlite nuevo.sqlite --fail-on high || exit 1

Revisar una cartera de sitios

for s in blog1.com blog2.com blog3.com; do
  crawlforge crawl "https://$s/" --out "cartera/$s.sqlite"
done
crawlforge portfolio ./cartera --lang es

Un solo panel sobre todos los ficheros: qué cambió desde el rastreo anterior de cada sitio, qué reglas fallan en cuántos sitios, y una línea por sitio. Entero en §3.quater.

Un conjunto concreto de URLs

printf '%s\n' https://cliente.com/landing-a https://cliente.com/landing-b > urls.txt
crawlforge list urls.txt --lang es

3.quater La cartera: muchos sitios a la vez

Una auditoría suelta es una foto. Quien lleva muchos sitios necesita otras dos respuestas: qué se rompió desde la semana pasada y qué falla en todos a la vez. Eso es portfolio:

crawlforge portfolio ./rastreos/               # un directorio se recorre buscando *.sqlite
crawlforge portfolio a.sqlite b.sqlite c.sqlite

Los .prev.sqlite que hay junto a tus rastreos no cuentan como sitios: cada uno es el «antes» del rastreo de al lado, y el panel compara la pareja solo. Es el mismo fichero que usa diff, producido de la misma manera: repitiendo el rastreo sobre el mismo fichero de salida.

El panel cuenta sitios, no ficheros. Una lista list que auditó varios sitios ocupa una sola fila, con todos ellos en la etiqueta, pero suma cada uno de sus sitios al recuento de la cabecera, al «salta en N de M sitios» (sus hallazgos se reparten según el sitio de su URL) y al tope de sitios del nivel. Hasta la 0.15.0 contaba como uno, y una lista con cincuenta dominios pasaba por un solo sitio.

Salida real, recortada (una cartera de prueba de cinco sitios; dos ficheros los rastreó una versión anterior, un rastreo quedó truncado y otro es de modo lista):

$ crawlforge portfolio ./cartera --lang es

── Panel de cartera ─────────────────────────
  5 sitios · rastreos del 2026-08-04 al 2026-08-04

── Avisos ───────────────────────────────────
  AVISO     No todos los sitios se rastrearon con el mismo catálogo de reglas (0.4.0,
            0.6.2). Una regla puede faltar en un sitio porque no existía cuando se rastreó.

── Qué cambió ───────────────────────────────
  1 de 5 sitios tiene un rastreo anterior (.prev.sqlite) con el que comparar.

  Hallazgos nuevos críticos y altos:
    https://alpha.example/
      crítico   HTTP-404-INTERNAL                   2
        https://alpha.example/p/000005/
        https://alpha.example/p/000006/

  El resto, sitio a sitio:
    https://alpha.example/
      Hallazgos resueltos 2 · Códigos de estado que empeoran 2

── Qué falla en toda la cartera ─────────────
  Una regla que salta en la mayoría de los sitios rara vez es contenido: suele ser una
  plantilla o un plugin compartido — un arreglo que sirve para todos.

  medio     CANON-CROSS-DOMAIN             3 de 5 sitios
  crítico   HTTP-NO-HTTPS                  2 de 5 sitios
  medio     INDEX-DEEP-PAGE                1 de 5 sitios (2 no concluyentes)

── La cartera de un vistazo ─────────────────
       URLs  index.  crit  high   med   low  info  rastreado   sitio
        240       0     2     0   118     0     0  2026-08-04  https://alpha.example/
          8       0     1     1     7     0     0  2026-08-04  http://127.0.0.1:8912/  (truncado)

Tres cosas de esa salida son deliberadas, y son las que hacen fiable el panel:

  • «1 de 5 sitios (2 no concluyentes)» — un rastreo truncado o de modo lista nunca evaluó las reglas que necesitan el grafo de enlaces completo, así que para esas reglas el panel separa tres estados: dispara, no dispara, y no se pudo evaluar. Una regla que no aparece en un sitio truncado no es una regla que ahí pase.
  • El aviso del catálogo va arriba. Ficheros rastreados con catálogos de reglas distintos no se comparan en silencio: una regla puede «faltar» en un sitio porque aún no existía.
  • El rango de fechas se dice siempre, y si entre el rastreo más viejo y el más nuevo hay más de una semana el panel lo avisa: eso no es una foto de la cartera, y «qué cambió» cubriría un periodo distinto en cada sitio.

Un fichero que no se puede abrir —no es un rastreo, es una base de otro programa, tiene un esquema más nuevo que el binario— sale en «Ficheros apartados» con su motivo, y el resto del panel se produce igual. Un fichero malo no te cuesta los otros once.

Lo demás funciona como report: --lang es traduce el panel, y --format md o --format html con --out producen un fichero para pegar en un ticket o enviar:

crawlforge portfolio ./cartera --format html --out panel.html --lang es

El panel no es del nivel gratuito. La CLI corre por defecto como el nivel más alto; solo importa si defines CRAWLFORGE_TIER (ver §5).


4. Lo que hoy no hace

Dicho por delante, para que no pierdas tiempo buscándolo:

  • No renderiza JavaScript. Un sitio cuyo contenido se monta en el navegador se verá vacío. Está previsto.
  • No sigue los sitios externos. De los enlaces salientes se comprueba el estado —que es lo que hace saltar HTTP-404-EXTERNAL, y HTTP-EXTERNAL-UNVERIFIABLE cuando el otro sitio rechaza la comprobación con un 401, 403 o 429—, pero de otro dominio no se parsea ni se rastrea nada. No hay flag --follow-external: rastrear entero un sitio ajeno no es algo que la herramienta ofrezca desde la línea de comandos. La clave follow_external existe en el fichero de configuración y sigue apagada por defecto; una reanudación ignora lo que el fichero diga de ella, igual que ignora un ignore_robots guardado.
  • No hay interfaz gráfica todavía.
  • No hay programación de rastreos. El panel de cartera (§3.quater) lee los ficheros que ya tienes; producirlos a un ritmo sigue siendo trabajo de tu cron.
  • HTTP-TEMP-REDIRECT no existe todavía: necesita un histórico de rastreos que aún no existe.

Están implementadas todas las reglas del catálogo salvo HTTP-TEMP-REDIRECT; crawlforge rules lista las que trae tu build.


5. Cuando algo va mal

Síntoma Qué pasa
El rastreo acaba con muchas menos URLs de las que esperas Algo lo cortó: --max-urls, --max-depth, --max-duration, o el tope de 1.000 del nivel gratuito si está puesto CRAWLFORGE_TIER=free. El informe dice que el rastreo quedó truncado y por qué límite.
Un report menciona hallazgos «no evaluados» El rastreo se truncó y las reglas que necesitan el grafo completo se han silenciado a propósito.
El sitio devuelve 429 o 503 Baja --concurrency. La herramienta ya reduce a la mitad su concurrencia en ese host tras tres 429, 503, conexiones cortadas o timeouts seguidos, espera lo que pida un Retry-After (hasta dos minutos) antes de reintentar y respeta el Crawl-delay del robots.txt, pero un WAF puede ser más estricto que todo eso. Si redujo la concurrencia, el resumen del rastreo lo dice y cuántas veces.
«no such file» al hacer diff Falta el .prev.sqlite: solo aparece al repetir un rastreo sobre el mismo fichero de salida.
El programa parece colgado tras rastrear Está en la pasada final: enlaces entrantes y reglas de conjunto. Dice por qué regla va (final pass · rule 7/29 · …). En sitios grandes tarda minutos.
Un rastreo antiguo no se puede reanudar Solo se rechaza si el fichero es más nuevo que el programa, o si le falta cruzar una migración que cambia lo que el motor escribe. Se sigue abriendo con report, export y diff.
Aparecen .sqlite-wal y .sqlite-shm al lado El rastreo no cerró limpiamente. No copies solo el .sqlite: te faltarían datos. Vuelve a abrirlo con crawlforge report para que los consolide.

Los errores dicen qué fichero falta y qué comando lo genera. Si alguno no lo dice, es un fallo del producto y merece anotarse.


6. Referencia rápida

crawlforge crawl  <URL>       # rastrear por HTTP
crawlforge audit  <DIR> --base <URL>   # auditar una carpeta compilada
crawlforge list   <FICHERO>   # una lista exacta de URLs
crawlforge resume <FICHERO>   # continuar un rastreo cortado
crawlforge report <FICHERO>   # resumen, o --format md|html
crawlforge export <FICHERO> --format xlsx --out a.xlsx
crawlforge diff   <ANTES> <DESPUES> [--fail-on high] [--out d.sqlite]
crawlforge diff   <DIFF> [--rule ID | --change TIPO]   # abrir un diff guardado
crawlforge portfolio <RUTA>... [--format md|html --out f]   # panel sobre muchos rastreos
crawlforge rules  [--category X] [--detail] [--format json]
crawlforge config schema      # JSON Schema del YAML de --config
# report, inspect, export, diff y portfolio aceptan --config <YAML> para aplicar su `rules:`

Cualquiera de ellos con --help da la lista completa de opciones.