¿Cómo extraer datos de sitios web protegidos por Cloudflare en n8n?

Estoy creando un flujo de resumen diario que obtiene contenido de blogs de la industria HVAC. Tres de mis fuentes (HouseCall Pro, Jobber Academy, HVAC Informed) están protegidas por Cloudflare. Cuando mi nodo HTTP Request accede a estas URLs, obtiene una página de desafío de Cloudflare “Just a moment…” 403 en lugar del contenido real.

Estos sitios tampoco tienen feeds RSS, por lo que los nodos RSS Feed Read no funcionan.

Lo que he intentado:

  • Nodo HTTP Request (GET) — devuelve HTML de desafío de Cloudflare

  • Nodo RSS Feed Read — devuelve errores de análisis XML (las páginas no son RSS)

Preguntas:

  1. ¿Hay alguna forma de eludir la protección bot de Cloudflare de forma nativa en n8n?

  2. ¿Debería usar Apify, ScrapingBee o BrightData para estas fuentes?

  3. ¿Existe un nodo comunitario que maneje sitios protegidos por Cloudflare?

  4. ¿Ayudaría agregar encabezados similares a navegadores (User-Agent, Accept-Language, etc.)?

@Asim_Arman hizo una verificación rápida de tus 3 fuentes antes de recomendar nada y hay buenas noticias para una de ellas — HouseCall Pro en realidad no está en Cloudflare y su /feed/ funciona bien si solo añades un User-Agent de navegador real al nodo HTTP Request. probado con Mozilla/5.0 UA contra Housecall Pro y obtuve XML RSS válido. tu 403 allí casi seguramente es porque el nodo HTTP Request de n8n usa por defecto un UA genérico (o ninguno) y el sitio tiene un filtro UA básico. cambia a RSS Feed Read apuntado a Housecall Pro con anulación de User-Agent y listo para ese, sin necesidad de scraping.

Jobber (getjobber.com) está genuinamente en Cloudflare — el header cf-ray está presente en las respuestas — y no hay RSS para la sección academy. HVAC Informed tampoco expone RSS. esos dos genuinamente necesitan un servicio de scraping.

sobre la pregunta de bypass-cloudflare-natively, no hay forma real de hacerlo en n8n. su desafío requiere ejecutar JS para resolver proof-of-work más fingerprinting de comportamiento, y el nodo HTTP Request es un cliente http plano sin motor de navegador. las librerías estilo cloudscraper de 2022-2023 están todas rotas contra Cloudflare actual también, así que los pocos nodos de comunidad que las envuelven no funcionan tampoco.

headers tipo navegador solucionan filtros UA básicos (que es lo que el 403 de HouseCall Pro era, irónicamente) pero no hacen nada contra el verdadero desafío de Cloudflare — hacen fingerprinting de TLS, JA3, timing de solicitudes y ejecución de JS más allá de solo headers.

para ScrapingBee vs Apify vs BrightData — para tu caso de uso (digest diario de bajo volumen, solo 2 sitios que necesitan scraping real) ScrapingBee o Firecrawl es lo más limpio. comparación aproximada:

{
  "scrapingbee": {"per_request": "~$0.001-0.005", "fit": "bajo-medio volumen, simple, rápido"},
  "scraperapi": {"per_request": "~$0.001-0.003", "fit": "comparable a scrapingbee"},
  "firecrawl": {"per_request": "~$0.001", "fit": "scrape-to-markdown, salida lista para LLM"},
  "brightdata": {"per_request": "~$0.005-0.02", "fit": "objetivos más difíciles, caro pero más confiable"},
  "apify": {"per_request": "varía", "fit": "flujos multi-página complejos"}
}

ScrapingBee llamado vía HTTP Request se ve así:

{
  "method": "GET",
  "url": "https://app.scrapingbee.com/api/v1/",
  "qs": {
    "api_key": "<your_key>",
    "url": "https://www.getjobber.com/academy/",
    "render_js": "true",
    "premium_proxy": "true"
  }
}

el estado final es un RSS Feed Read para HouseCall Pro, dos llamadas HTTP ScrapingBee para Jobber e HVAC Informed, luego parse → digest. mucho más barato que tratar los 3 como protegidos por Cloudflare cuando solo uno lo está realmente.

¡Muchas gracias! Estoy probando esto y te aviso

¡De nada! ¡Cuéntame cómo funciona!

Me gustaría añadir que Apify cuenta con el Smart Article Extractor que puede (generalmente) detectar automáticamente enlaces de artículos en una página y luego rasparlos uno por uno.

Y emula un navegador para que también pueda eludir Cloudflare. También es un actor de uso único, así que una llamada diaria a tres blogs probablemente se ajuste a su nivel gratuito.

Para casos más generales (no artículos), puedes obtener contenido estructurado usando su Website Content Crawler y luego procesarlo a través de un modelo de IA pequeño, como haiku o gpt minis.

Para sitios más complejos como Jobber e HVAC Informed, si utilizas una instancia n8n autohospedada con Docker y prefieres evitar soluciones de pago, una alternativa interesante consiste en ejecutar SeleniumBase en modo Undetected ChromeDriver (UC) dentro de un contenedor Docker separado. El modo UC (Undetected ChromeDriver) de SeleniumBase es actualmente una de las herramientas de código abierto más robustas para eludir las protecciones de Cloudflare.

Puedes entonces configurar este contenedor para ejecutar tus scripts de scraping y luego activarlos directamente desde n8n mediante el nodo Execute Command (o utilizando una herramienta personalizada si has estructurado tu flujo alrededor de un Agente IA n8n.

Una cosa que nadie ha mencionado aún: antes de recurrir a un servicio de scraping, verifica si estos sitios tienen un sitemap o feed Atom escondido en una ruta no estándar.

Intenta acceder a estos antes de pagar por nada:

  • /sitemap.xml
  • /sitemap_index.xml
  • /feed/
  • /atom.xml
  • /blog/feed/ o /blog/rss/

Muchos blogs impulsados por WordPress/HubSpot (que tanto HouseCall Pro como Jobber utilizan) tienen feeds habilitados pero no están vinculados en el . El nodo RSS Read funcionará bien si accedes a la URL correcta.

Para los que genuinamente no tienen feed y Cloudflare te bloquea:

Enfoque más económico de nivel producción: Firecrawl o ScrapingBee a través del nodo HTTP Request.

HTTP Request → POST https://api.firecrawl.dev/v1/scrape
Headers: Authorization: Bearer
Body: { “url”: “https://target.com/blog”, “formats”: [“markdown”] }

Devuelve markdown limpio — no se necesita parsing de HTML. Ejecútalo en un Schedule Trigger una vez al día. Con 3 sitios × 1 solicitud/día, te mantendrás bien dentro de cualquier nivel gratuito.

Endurecimiento para producción (la parte que la mayoría de tutoriales omiten):

  1. Añade un reintento con backoff en el nodo HTTP Request — las APIs de scraping ocasionalmente te envían 429
  2. Almacena el hash de la última URL obtenida en una tabla Postgres para que no reproceses el mismo artículo mañana (idempotencia)
  3. Añade un workflow con Error Trigger — si tu digest falla silenciosamente el lunes, quieres saberlo antes del viernes

La parte “simplemente scrapealo” es fácil. Hacer que se ejecute de forma confiable cada día durante 6 meses sin fallos silenciosos es donde la mayoría de workflows de digest diarios se rompen.

Oye, te agradezco mucho el desglose detallado, achamm. Esto fue muy útil y terminé siguiendo la mayoría.

Lo que seguí exactamente:

  • HouseCall Pro - confirmé que no era Cloudflare, agregué el encabezado User-Agent al nodo HTTP Request, lo apunté a /feed/, lo pasé por un nodo XML, luego un nodo Code. Ahora funciona perfectamente.

  • Jobber e HVAC Informed - usé Firecrawl en lugar de ScrapingBee (el mismo nivel de precio que mencionaste, solo preferí el resultado en markdown para pasar a Claude).

    Enfoque de dos pasos: primero raspar la página de listado para extraer URLs de artículos, luego raspar cada artículo individualmente a través de una segunda HTTP Request.

Lo que añadimos más allá de tus recomendaciones:

  • Un nodo Parse Code después de cada solicitud Firecrawl para normalizar todas las fuentes en campos idénticos (title, content, link, feedSource) antes de fusionarlas

Te agradezco mucho hermano, me alegra tenerte en la comunidad

¡De nada, nos alegra haber podido ayudarte!

Si tienes la posibilidad de auto-alojar una herramienta (por ejemplo mediante Docker en el mismo servidor que tu n8n) y quieres evitar los límites de cuota de las plataformas de pago (como Apify, ScrapingBee o Firecrawl), deberías echar un vistazo a FlareSolverr.
Es un proxy de código abierto que ejecuta un navegador sin interfaz gráfica en segundo plano para resolver el desafío de Cloudflare y devolverte el HTML bruto de la página. Solo tienes que consultarlo localmente con un simple nodo HTTP Request en n8n.
Sin embargo, FlareSolverr te devuelve el código HTML bruto. Por lo tanto, tendrás que añadir un nodo HTML o un nodo Code justo después para analizar y estructurar tú mismo los datos en JSON.
un pequeño ejemplo:

{
“nodes”: [
{
“parameters”: {
“jsCode”: “const html = $json.solution.response;\n\nfunction getText(html, startMarker, endMarker) {\n const start = html.indexOf(startMarker);\n if (start === -1) return ‘’;\n const end = html.indexOf(endMarker, start);\n if (end === -1) return ‘’;\n return html.substring(start + startMarker.length, end).trim();\n}\n\nfunction getMetaContent(html, name) {\n const regex = new RegExp(<meta name=\"${name}\" content=\"([^\"]+)\", ‘i’);\n const match = html.match(regex);\n return match ? match[1] : ‘’;\n}\n\nlet title = getText(html, ‘<h1 class="story-title">’, ‘’);\n// limpia el título de las etiquetas\ntitle = title.replace(/<a[^>]>([^<])</a>/, ‘$1’).trim();\n\nconst description = getMetaContent(html, ‘description’);\n\nlet content = getText(html, ‘<div class="articlebody clear cf" id="articlebody">’, ‘<div class="stophere" id="hiddenH1">’);\n// limpia el contenido\ncontent = content\n .replace(/<script[^>]>[\s\S]?</script>/gi, ‘’)\n .replace(/<style[^>]>[\s\S]?</style>/gi, ‘’)\n .replace(/<[^>]+>/g, ’ ')\n .replace(/\s+/g, ’ ')\n .trim();\n\nreturn [{\n title: title,\n description: description,\n content: content\n}];”
},
“type”: “n8n-nodes-base.code”,
“typeVersion”: 2,
“position”: [
-912,
4480
],
“id”: “a6275095-c4ed-427f-a791-620bcd83710b”,
“name”: “Extract from HTML”
},
{
“parameters”: {
“method”: “POST”,
“url”: “=http://flaresolverr:8191/v1”,
“sendHeaders”: true,
“headerParameters”: {
“parameters”: [
{
“name”: “Content-Type”,
“value”: “application/json”
}
]
},
“sendBody”: true,
“bodyParameters”: {
“parameters”: [
{
“name”: “cmd”,
“value”: “request.get”
},
{
“name”: “url”,
“value”: “={{ $json.url }}”
},
{
“name”: “maxTimeout”,
“value”: 90000
}
]
},
“options”: {
“timeout”: 120000
}
},
“type”: “n8n-nodes-base.httpRequest”,
“typeVersion”: 4.4,
“position”: [
-1120,
4480
],
“id”: “4c884f8a-09c7-4c13-b7e8-ac38098b00f6”,
“name”: “FlareSolverr”,
“retryOnFail”: true
}
],
“connections”: {
“Extract from HTML”: {
“main”: [

]
},
“FlareSolverr”: {
“main”: [
[
{
“node”: “Extract from HTML”,
“type”: “main”,
“index”: 0
}
]
]
},
“pinData”: {},
“meta”: {
“templateCredsSetupCompleted”: true,
“instanceId”: “03428eda2f2a76af33393f7fa59419dea9e2e74e0d630500b36df8cf5b8e161d”
}
}