Pular para conteúdo

WEB_SCRAPER (SCRAPE) - Extração Automática de Dados Web

O que é este Node?

O WEB_SCRAPER (SCRAPE) é o node responsável por extrair dados automaticamente de websites utilizando técnicas inteligentes de web scraping. Ele navega pela estrutura HTML da página, identifica o conteúdo principal e extrai textos, links, imagens e metadados de forma automática.

Por que este Node existe?

Coletar dados de websites manualmente é demorado, repetitivo e propenso a erros. O WEB_SCRAPER (SCRAPE) existe para:

  1. Automatizar coleta de dados: Extrai informações de sites de forma programática e escalável
  2. Integrar dados externos: Permite usar dados de sites externos em seus fluxos automatizados
  3. Monitorar conteúdo web: Coleta informações atualizadas de produtos, notícias, preços, etc.
  4. Enriquecer conversas: Busca dados em tempo real para responder perguntas dos usuários
  5. Respeitar limites éticos: Implementa delays, respeita robots.txt e evita sobrecarga de servidores

Como funciona internamente?

Quando o WEB_SCRAPER (SCRAPE) é executado, o sistema:

  1. Valida a URL: Verifica se a URL fornecida é válida e acessível
  2. Verifica robots.txt: Se configurado, consulta o robots.txt do site para verificar permissões
  3. Faz requisição HTTP: Busca o conteúdo HTML da página com headers personalizados
  4. Analisa HTML: Usa Cheerio para parsear o HTML e criar uma árvore DOM navegável
  5. Remove elementos indesejados: Elimina scripts, estilos, menus, rodapés conforme configurado
  6. Extrai conteúdo principal: Identifica automaticamente a área de conteúdo principal da página
  7. Coleta dados adicionais: Extrai links, imagens, metadados conforme solicitado
  8. Limpa e formata: Normaliza espaços em branco, remove linhas vazias e formata o texto
  9. Aplica rate limiting: Aguarda delay configurado antes da próxima requisição
  10. Retorna resultado: Devolve os dados extraídos no formato especificado

Código interno (web-scraper-executor.service.ts:95-197):

async execute(data: WebScraperNodeData, context: ExecutionContext): Promise<ExecutionResult> {
  const startTime = Date.now();
  this.logger.log(`Executing Web Scraper node ${context.nodeId} for URL(s): ${data.url || data.urls?.join(', ')}`);

  try {
    // Validate configuration
    const validation = await this.validate(data);
    if (!validation.isValid) {
      return {
        success: false,
        error: `Validation failed: ${validation.errors?.join(', ')}`,
        executionTime: Date.now() - startTime
      };
    }

    // Determine target URLs
    const urls = data.urls || (data.url ? [data.url] : []);
    if (urls.length === 0) {
      return {
        success: false,
        error: 'No URLs provided for scraping',
        executionTime: Date.now() - startTime
      };
    }

    // Check robots.txt if requested
    if (data.respectRobots) {
      for (const url of urls) {
        const robotsCheck = await this.checkRobotsTxt(url, data.userAgent || 'LuminaBot/1.0');
        if (!robotsCheck.allowed) {
          this.logger.warn(`Robots.txt disallows scraping for ${url}`);
          if (!data.ignoreErrors) {
            return {
              success: false,
              error: `Robots.txt disallows scraping for ${url}`,
              executionTime: Date.now() - startTime
            };
          }
        }
      }
    }

    // Execute scraping
    const results: ScrapingResult[] = [];
    const maxPages = Math.min(data.maxPages || 1, 50);
    const delay = Math.max(data.delay || 1000, 500);

    for (const url of urls.slice(0, maxPages)) {
      try {
        const scrapingResult = await this.scrapeUrl(url, data);
        results.push(scrapingResult);

        // Rate limiting
        if (urls.indexOf(url) < urls.length - 1) {
          await this.sleep(delay);
        }
      } catch (error) {
        this.logger.error(`Failed to scrape ${url}:`, error.message);

        if (!data.ignoreErrors) {
          return {
            success: false,
            error: `Failed to scrape ${url}: ${error.message}`,
            executionTime: Date.now() - startTime
          };
        } else {
          results.push({
            url,
            error: error.message,
            timestamp: new Date().toISOString(),
            processingTime: 0
          });
        }
      }
    }

    // Process results based on output format
    const processedData = this.formatResults(results, data);
    const totalItems = results.reduce((sum, result) => sum + (result.text ? 1 : 0), 0);

    this.logger.log(`Web scraping completed successfully for node ${context.nodeId}. Scraped ${results.length} pages, found ${totalItems} items`);

    return {
      success: true,
      data: processedData,
      executionTime: Date.now() - startTime,
      logs: [
        `Scraped ${results.length} URLs`,
        `Total items found: ${totalItems}`,
        `Processing time: ${Date.now() - startTime}ms`
      ]
    };

  } catch (error) {
    this.logger.error(`Web scraper execution failed for node ${context.nodeId}:`, error.stack);

    return {
      success: false,
      error: error.message || 'Unknown web scraping error',
      executionTime: Date.now() - startTime
    };
  }
}

Quando você DEVE usar este Node?

Use WEB_SCRAPER (SCRAPE) sempre que precisar de extrair dados estruturados de websites:

Casos de uso

  1. Monitoramento de preços: "Extraia diariamente os preços dos produtos concorrentes e salve no banco de dados"
  2. Agregação de notícias: "Colete as últimas notícias sobre tecnologia de 5 portais diferentes"
  3. Pesquisa de mercado: "Extraia avaliações de produtos de sites de e-commerce para análise"
  4. Enriquecimento de dados: "Busque informações adicionais sobre empresas em seus sites oficiais"
  5. Validação de informações: "Verifique se um endereço de loja existe no Google Maps"

Quando NÃO usar WEB_SCRAPER

  • Sites com API oficial: Use a integração direta da API ao invés de scraping
  • Sites que bloqueiam scrapers: Use serviços profissionais de scraping como ScraperAPI
  • Sites com CAPTCHA: O node não resolve CAPTCHAs automaticamente
  • Conteúdo JavaScript pesado: Use ferramentas com browser real como Puppeteer/Playwright

Parâmetros Detalhados

url (string, obrigatório)

O que é: A URL completa do website que você deseja extrair dados. Deve incluir o protocolo (http:// ou https://).

Padrão: Nenhum (obrigatório)

Flow completo para testar:

{
  "name": "Teste Web Scraper - URL Básica",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair Notícia",
        "parameters": {
          "operation": "scrape",
          "url": "https://www.bbc.com/news",
          "mode": "auto",
          "extractText": true,
          "extractLinks": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Mostrar Resultado",
        "parameters": {
          "message": "Título: {{scraper_1.data.data[0].title}}\n\nConteúdo extraído com sucesso!\nTotal de links: {{scraper_1.data.data[0].links.length}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Importe o flow e execute. O sistema irá extrair o conteúdo da página da BBC News e mostrar o título e quantidade de links encontrados.

mode (string, opcional)

O que é: Define o modo de extração de dados. Pode ser "auto" (extração automática inteligente), "manual" (usando seletores CSS) ou "template" (usando templates pré-configurados para tipos específicos de sites).

Padrão: "auto"

Valores aceitos: "auto", "manual", "template"

Flow completo para testar:

{
  "name": "Teste Web Scraper - Modo Auto",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Modo Auto",
        "parameters": {
          "operation": "scrape",
          "url": "https://example.com/article",
          "mode": "auto",
          "extractText": true,
          "extractMeta": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Resultado",
        "parameters": {
          "message": "Conteúdo extraído automaticamente:\n{{scraper_1.data.data[0].text}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: O modo "auto" identifica automaticamente a área principal de conteúdo da página sem precisar configurar seletores CSS.

extractText (boolean, opcional)

O que é: Define se o texto do conteúdo principal da página deve ser extraído. Quando true, o sistema tenta identificar automaticamente a área de conteúdo principal (artigo, post, produto) e extrai todo o texto relevante.

Padrão: true

Flow completo para testar:

{
  "name": "Teste Web Scraper - Extrair Texto",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair Artigo",
        "parameters": {
          "operation": "scrape",
          "url": "https://en.wikipedia.org/wiki/Artificial_intelligence",
          "mode": "auto",
          "extractText": true,
          "cleanText": true,
          "minTextLength": 100
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Mostrar Texto",
        "parameters": {
          "message": "Primeiros 500 caracteres:\n{{scraper_1.data.data[0].text.substring(0, 500)}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Execute e veja o texto principal do artigo da Wikipedia sendo extraído e limpo automaticamente.

O que é: Define se todos os links (tags ) da página devem ser extraídos. Útil para descobrir páginas relacionadas, navegação ou análise de estrutura do site.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Web Scraper - Extrair Links",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Coletar Links",
        "parameters": {
          "operation": "scrape",
          "url": "https://news.ycombinator.com",
          "mode": "auto",
          "extractLinks": true,
          "extractText": false
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Contar Links",
        "parameters": {
          "message": "Total de links encontrados: {{scraper_1.data.data[0].links.length}}\n\nPrimeiros 5 links:\n{{scraper_1.data.data[0].links.slice(0, 5).join('\n')}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Coleta todos os links do Hacker News e mostra os 5 primeiros.

extractImages (boolean, opcional)

O que é: Define se todas as URLs de imagens (tags ) devem ser extraídas. O sistema converte URLs relativas em absolutas automaticamente.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Web Scraper - Extrair Imagens",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Coletar Imagens",
        "parameters": {
          "operation": "scrape",
          "url": "https://unsplash.com",
          "mode": "auto",
          "extractImages": true,
          "extractText": false
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Mostrar Resultado",
        "parameters": {
          "message": "Total de imagens: {{scraper_1.data.data[0].images.length}}\n\nPrimeira imagem:\n{{scraper_1.data.data[0].images[0]}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Extrai todas as URLs de imagens da página.

extractMeta (boolean, opcional)

O que é: Extrai metadados da página incluindo meta tags, Open Graph, Twitter Cards, canonical URL, idioma e charset. Útil para SEO e análise de conteúdo.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Web Scraper - Metadados",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair Metadata",
        "parameters": {
          "operation": "scrape",
          "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
          "mode": "auto",
          "extractMeta": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Mostrar Meta",
        "parameters": {
          "message": "Título OG: {{scraper_1.data.data[0].metadata['og:title']}}\nDescrição: {{scraper_1.data.data[0].metadata['og:description']}}\nIdioma: {{scraper_1.data.data[0].metadata.lang}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Extrai metadados Open Graph de um vídeo do YouTube.

delay (number, opcional)

O que é: Tempo de espera em milissegundos entre requisições ao mesmo domínio. Respeita o servidor e evita bloqueios por sobrecarga.

Padrão: 1000 (1 segundo)

Mínimo: 500 (500ms - aplicado automaticamente se você tentar usar menos)

Flow completo para testar:

{
  "name": "Teste Web Scraper - Rate Limiting",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Múltiplas URLs",
        "parameters": {
          "operation": "scrape",
          "urls": [
            "https://example.com/page1",
            "https://example.com/page2",
            "https://example.com/page3"
          ],
          "mode": "auto",
          "delay": 2000,
          "extractText": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Resultado",
        "parameters": {
          "message": "Páginas extraídas: {{scraper_1.data.totalPages}}\nTempo total: {{scraper_1.data.totalProcessingTime}}ms"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Extrai 3 páginas com 2 segundos de intervalo entre cada requisição.

respectRobots (boolean, opcional)

O que é: Quando true, consulta o arquivo robots.txt do site antes de fazer scraping e respeita as regras de User-agent. Prática ética recomendada.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Web Scraper - Robots.txt",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Scraping Ético",
        "parameters": {
          "operation": "scrape",
          "url": "https://www.google.com",
          "mode": "auto",
          "respectRobots": true,
          "userAgent": "LuminaBot/1.0",
          "ignoreErrors": false
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Status",
        "parameters": {
          "message": "Scraping permitido: {{scraper_1.success}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Tenta fazer scraping respeitando robots.txt. Se bloqueado, retorna erro.

cleanText (boolean, opcional)

O que é: Aplica limpeza no texto extraído: normaliza espaços em branco, remove linhas vazias e aplica trim. Deixa o conteúdo mais legível.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Web Scraper - Limpeza de Texto",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair e Limpar",
        "parameters": {
          "operation": "scrape",
          "url": "https://example.com",
          "mode": "auto",
          "extractText": true,
          "cleanText": true,
          "normalizeWhitespace": true,
          "removeEmptyLines": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Texto Limpo",
        "parameters": {
          "message": "Texto formatado:\n{{scraper_1.data.data[0].text}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Extrai texto e aplica limpeza automática, removendo espaços extras.

outputFormat (string, opcional)

O que é: Define o formato de saída dos dados extraídos. Pode ser JSON (estruturado), CSV (tabular), XML (estruturado), HTML (original) ou TEXT (apenas texto).

Padrão: "json"

Valores aceitos: "json", "csv", "xml", "html", "text"

Flow completo para testar:

{
  "name": "Teste Web Scraper - Formato CSV",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair como CSV",
        "parameters": {
          "operation": "scrape",
          "url": "https://example.com",
          "mode": "auto",
          "extractText": true,
          "outputFormat": "csv"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Dados CSV",
        "parameters": {
          "message": "Formato CSV:\n{{scraper_1.data}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 700, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Teste: Retorna os dados em formato CSV pronto para exportação.

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim Tipo de operação ("scrape")
url string Sim* URL do website a extrair
urls string[] Sim* Array de URLs (alternativa a url)
mode string Não Modo de extração: "auto", "manual", "template" (padrão: "auto")
extractText boolean Não Extrair texto principal (padrão: true)
extractLinks boolean Não Extrair links da página (padrão: false)
extractImages boolean Não Extrair URLs de imagens (padrão: false)
extractMeta boolean Não Extrair metadados (padrão: false)
delay number Não Delay entre requisições em ms (padrão: 1000, mín: 500)
timeout number Não Timeout da requisição em ms (padrão: 30000)
userAgent string Não User-Agent customizado (padrão: "LuminaBot/1.0")
headers object Não Headers HTTP adicionais
respectRobots boolean Não Respeitar robots.txt (padrão: false)
cleanText boolean Não Limpar e formatar texto (padrão: false)
normalizeWhitespace boolean Não Normalizar espaços em branco (padrão: true se cleanText)
removeEmptyLines boolean Não Remover linhas vazias (padrão: true se cleanText)
minTextLength number Não Tamanho mínimo do texto extraído
outputFormat string Não Formato de saída: "json", "csv", "xml", "text" (padrão: "json")
ignoreErrors boolean Não Continuar mesmo com erros (padrão: false)
maxPages number Não Número máximo de páginas (padrão: 1, máx: 50)

*Obrigatório fornecer url OU urls

Exemplo 1: Monitoramento de Preços de Produto

Objetivo: Extrair preço de produto de e-commerce e alertar se mudou

JSON para Importar

{
  "name": "Monitor de Preço de Produto",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Extrair Produto",
        "parameters": {
          "operation": "scrape",
          "url": "https://www.amazon.com.br/dp/B08N5WRWNW",
          "mode": "template",
          "template": "ecommerce",
          "delay": 2000,
          "respectRobots": true,
          "userAgent": "LuminaBot/1.0 (Product Monitoring)"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Salvar Dados",
        "parameters": {
          "variableName": "produto_info",
          "value": "{{scraper_1.data.data[0].text}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Enviar Alerta",
        "parameters": {
          "message": "Produto monitorado:\n{{produto_info}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "variable_1" },
    { "source": "variable_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Produto monitorado:
{
  "title": "Echo Dot (4ª Geração)",
  "price": "R$ 349,00",
  "description": "Smart speaker com Alexa...",
  "availability": "Em estoque",
  "rating": "4.8 estrelas"
}

Exemplo 2: Agregação de Notícias

Objetivo: Coletar últimas notícias de múltiplos portais

JSON para Importar

{
  "name": "Agregador de Notícias Tech",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Coletar Notícias",
        "parameters": {
          "operation": "scrape",
          "urls": [
            "https://www.tecmundo.com.br",
            "https://canaltech.com.br",
            "https://olhardigital.com.br"
          ],
          "mode": "template",
          "template": "news",
          "delay": 3000,
          "extractMeta": true,
          "outputFormat": "json"
        }
      }
    },
    {
      "id": "loop_1",
      "type": "loop",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Processar Cada Site",
        "parameters": {
          "items": "{{scraper_1.data.data}}",
          "itemVariable": "site_data"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Resumir Notícia",
        "parameters": {
          "message": "Site: {{site_data.url}}\nTítulo: {{site_data.title}}\nAutor: {{JSON.parse(site_data.text).author}}\nData: {{JSON.parse(site_data.text).date}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 900, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "loop_1" },
    { "source": "loop_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Site: https://www.tecmundo.com.br
Título: Nova IA da OpenAI supera GPT-4
Autor: João Silva
Data: 13/10/2025

Sistema: Site: https://canaltech.com.br
Título: Apple anuncia iPhone 16
Autor: Maria Santos
Data: 13/10/2025

Sistema: Site: https://olhardigital.com.br
Título: SpaceX completa 100º lançamento
Autor: Pedro Costa
Data: 13/10/2025

Exemplo 3: Extração de Dados de Contato

Objetivo: Extrair informações de contato de páginas "Sobre" de empresas

JSON para Importar

{
  "name": "Extrator de Dados de Contato",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "input_1",
      "type": "input",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "URL da Empresa",
        "parameters": {
          "message": "Digite a URL da página 'Sobre' ou 'Contato' da empresa:",
          "variableName": "company_url"
        }
      }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Extrair Dados",
        "parameters": {
          "operation": "scrape",
          "url": "{{company_url}}",
          "mode": "auto",
          "extractText": true,
          "extractLinks": true,
          "extractMeta": true,
          "cleanText": true,
          "normalizeWhitespace": true
        }
      }
    },
    {
      "id": "ai_1",
      "type": "ai",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Extrair Contatos com IA",
        "parameters": {
          "prompt": "Extraia do seguinte texto: email, telefone, endereço, horário de atendimento. Retorne em formato JSON.\n\nTexto:\n{{scraper_1.data.data[0].text}}",
          "model": "gpt-4",
          "variableName": "contact_info"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Mostrar Contatos",
        "parameters": {
          "message": "Informações extraídas:\n{{contact_info}}\n\nMetadados do site:\nTítulo: {{scraper_1.data.data[0].title}}\nIdioma: {{scraper_1.data.data[0].metadata.lang}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1100, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "ai_1" },
    { "source": "ai_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Digite a URL da página 'Sobre' ou 'Contato' da empresa:
Usuário: https://example-company.com/contact
Sistema: Informações extraídas:
{
  "email": "contato@example-company.com",
  "telefone": "+55 11 1234-5678",
  "endereço": "Av. Paulista, 1000 - São Paulo, SP",
  "horário": "Segunda a Sexta, 9h às 18h"
}

Metadados do site:
Título: Contato - Example Company
Idioma: pt-BR

Resposta do Node

{
  "success": true,
  "data": {
    "totalPages": 1,
    "successfulPages": 1,
    "errors": [],
    "totalProcessingTime": 2456,
    "data": [
      {
        "url": "https://example.com",
        "title": "Example Domain",
        "text": "This domain is for use in illustrative examples...",
        "links": [
          "https://www.iana.org/domains/example"
        ],
        "images": [
          "https://example.com/logo.png"
        ],
        "metadata": {
          "og:title": "Example Domain",
          "description": "Example website",
          "lang": "en",
          "canonical": "https://example.com"
        },
        "timestamp": "2025-10-13T12:30:00.000Z",
        "processingTime": 2456
      }
    ]
  },
  "executionTime": 2458,
  "logs": [
    "Scraped 1 URLs",
    "Total items found: 1",
    "Processing time: 2458ms"
  ]
}

Boas Práticas

SIM:

  • Use delay de pelo menos 1-2 segundos entre requisições
  • Ative respectRobots: true para scraping ético
  • Configure userAgent identificando seu bot
  • Use cleanText: true para melhor qualidade de dados
  • Ative ignoreErrors: true ao processar múltiplas URLs
  • Combine com node AI para estruturar dados extraídos
  • Use extractMeta: true para informações de SEO
  • Limite maxPages para evitar sobrecarga

NÃO:

  • Fazer scraping sem delays (causa bloqueios)
  • Ignorar robots.txt de sites que o proíbem
  • Usar para sites com APIs oficiais disponíveis
  • Fazer scraping massivo sem considerar impacto no servidor
  • Extrair dados pessoais sem consentimento (LGPD)
  • Usar User-Agent falso de browsers reais
  • Tentar burlar CAPTCHAs ou sistemas anti-bot

Dicas

💡 Dica 1: Combine Web Scraper com node SCHEDULE para criar monitores automáticos que checam preços, vagas de emprego ou notícias periodicamente.

💡 Dica 2: Use o template "ecommerce" para extrair automaticamente preço, título, descrição e avaliação de produtos sem configurar seletores CSS.

💡 Dica 3: Ative extractSchemaOrg: true para extrair dados estruturados (JSON-LD) que sites bem construídos incluem. Retorna informações já formatadas.

💡 Dica 4: Para sites complexos, use mode: "manual" com seletores CSS específicos. Inspecione o HTML no navegador (F12) para encontrar os seletores corretos.

💡 Dica 5: Ao processar múltiplas URLs, use ignoreErrors: true e verifique o array errors no resultado para saber quais URLs falharam.

💡 Dica 6: Combine com node CONDITION para criar alertas: "se o preço for menor que R$ 500, envie mensagem no WhatsApp".

💡 Dica 7: Use outputFormat: "csv" quando precisar exportar dados para Excel ou planilhas do Google Sheets.

Próximo Node

WEB_SCRAPER (EXTRACT) - Extração com seletores CSS customizados → WEB_SCRAPER (CRAWL) - Navegação automática por múltiplas páginas → AI - Processar dados extraídos com inteligência artificial → CONDITION - Criar regras baseadas nos dados extraídos