r/framer está lleno de hilos sobre 'Los LLM no pueden leer los sitios de Framer' porque Framer envía páginas con mucho JavaScript que los rastreadores de LLM no pueden procesar. El mismo problema afecta a las aplicaciones React de una sola página. Este tutorial explica cómo auditar su sitio para detectar problemas de legibilidad de LLM antes de que afecten su visibilidad de ChatGPT y Perplexity.
Requisitos previos
- Python 3.8+
- Una clave API de Scavio
- La URL de un sitio para auditar
Guia paso a paso
Paso 1: Obtener el HTML renderizado
Utilice Scavio para recuperar la página completamente renderizada (post-JS).
import requests, os
def fetch_rendered(url):
r = requests.post('https://api.scavio.dev/api/v1/extract',
headers={'x-api-key': os.environ['SCAVIO_API_KEY']},
json={'url': url, 'render_js': True})
return r.json()Paso 2: Obtener el HTML sin formato (anterior a JS)
Busque sin renderizado JS para ver qué ven realmente los rastreadores de LLM.
def fetch_raw(url):
r = requests.post('https://api.scavio.dev/api/v1/extract',
headers={'x-api-key': os.environ['SCAVIO_API_KEY']},
json={'url': url, 'render_js': False})
return r.json()Paso 3: Comparar extracción de texto
Cuente las palabras en cada uno. Si el formato raw es una fracción del renderizado, tienes un problema.
from bs4 import BeautifulSoup
def count_words(html):
return len(BeautifulSoup(html, 'html.parser').get_text().split())
def llm_readable_ratio(url):
raw = fetch_raw(url)
rendered = fetch_rendered(url)
rw = count_words(raw.get('html', ''))
rnw = count_words(rendered.get('html', ''))
return rw / rnw if rnw else 0Paso 4: Verifique metadatos y datos estructurados
A los LLM les encantan los títulos, las descripciones y JSON-LD. Asegúrese de que estén en HTML sin formato.
def check_metadata(raw_html):
soup = BeautifulSoup(raw_html, 'html.parser')
return {
'title': bool(soup.title),
'description': bool(soup.find('meta', {'name': 'description'})),
'og_tags': bool(soup.find('meta', {'property': 'og:title'})),
'json_ld': bool(soup.find('script', {'type': 'application/ld+json'}))
}Paso 5: Informar la auditoría
Imprima un resumen con índice de legibilidad y presencia de metadatos.
def audit(url):
raw = fetch_raw(url)
ratio = llm_readable_ratio(url)
meta = check_metadata(raw.get('html', ''))
print(f'URL: {url}')
print(f'LLM-readable ratio: {ratio:.0%}')
print(f'Metadata: {meta}')
if ratio < 0.5:
print('WARNING: Most content is hidden behind JavaScript. LLMs will miss it.')Ejemplo en Python
import os, requests
from bs4 import BeautifulSoup
API_KEY = os.environ['SCAVIO_API_KEY']
def fetch(url, render):
r = requests.post('https://api.scavio.dev/api/v1/extract',
headers={'x-api-key': API_KEY},
json={'url': url, 'render_js': render})
return r.json().get('html', '')
def audit(url):
raw_words = len(BeautifulSoup(fetch(url, False), 'html.parser').get_text().split())
rendered_words = len(BeautifulSoup(fetch(url, True), 'html.parser').get_text().split())
ratio = raw_words / rendered_words if rendered_words else 0
print(f'{url}: {ratio:.0%} LLM-readable')
if ratio < 0.5:
print(' Problem: LLMs only see half of the content.')
audit('https://example.framer.com')Ejemplo en JavaScript
async function fetchHtml(url, renderJs) {
const r = await fetch('https://api.scavio.dev/api/v1/extract', {
method: 'POST',
headers: { 'x-api-key': process.env.SCAVIO_API_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ url, render_js: renderJs })
});
return (await r.json()).html;
}
async function audit(url) {
const raw = await fetchHtml(url, false);
const rendered = await fetchHtml(url, true);
const rw = raw.replace(/<[^>]+>/g, ' ').split(/\s+/).length;
const rnw = rendered.replace(/<[^>]+>/g, ' ').split(/\s+/).length;
console.log(`${url}: ${(rw / rnw * 100).toFixed(0)}% LLM-readable`);
}
audit('https://example.framer.com');Salida esperada
https://example.framer.com: 12% LLM-readable
Problem: LLMs only see half of the content. Consider SSR, Next.js app router, or a static hero fallback.