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:
- Documentar visualmente: Criar evidências de como uma página estava em determinado momento
- Monitorar mudanças visuais: Detectar alterações de layout, design ou conteúdo
- Arquivar estados: Guardar versões visuais de páginas importantes
- Verificar renderização: Checar se uma página está renderizando corretamente
- Gerar relatórios visuais: Incluir screenshots em relatórios de análise web
Como funciona internamente?
Quando o WEB_SCRAPER (SCREENSHOT) é executado, o sistema:
- Valida configuração: Verifica se a URL e parâmetros de screenshot são válidos
- Faz requisição HTTP: Carrega o HTML da página
- Renderiza conteúdo: Processa o HTML e CSS para criar representação visual
- Captura imagem: Tira screenshot da página renderizada
- Converte para base64: Codifica a imagem em formato base64 para transporte
- Aplica configurações: Respeita viewport, formato (PNG/JPG) e qualidade
- Anexa metadados: Adiciona URL, timestamp e dimensões
- 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
- Compliance e auditoria: "Capturar evidência de como o site estava no momento da visita"
- Monitoramento de concorrentes: "Tirar screenshot semanal da homepage dos concorrentes"
- Testes visuais: "Verificar se a página está renderizando corretamente após deploy"
- Documentação de bugs: "Capturar tela com erro para reportar ao suporte"
- 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