2026-09-19 · cf-crawl: Browser Run desde Workers (scrape + crawl)
Repo: ~/nef/cf-crawl/cf-crawl-project · Worker: https://cf-crawl-project.nef.workers.dev
Qué se construyó
Worker TS con binding browser que expone endpoints sin API token:
/scrape?url=&selectors=h1,h2— extracción por CSS selectors/links?url=&sameDomain=true— descubrimiento de URLs/markdown?url=— página completa a markdown/read?url=— modo lector: HTML limpio, sin scripts/rastreo, paga soft-paywalls/crawl?url=&maxPages=&pattern=&include=markdown— BFS: links() + markdown() (el/crawlnativo de Browser Run es solo REST API, no existe como quickAction)
Trapis verificados (no repetir debugging)
env.BROWSER.quickAction()devuelve unResponse, no JSON ya parseado. Hacerawait res.json()y revisar{success, result}/{success:false, errors}. stringify directo de un Response da{}(falso positivo que ya mordimos).scrapepideelements: [{selector}](array), noselectorsuelto.- Requiere
compatibility_date >= 2026-03-24; en dev local solo funciona con remote. - Rate limit del plan Free agresivo: 3s de pacing entre páginas + backoff de 12s.
- El token OAuth viejo (env
CLOUDFLARE_API_TOKEN) no tenía permisos →unset CLOUDFLARE_API_TOKEN && npx wrangler login(OAuth incluyebrowser:write).
Sitios de noticias probados
- bbc.com/mundo: funciona bien. Crawl de artículos con
?pattern=/mundo/articles/da cuerpos limpios; /read renderiza 1600+ palabras, 0 scripts. - jornada.com.mx: home scrapeable (links), pero
/ultimasnoticiasbloquea (Cloudflare bot protection contra Browser Run). - elpais.com: SÍ pasa Browser Run.
/readde un artículo sacó ~1260 palabras. El/linksde la sección devuelve 0 (render lazy), pero /read de una URL de artículo concreta funciona.
Nota honesta sobre “sin paywall”
Browser Run SIEMPRE se identifica como bot (no configurable). /read limpia el DOM:
quita <script>, <iframe>, <form>, <nav>, <footer>, frontmatter YAML, y
inyecta CSS que oculta overlays .paywall/.modal/.overlay/.subscribe y fuerza
max-height:none → recupera paywalls blandos (texto ya presente en DOM, tapado).
NO rompe paywalls duros (donde el servidor no manda el texto). Es un lector limpio,
no un burlador. Para EL PAÍS de pago real, no hay cuerpo que rascar.
Uso del puente harvest_cf.py (news-silo)
.venv/bin/python harvest_cf.py https://www.bbc.com/mundo --pages 5 \
--pattern '/mundo/articles/' --out data/harvest-bbc.json
.venv/bin/python silo.py -i data/harvest-bbc.json -o data/silo-bbc.json
El crawl solo abre páginas que casen con —pattern y excluye la home. Output ya es {records,texts} y silo.py consume data[“texts”].
Endpoints nuevos en el worker
/read?url=→ HTML de lectura limpia (modo lector) sin rastreo. Botón 📖 en index.html./crawl?...&include=markdown&pattern=REGEX→ cuerpos completos para el silo.
Siguiente paso natural
Sembrar harvest_cf.py con varios medios que toleran Browser Run (BBC, EL PAÍS, Milenio) → silo_medios.py. Medir cuota: el plan Free da ~10 min browser/mes, cada crawl de 5 páginas consume buen trecho. Ver dashboard Workers → Browser Run.
2026-09-19 (cierre) · LLEGÓ EL CORREO DE CUOTA → PIVOTE A /rss GRATUITO
Llegó aviso de Cloudflare por consumo de Browser Run. Decisión: no seguir gastando cuota de browser. Se apaga Browser Run por defecto y se construye la vía gratuita.
Qué cambió en el worker
- Nuevo
/rss?url=— parsea RSS/Atom confetchnormal. 0 min de browser, gratis en el plan Workers Free siempre. Parámetros:?url=<feed>. - Gate
BROWSER_ENABLED(var en wrangler.jsonc, defaultoff). Conoff, los endpoints de Browser Run devuelven 503 con mensaje claro;/rsssigue vivo. - Re-activar cuota cuando toque:
wrangler deploy --var BROWSER_ENABLED:on(o editar wrangler.jsonc →"BROWSER_ENABLED":"on").
Puente harvest_cf.py — modo RSS (gratuito)
.venv/bin/python harvest_cf.py --rss bbc-mundo elpais elfinanciero \
--out data/harvest-rss.json
.venv/bin/python silo.py -i data/harvest-rss.json -o data/silo-rss.json --min-cluster-size 3
Feeds verificados que SÍ pasan por /rss desde el edge de CF:
- bbc-mundo → feeds.bbci.co.uk/mundo/rss.xml (34)
- elpais → feeds.elpais.com/mrss-s/pages/ep/site/elpais.com/portada (60)
- elfinanciero → elfinanciero.com.mx/rss (60) 154 notas, 9 silos, coste browser = 0. Resultado en data/silo-rss.json. No pasan /rss (bloquean el fetch incluso desde CF): jornada (403), milenio (403), aristegui (530/defunto), eluniversal (404). Para esos, más adelante: Workers AI embeddings gratuitos o un feed proxy, no Browser Run.
Regla para no repetir el susto
Browser Run = cuota escasa. Usarlo SOLO para /read puntual o cosechar 1 artículo sin
RSS. Para escalar el silo, siempre /rss + Workers AI. La clave del silo no es el
scraping, es el patrón de feed estable.
brave-search
No estaba configurado en ~/.config/opencode/opencode.jsonc (ni en ningún opencode.json
de ~/nef). Nada que desactivar. La búsqueda la cubren los MCP de Cloudflare ya activos
(documentation + observability). Si algún día añades brave, pon enabled:false ahí.