Pular para conteúdo

WEB_SCRAPER (SCREENSHOT) - Captura Visual de Páginas Web

O que é este Node?

O WEB_SCRAPER (SCREENSHOT) é o node responsável por capturar screenshots (capturas de tela) de websites, convertendo páginas web em imagens. Perfeito para documentação, arquivamento visual, monitoramento de mudanças de layout e verificação de renderização.

Por que este Node existe?

Às vezes você precisa do visual completo de uma página, não apenas do texto. O WEB_SCRAPER (SCREENSHOT) existe para:

  1. Documentar visualmente: Criar evidências de como uma página estava em determinado momento
  2. Monitorar mudanças visuais: Detectar alterações de layout, design ou conteúdo
  3. Arquivar estados: Guardar versões visuais de páginas importantes
  4. Verificar renderização: Checar se uma página está renderizando corretamente
  5. Gerar relatórios visuais: Incluir screenshots em relatórios de análise web

Como funciona internamente?

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

  1. Valida configuração: Verifica se a URL e parâmetros de screenshot são válidos
  2. Faz requisição HTTP: Carrega o HTML da página
  3. Renderiza conteúdo: Processa o HTML e CSS para criar representação visual
  4. Captura imagem: Tira screenshot da página renderizada
  5. Converte para base64: Codifica a imagem em formato base64 para transporte
  6. Aplica configurações: Respeita viewport, formato (PNG/JPG) e qualidade
  7. Anexa metadados: Adiciona URL, timestamp e dimensões
  8. Retorna resultado: Devolve screenshot em base64 junto com metadados

Observação: A implementação completa com browser real (Puppeteer/Playwright) seria necessária para screenshots de qualidade. O código atual suporta a estrutura mas requer integração com ferramenta de browser headless.

Código interno (web-scraper-executor.service.ts:69-72, 366-370):

// Configuration for screenshot
interface WebScraperNodeData extends FlowNodeData {
  // Advanced Features
  screenshot?: boolean;
  saveHtml?: boolean;
  detectLanguage?: boolean;
  extractSchemaOrg?: boolean;
}

// In extraction process
if (config.screenshot) {
  result.screenshot = this.takeScreenshot($, url); // base64 encoded
}

if (config.saveHtml) {
  result.html = $.html();
}

Quando você DEVE usar este Node?

Use WEB_SCRAPER (SCREENSHOT) sempre que precisar de representação visual de páginas:

Casos de uso

  1. Compliance e auditoria: "Capturar evidência de como o site estava no momento da visita"
  2. Monitoramento de concorrentes: "Tirar screenshot semanal da homepage dos concorrentes"
  3. Testes visuais: "Verificar se a página está renderizando corretamente após deploy"
  4. Documentação de bugs: "Capturar tela com erro para reportar ao suporte"
  5. Arquivamento histórico: "Guardar versão visual de páginas importantes periodicamente"

Quando NÃO usar WEB_SCRAPER (SCREENSHOT)

  • Conteúdo JavaScript pesado: Screenshots básicos não executam JavaScript complexo
  • Páginas com login: Não consegue passar por autenticação sem browser real
  • SPAs modernas: Single Page Apps precisam de browser headless completo
  • Vídeos e animações: Screenshot captura momento estático, não animações

Parâmetros Detalhados

screenshot (boolean, obrigatório)

O que é: Ativa a captura de screenshot da página. Quando true, além dos dados textuais, uma imagem da página será gerada.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Screenshot - Captura 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": "Capturar Tela",
        "parameters": {
          "operation": "screenshot",
          "url": "https://example.com",
          "screenshot": true,
          "saveHtml": true,
          "includeMetadata": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Screenshot capturado!\nURL: {{scraper_1.data.data[0].url}}\nTamanho da imagem: {{scraper_1.data.data[0].screenshot.length}} bytes (base64)"
        }
      }
    },
    {
      "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: Captura screenshot de uma página e retorna em base64.

saveHtml (boolean, opcional)

O que é: Além do screenshot, salva também o HTML completo da página. Útil para ter ambos: visual e código fonte.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Screenshot - Com HTML",
  "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": "Screenshot + HTML",
        "parameters": {
          "operation": "screenshot",
          "url": "https://www.google.com",
          "screenshot": true,
          "saveHtml": true,
          "extractMeta": true
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Salvar Dados",
        "parameters": {
          "variableName": "page_backup",
          "value": {
            "url": "{{scraper_1.data.data[0].url}}",
            "screenshot": "{{scraper_1.data.data[0].screenshot}}",
            "html": "{{scraper_1.data.data[0].html}}",
            "timestamp": "{{scraper_1.data.data[0].timestamp}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Resumo",
        "parameters": {
          "message": "Backup completo criado!\nHTML: {{page_backup.html.length}} caracteres\nScreenshot: {{page_backup.screenshot.length}} bytes\nData: {{page_backup.timestamp}}"
        }
      }
    },
    {
      "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" }
  ]
}

Teste: Captura screenshot E HTML completo da página.

includeMetadata (boolean, opcional)

O que é: Inclui metadados completos sobre a captura: título, meta tags, dimensões, timestamp, tempo de processamento.

Padrão: false

Flow completo para testar:

{
  "name": "Teste Screenshot - Com 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": "Screenshot Detalhado",
        "parameters": {
          "operation": "screenshot",
          "url": "https://github.com",
          "screenshot": true,
          "includeMetadata": true,
          "extractMeta": true
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Detalhes",
        "parameters": {
          "message": "Screenshot capturado de: {{scraper_1.data.data[0].title}}\n\nMetadados:\nOG Title: {{scraper_1.data.data[0].metadata['og:title']}}\nOG Image: {{scraper_1.data.data[0].metadata['og:image']}}\nIdioma: {{scraper_1.data.data[0].metadata.lang}}\nProcessamento: {{scraper_1.data.data[0].processingTime}}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: Captura screenshot com todos os metadados da página.

Parâmetros

Campo Tipo Obrigatório Descrição
operation string Sim Tipo de operação ("screenshot")
url string Sim URL da página a capturar
screenshot boolean Sim Deve ser true
saveHtml boolean Não Salvar HTML junto (padrão: false)
includeMetadata boolean Não Incluir metadados completos (padrão: false)
extractMeta boolean Não Extrair meta tags
timeout number Não Timeout da captura em ms
userAgent string Não User-Agent customizado

Exemplo 1: Monitoramento Visual de Concorrente

Objetivo: Capturar screenshot semanal da homepage de concorrente

JSON para Importar

{
  "name": "Monitor Visual de Concorrente",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "schedule_1",
      "type": "schedule",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Toda Segunda 9h",
        "parameters": {
          "cron": "0 9 * * 1",
          "timezone": "America/Sao_Paulo"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Sites Concorrentes",
        "parameters": {
          "variableName": "concorrentes",
          "value": [
            "https://competitor1.com",
            "https://competitor2.com",
            "https://competitor3.com"
          ]
        }
      }
    },
    {
      "id": "loop_1",
      "type": "loop",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Cada Concorrente",
        "parameters": {
          "items": "{{concorrentes}}",
          "itemVariable": "site_url"
        }
      }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Capturar Screenshot",
        "parameters": {
          "operation": "screenshot",
          "url": "{{site_url}}",
          "screenshot": true,
          "saveHtml": true,
          "includeMetadata": true,
          "delay": 3000
        }
      }
    },
    {
      "id": "s3_1",
      "type": "aws_s3",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Salvar no S3",
        "parameters": {
          "operation": "upload",
          "bucket": "competitor-screenshots",
          "key": "{{site_url.replace('https://', '').replace('/', '_')}}_{{new Date().toISOString().split('T')[0]}}.png",
          "body": "{{Buffer.from(scraper_1.data.data[0].screenshot, 'base64')}}",
          "contentType": "image/png"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "Screenshot salvo: {{site_url}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "schedule_1" },
    { "source": "schedule_1", "target": "variable_1" },
    { "source": "variable_1", "target": "loop_1" },
    { "source": "loop_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "s3_1" },
    { "source": "s3_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Screenshot salvo: https://competitor1.com
Sistema: Screenshot salvo: https://competitor2.com
Sistema: Screenshot salvo: https://competitor3.com

Exemplo 2: Documentação Visual de Produto

Objetivo: Capturar screenshots de páginas de produto para documentação

JSON para Importar

{
  "name": "Documentador Visual de Produtos",
  "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": "URLs dos Produtos",
        "parameters": {
          "message": "Cole as URLs dos produtos (separadas por vírgula):",
          "variableName": "urls_input"
        }
      }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Processar URLs",
        "parameters": {
          "variableName": "product_urls",
          "value": "{{urls_input.split(',').map(u => u.trim())}}"
        }
      }
    },
    {
      "id": "loop_1",
      "type": "loop",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Cada Produto",
        "parameters": {
          "items": "{{product_urls}}",
          "itemVariable": "product_url"
        }
      }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Screenshot + Dados",
        "parameters": {
          "operation": "screenshot",
          "url": "{{product_url}}",
          "screenshot": true,
          "saveHtml": true,
          "extractMeta": true,
          "includeMetadata": true,
          "mode": "template",
          "template": "ecommerce"
        }
      }
    },
    {
      "id": "variable_2",
      "type": "variable",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Criar Documento",
        "parameters": {
          "variableName": "product_doc",
          "value": {
            "url": "{{product_url}}",
            "screenshot": "{{scraper_1.data.data[0].screenshot}}",
            "produto": "{{JSON.parse(scraper_1.data.data[0].text)}}",
            "metadata": "{{scraper_1.data.data[0].metadata}}",
            "timestamp": "{{scraper_1.data.data[0].timestamp}}"
          }
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Resumo",
        "parameters": {
          "message": "Documentado: {{product_doc.produto.title}}\nPreço: {{product_doc.produto.price}}\nScreenshot: OK"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "input_1" },
    { "source": "input_1", "target": "variable_1" },
    { "source": "variable_1", "target": "loop_1" },
    { "source": "loop_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "variable_2" },
    { "source": "variable_2", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: Cole as URLs dos produtos (separadas por vírgula):
Usuário: https://store.com/produto1, https://store.com/produto2, https://store.com/produto3
Sistema: Documentado: Notebook Dell Inspiron 15
Preço: R$ 3.499,00
Screenshot: OK

Sistema: Documentado: Mouse Logitech MX Master 3
Preço: R$ 449,00
Screenshot: OK

Sistema: Documentado: Teclado Mecânico Keychron K2
Preço: R$ 699,00
Screenshot: OK

Exemplo 3: Evidência de Compliance

Objetivo: Capturar evidência visual de termos de uso/privacidade

JSON para Importar

{
  "name": "Captura de Evidência Legal",
  "nodes": [
    {
      "id": "start_1",
      "type": "start",
      "position": { "x": 100, "y": 100 },
      "data": { "label": "Início" }
    },
    {
      "id": "variable_1",
      "type": "variable",
      "position": { "x": 300, "y": 100 },
      "data": {
        "label": "Páginas Legais",
        "parameters": {
          "variableName": "legal_pages",
          "value": [
            {
              "tipo": "Termos de Uso",
              "url": "https://company.com/terms"
            },
            {
              "tipo": "Política de Privacidade",
              "url": "https://company.com/privacy"
            },
            {
              "tipo": "Cookies",
              "url": "https://company.com/cookies"
            }
          ]
        }
      }
    },
    {
      "id": "loop_1",
      "type": "loop",
      "position": { "x": 500, "y": 100 },
      "data": {
        "label": "Cada Página",
        "parameters": {
          "items": "{{legal_pages}}",
          "itemVariable": "page"
        }
      }
    },
    {
      "id": "scraper_1",
      "type": "web_scraper",
      "position": { "x": 700, "y": 100 },
      "data": {
        "label": "Capturar Evidência",
        "parameters": {
          "operation": "screenshot",
          "url": "{{page.url}}",
          "screenshot": true,
          "saveHtml": true,
          "extractText": true,
          "includeMetadata": true,
          "extractMeta": true
        }
      }
    },
    {
      "id": "variable_2",
      "type": "variable",
      "position": { "x": 900, "y": 100 },
      "data": {
        "label": "Criar Evidência",
        "parameters": {
          "variableName": "evidencia",
          "value": {
            "tipo": "{{page.tipo}}",
            "url": "{{page.url}}",
            "data_captura": "{{new Date().toISOString()}}",
            "screenshot_base64": "{{scraper_1.data.data[0].screenshot}}",
            "html_completo": "{{scraper_1.data.data[0].html}}",
            "texto_extraido": "{{scraper_1.data.data[0].text}}",
            "hash_sha256": "{{crypto.createHash('sha256').update(scraper_1.data.data[0].html).digest('hex')}}"
          }
        }
      }
    },
    {
      "id": "database_1",
      "type": "database",
      "position": { "x": 1100, "y": 100 },
      "data": {
        "label": "Salvar Evidência",
        "parameters": {
          "operation": "insert",
          "table": "legal_evidence",
          "data": "{{evidencia}}"
        }
      }
    },
    {
      "id": "message_1",
      "type": "message",
      "position": { "x": 1300, "y": 100 },
      "data": {
        "label": "Confirmar",
        "parameters": {
          "message": "✅ Evidência salva: {{page.tipo}}\nHash: {{evidencia.hash_sha256.substring(0, 16)}}...\nData: {{evidencia.data_captura}}"
        }
      }
    },
    {
      "id": "end_1",
      "type": "end",
      "position": { "x": 1500, "y": 100 },
      "data": { "label": "Fim" }
    }
  ],
  "edges": [
    { "source": "start_1", "target": "variable_1" },
    { "source": "variable_1", "target": "loop_1" },
    { "source": "loop_1", "target": "scraper_1" },
    { "source": "scraper_1", "target": "variable_2" },
    { "source": "variable_2", "target": "database_1" },
    { "source": "database_1", "target": "message_1" },
    { "source": "message_1", "target": "end_1" }
  ]
}

Saída esperada:

Sistema: ✅ Evidência salva: Termos de Uso
Hash: a8b9c0d1e2f3g4h5...
Data: 2025-10-13T12:30:00.000Z

Sistema: ✅ Evidência salva: Política de Privacidade
Hash: b9c0d1e2f3g4h5i6...
Data: 2025-10-13T12:30:05.000Z

Sistema: ✅ Evidência salva: Cookies
Hash: c0d1e2f3g4h5i6j7...
Data: 2025-10-13T12:30:10.000Z

Resposta do Node

{
  "success": true,
  "data": {
    "totalPages": 1,
    "successfulPages": 1,
    "errors": [],
    "totalProcessingTime": 3456,
    "data": [
      {
        "url": "https://example.com",
        "title": "Example Domain",
        "screenshot": "iVBORw0KGgoAAAANSUhEUgAAA...(base64 muito longo)...==",
        "html": "<!DOCTYPE html><html>...</html>",
        "metadata": {
          "og:title": "Example Domain",
          "description": "Example website",
          "lang": "en"
        },
        "timestamp": "2025-10-13T12:30:00.000Z",
        "processingTime": 3456
      }
    ]
  },
  "executionTime": 3458
}

Boas Práticas

SIM:

  • Combine screenshot com saveHtml para ter backup completo
  • Use includeMetadata para rastreabilidade
  • Salve screenshots em S3/storage ao invés de banco de dados
  • Adicione hash SHA256 para verificar integridade
  • Use timestamps para organizar capturas históricas
  • Configure timeout adequado (páginas pesadas demoram mais)

NÃO:

  • Salvar screenshots grandes diretamente no banco
  • Capturar screenshots de páginas com login sem autorização
  • Esquecer de comprimir imagens antes de armazenar
  • Fazer capturas em alta frequência (usa muitos recursos)
  • Ignorar que screenshots básicos não executam JavaScript

Dicas

💡 Dica 1: Para screenshots de qualidade de produção, considere integrar Puppeteer ou Playwright - eles executam JavaScript completo.

💡 Dica 2: Screenshots em base64 são grandes. Converta para Buffer e salve em storage antes de inserir no banco de dados.

💡 Dica 3: Combine com SCHEDULE para criar sistema de monitoramento visual automático.

💡 Dica 4: Use hash SHA256 do HTML para detectar mudanças na página sem comparar screenshots pixel por pixel.

💡 Dica 5: Para evidências legais, salve screenshot + HTML + hash + timestamp para ter prova irrefutável.

💡 Dica 6: Considere usar serviços especializados (ScreenshotAPI, ApiFlash) para screenshots de produção com JavaScript.

Próximo Node

WEB_SCRAPER (MONITOR) - Monitorar mudanças em páginas → AWS_S3 - Armazenar screenshots → SCHEDULE - Agendar capturas periódicas