SEO Técnico & Engenharia de Dados

Análise de Log Files para SEO com DuckDB e Cloudflare Logpush: Como Mapear o Comportamento Real do Googlebot em Grande Escala

SEO Técnico & Engenharia de Dados
Análise de Logs com DuckDB
Cayo Galotti Especialista Google

O relatório de estatísticas de rastreamento no Google Search Console fornece uma visão resumida da atividade do Googlebot, mas esconde detalhes determinantes. A interface oficial agrupa dados em médias gerais, aplica amostragem em propriedades maiores e não permite correlacionar parâmetros de requisição linha a linha com a taxonomia do site. Em portais de e-commerce, publishers ou aplicações com centenas de milhares de URLs, essa falta de granularidade impede diagnósticos precisos sobre desperdício de crawl budget e falhas de indexação.

Para enxergar o comportamento real do buscador, a única fonte confiável são os registros de acesso brutos. Durante anos, processar esses dados significou instalar pilhas pesadas como Elasticsearch e Logstash, pagar faturas altas de processamento em data warehouses em nuvem ou lutar contra softwares de desktop que travam ao carregar arquivos de texto com mais de dois gigabytes.

A união do Cloudflare Logpush com o DuckDB elimina essa barreira. O Logpush descarrega automaticamente os registros da borda em buckets de armazenamento de baixo custo, enquanto o DuckDB executa consultas colunares ultrarrápidas diretamente nos arquivos compactados na sua própria máquina ou em rotinas de CI/CD. Esse pipeline entrega visibilidade total sobre cada hit do Googlebot com custo de infraestrutura próximo de zero.

O gargalo dos logs de servidor e a vantagem da captura na borda

Coletar logs diretamente no servidor de aplicação, seja em instâncias Nginx, Apache ou containers Node.js, traz falhas estruturais para o SEO técnico moderno. A principal limitação é a invisibilidade das requisições atendidas pela CDN. Quando uma CDN como a Cloudflare responde a uma requisição do Googlebot diretamente a partir do cache de borda, retornando status 200 com conteúdo em cache ou 304 Not Modified, esse evento nunca atinge o servidor de origem. O log do Nginx simplesmente não sabe que o Googlebot visitou aquela página.

Outro problema frequente é a fragmentação. Em ambientes distribuídos com balanceadores de carga e múltiplos pods, consolidar arquivos de log dispersos exige agentes de coleta locais que consomem CPU e disco da aplicação. Se o tráfego do bot aumentar repentinamente, a gravação de logs em disco na origem pode concorrer diretamente com as requisições dos usuários reais.

Capturar os dados na borda via Cloudflare Logpush resolve esses pontos. O evento de log é gerado no momento exato em que a requisição atinge a rede perimetral, independentemente de a resposta ser servida pelo cache ou encaminhada à origem.

Critério de ComparaçãoLogs do Servidor de Origem Nginx ou ApacheLogs na Borda com Cloudflare Logpush
Requisições em cacheInvisíveis quando a CDN responde com hit de cache100% capturadas, incluindo status 304 e hits de borda
Custo de I/O na máquinaAlto consumo de disco e concorrência com a aplicaçãoZero impacto na CPU ou no disco do servidor web
Fragmentação de arquivosLogs divididos entre múltiplos servidores e podsArquivos centralizados e agrupados por intervalo de tempo
Latência de disponibilidadeExige pipelines de ingestão ou rotinas de rsyncEnvio em lotes a cada poucos minutos para storage
Formatos nativosLinhas de texto não estruturado tipo Combined LogJSON estruturado ou Apache Parquet pronto para análise

Em arquiteturas que utilizam configurações de proxy reverso e múltiplos serviços, a captura perimetral garante um registro unificado de todos os subdiretórios, impedindo que partes do site fiquem sem monitoramento.

Arquitetura do pipeline: Cloudflare Logpush e storage de objetos

O Cloudflare Logpush é um recurso que exporta dados de logs de tráfego HTTP em tempo real ou em lotes curtos, geralmente a cada minuto ou cinco minutos, diretamente para serviços de armazenamento de objetos. Entre os destinos suportados estão o Cloudflare R2, AWS S3, Google Cloud Storage e Azure Blob Storage.

Para manter a operação eficiente e financeiramente sustentável, o Cloudflare R2 é uma opção recomendada por não cobrar taxas de tráfego de saída, conhecidas como egress fees. Os logs são empacotados em lotes comprimidos em gzip ou Parquet, organizados em diretórios com base no timestamp do evento.

Seleção de campos relevantes para SEO

Um arquivo de log bruto de CDN contém dezenas de propriedades relacionadas a segurança, TLS e cabeçalhos de rede. Para o trabalho de SEO, manter todos os campos infla o tamanho do arquivo sem utilidade analítica. Ao configurar o job de Logpush na Cloudflare, selecione apenas as colunas que respondem às perguntas de rastreamento:

  • ClientIP: endereço IP de origem que originou a requisição.
  • ClientRequestHost: hostname requisitado, fundamental para filtrar domínios e subdomínios.
  • ClientRequestMethod: método HTTP, como GET ou HEAD.
  • ClientRequestPath: caminho da URL após o domínio.
  • ClientRequestURI: caminho completo da URL incluindo query strings e parâmetros.
  • ClientRequestUserAgent: string completa do navegador ou bot informada na requisição.
  • EdgeResponseStatus: código de status retornado pela borda ao cliente.
  • EdgeResponseBytes: tamanho da resposta entregue ao crawler em bytes.
  • EdgeStartTimestamp: timestamp exato em nanossegundos do início da conexão.
  • CacheCacheStatus: estado do cache na Cloudflare, como hit, miss, bypass ou expired.
  • OriginResponseTime: tempo em milissegundos que a origem levou para responder quando houve miss de cache.

Filtragem antecipada de bots no Logpush

Você pode configurar o filtro do Logpush diretamente no painel da Cloudflare ou via API para exportar apenas requisições que contenham termos específicos no User-Agent. Isso reduz o volume de dados gravados no storage em mais de 90%, já que o tráfego de usuários comuns é descartado antes do envio.

Um exemplo de regra de filtro no Logpush para isolar rastreadores de busca:

{
  "filter": "ClientRequestUserAgent contains 'Googlebot' or ClientRequestUserAgent contains 'Google-InspectionTool' or ClientRequestUserAgent contains 'Storebot-Google'"
}

Essa filtragem perimetral gera arquivos compactados que ocupam poucos megabytes por dia, mesmo em sites que recebem centenas de milhares de hits de rastreadores.

Por que DuckDB é a ferramenta ideal para análise de logs

O DuckDB é um sistema de gerenciamento de banco de dados relacional e analítico, focado em processamento colunar e projetado para rodar embutido no mesmo processo da aplicação. Diferente de bancos transacionais como MySQL ou PostgreSQL, que armazenam registros linha por linha, o DuckDB organiza os dados em colunas. Quando uma consulta calcula a contagem de códigos de resposta ou agrupa URLs por diretório, o motor lê apenas as colunas envolvidas na agregação, ignorando o restante dos dados em disco.

Ao contrário de soluções como Snowflake ou Google BigQuery, que cobram por volume de dados escaneados e exigem configurações complexas de infraestrutura, o DuckDB roda localmente em um único arquivo binário sem dependências externas. Ele pode ser executado pelo terminal ou integrado diretamente a scripts em Python. Embora conectar a API do Search Console ao BigQuery seja uma solução consagrada para relatórios consolidados de posições e cliques, a auditoria de logs brutos no dia a dia é muito mais ágil e barata quando feita com DuckDB.

Outra vantagem decisiva é o suporte nativo a leitura de arquivos remotos e compactados. Por meio da extensão httpfs, o DuckDB lê arquivos JSON com gzip ou arquivos Parquet diretamente do bucket R2 ou S3 usando requisições HTTP Range. Ele baixa apenas os bytes estritamente necessários para responder à consulta SQL, economizando banda e memória RAM.

Configuração do ambiente e leitura de dados

Para iniciar a exploração, você precisa apenas do binário do DuckDB ou da biblioteca oficial para Python instalada no seu ambiente.

Se você estiver utilizando Python:

pip install duckdb

Com o DuckDB instalado, a conexão com o bucket onde os logs do Cloudflare Logpush estão armazenados é configurada em poucos comandos SQL. O exemplo a seguir demonstra como carregar a extensão de arquivos remotos e apontar para um bucket compatível com a API S3, como o Cloudflare R2:

INSTALL httpfs;
LOAD httpfs;

SET s3_endpoint = 'seu_account_id.r2.cloudflarestorage.com';
SET s3_access_key_id = 'sua_access_key';
SET s3_secret_access_key = 'sua_secret_key';
SET s3_url_style = 'path';

Se você preferir baixar os arquivos de log para um disco local ou SSD externo para trabalhar offline, a sintaxe de consulta permanece idêntica. O DuckDB interpreta glob patterns para ler múltiplos arquivos de forma transparente:

SELECT 
    COUNT(*) AS total_requisicoes,
    MIN(EdgeStartTimestamp) AS primeira_requisicao,
    MAX(EdgeStartTimestamp) AS ultima_requisicao
FROM read_ndjson_objects('logs/2026/09/*/*.json.gz');

O comando acima analisa todos os arquivos compactados de um mês inteiro sem precisar descompactá-los previamente no disco, processando milhões de registros em poucos segundos.

Validação de autenticidade do Googlebot via SQL

Qualquer pessoa pode enviar uma requisição HTTP alterando o cabeçalho User-Agent para Googlebot. Scrapers concorrentes, ferramentas de análise agressivas e bots de spam utilizam essa técnica constantemente para evitar bloqueios de firewall. Incluir essas requisições falsificadas na sua análise de logs distorce completamente os cálculos de crawl budget e mascara o real comportamento do Google.

O método oficial recomendado pelo Google para validar a autenticidade de um crawler exige duas etapas: verificar se o IP reverso aponta para domínios terminados em googlebot.com ou google.com, e confirmar se a resolução desse domínio retorna o mesmo IP original, processo conhecido como Forward Confirmed Reverse DNS.

Em análises de grande volume, executar consultas DNS individuais para cada requisição é inviável pela alta latência. A estratégia mais eficiente consiste em cruzar os IPs registrados no log com a lista pública de intervalos de rede CIDR que o Google disponibiliza oficialmente.

O Google publica duas listas em formato JSON com seus blocos de IP atualizados: uma para bots gerais e outra específica para o Googlebot. Podemos carregar esses blocos no DuckDB e criar uma classificação precisa dos acessos.

CREATE OR REPLACE TABLE google_cidrs AS 
SELECT 
    unnest(prefixes)->>'$.ipv4Prefix' AS cidr_ipv4,
    unnest(prefixes)->>'$.ipv6Prefix' AS cidr_ipv6
FROM read_json_auto('https://developers.google.com/search/apis/ipranges/googlebot.json');

CREATE OR REPLACE VIEW logs_validados AS
SELECT 
    l.EdgeStartTimestamp,
    l.ClientIP,
    l.ClientRequestHost,
    l.ClientRequestPath,
    l.ClientRequestURI,
    l.ClientRequestUserAgent,
    l.EdgeResponseStatus,
    l.EdgeResponseBytes,
    l.CacheCacheStatus,
    CASE 
        WHEN EXISTS (
            SELECT 1 FROM google_cidrs c 
            WHERE (c.cidr_ipv4 IS NOT NULL AND l.ClientIP::VARCHAR = c.cidr_ipv4)
               OR (c.cidr_ipv6 IS NOT NULL AND l.ClientIP::VARCHAR = c.cidr_ipv6)
        ) THEN 'Googlebot Legítimo'
        ELSE 'Bot Falsificado ou Não Verificado'
    END AS status_autenticidade
FROM read_ndjson_objects('logs/2026/09/*.json.gz') l
WHERE l.ClientRequestUserAgent LIKE '%Googlebot%';

Ao isolar apenas as linhas marcadas como legítimas, eliminamos o ruído de raspadores externos e focamos nos dados reais de indexação.

Consultas analíticas essenciais para mapear o rastreamento

Com os dados higienizados e indexados pelo DuckDB, podemos extrair respostas diretas para os problemas mais comuns de indexação e performance.

1. Proporção entre Googlebot Smartphone e Googlebot Desktop

O Google opera prioritariamente via Mobile-First Indexing, utilizando o rastreador de smartphone para coletar a versão principal da página e o rastreador de desktop para checagens secundárias. Um desvio dessa proporção pode indicar problemas de renderização ou inconsistência de links entre versões.

SELECT 
    CASE 
        WHEN ClientRequestUserAgent LIKE '%Android%' THEN 'Googlebot Smartphone'
        WHEN ClientRequestUserAgent LIKE '%iPhone%' THEN 'Googlebot Smartphone'
        ELSE 'Googlebot Desktop'
    END AS tipo_crawler,
    COUNT(*) AS total_hits,
    ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentual_participacao
FROM logs_validados
WHERE status_autenticidade = 'Googlebot Legítimo'
GROUP BY 1
ORDER BY total_hits DESC;

Em sites saudáveis, o Googlebot Smartphone costuma responder por 75% a 90% das requisições totais. Se o crawler de desktop estiver dominando o tráfego, investigue se o site ainda possui páginas separadas em subdomínio móvel ou se faltam diretivas de viewport responsivo.

2. Distribuição de códigos de status HTTP por seção do site

Requisições que resultam em códigos 404, 500 ou cadeias longas de 301 consomem cota de rastreamento sem gerar valor orgânico. A consulta abaixo agrupa as URLs pelo primeiro diretório da estrutura e calcula a distribuição de status:

SELECT 
    regexp_extract(ClientRequestPath, '^/([^/]+)/?', 1) AS secao_site,
    COUNT(*) AS total_requisicoes,
    COUNT(*) FILTER (WHERE EdgeResponseStatus = 200) AS status_200,
    COUNT(*) FILTER (WHERE EdgeResponseStatus BETWEEN 300 AND 399) AS redirecionamentos_3xx,
    COUNT(*) FILTER (WHERE EdgeResponseStatus BETWEEN 400 AND 499) AS erros_cliente_4xx,
    COUNT(*) FILTER (WHERE EdgeResponseStatus >= 500) AS erros_servidor_5xx,
    ROUND(COUNT(*) FILTER (WHERE EdgeResponseStatus >= 400) * 100.0 / COUNT(*), 2) AS taxa_erro_percentual
FROM logs_validados
WHERE status_autenticidade = 'Googlebot Legítimo'
GROUP BY 1
HAVING total_requisicoes > 100
ORDER BY total_requisicoes DESC;

Se uma categoria de produtos ou seção de blog apresentar taxa de erro 4xx acima de 5%, o bot está gastando recursos em links quebrados que deveriam ter sido removidos do código-fonte ou atualizados. Em rotinas de engenharia de software, integrar testes de regressão no GitHub Actions ajuda a impedir que deploys em produção quebrem URLs canônicas e gerem picos súbitos de status 404.

3. Eficiência de cache da borda para o crawler

O Googlebot valoriza páginas que respondem com baixa latência. Se a CDN estiver servindo as requisições a partir do cache, o tempo de resposta percebido pelo robô cai de várias centenas de milissegundos para menos de cinquenta milissegundos.

SELECT 
    CacheCacheStatus AS status_cache_cloudflare,
    COUNT(*) AS volume_hits,
    ROUND(AVG(EdgeResponseBytes) / 1024, 2) AS tamanho_medio_kb,
    ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER (), 2) AS percentual_total
FROM logs_validados
WHERE status_autenticidade = 'Googlebot Legítimo'
GROUP BY 1
ORDER BY volume_hits DESC;

Taxas elevadas de status MISS ou BYPASS no tráfego do Googlebot revelam que a política de cache não está protegendo a aplicação. Isso acontece com frequência quando cabeçalhos Cache-Control configuram no-cache inadvertidamente ou quando cookies de sessão desnecessários são emitidos para bots de busca.

4. Identificação de desperdício em URLs com parâmetros

Sistemas de busca interna, filtros de catálogo e parâmetros de rastreamento de campanhas podem gerar combinações quase infinitas de URLs. Se o Googlebot começar a varrer essas páginas parametrizadas, o orçamento de rastreamento das páginas que realmente importam será sacrificado.

SELECT 
    regexp_extract(ClientRequestURI, '\?([^=]+)=', 1) AS parametro_principal,
    COUNT(*) AS total_hits,
    COUNT(DISTINCT ClientRequestURI) AS urls_distintas,
    ROUND(COUNT(*) * 1.0 / COUNT(DISTINCT ClientRequestURI), 2) AS media_hits_por_url
FROM logs_validados
WHERE status_autenticidade = 'Googlebot Legítimo'
  AND ClientRequestURI LIKE '%?%'
GROUP BY 1
ORDER BY total_hits DESC
LIMIT 20;

Identificar os parâmetros que mais atraem requisições inúteis fornece a base técnica para configurar regras de bloqueio no arquivo robots.txt ou implementar diretivas canônicas estritas antes que o índice seja poluído.

Cruzamento com sitemaps para descobrir páginas órfãs e zumbis

O diagnóstico mais valioso da análise de logs surge ao cruzar as URLs requisitadas pelo Googlebot com a lista de URLs declaradas nos sitemaps XML e na arquitetura de links internos.

Existem três cenários fundamentais nesse relacionamento:

  1. URLs no sitemap que o Googlebot nunca rastreia: indicam fraqueza na distribuição de autoridade interna ou páginas de baixa relevância percebida.
  2. URLs fora do sitemap que o Googlebot rastreia ativamente: frequentemente revelam páginas órfãs geradas por filtros legados, links internos esquecidos no rodapé ou rastreamento de URLs que deveriam estar bloqueadas.
  3. URLs com alto volume de rastreamento e alta conversão: o cenário ideal onde o esforço do bot coincide com as prioridades comerciais do negócio.

Podemos carregar o arquivo de sitemap diretamente no DuckDB a partir de um arquivo local ou URL, extrair as URLs canônicas e rodar uma junção completa contra os logs:

CREATE OR REPLACE TABLE sitemap_urls AS
SELECT 
    unnest(xpath('/sitemapindex/sitemap/loc/text() | /urlset/url/loc/text()', xml_content))::VARCHAR AS url_sitemap
FROM (
    SELECT read_text('sitemap.xml') AS xml_content
);

WITH logs_unicos AS (
    SELECT DISTINCT 
        'https://' || ClientRequestHost || ClientRequestPath AS url_log,
        COUNT(*) AS total_hits_googlebot,
        MAX(EdgeStartTimestamp) AS data_ultimo_hit
    FROM logs_validados
    WHERE status_autenticidade = 'Googlebot Legítimo'
      AND EdgeResponseStatus = 200
    GROUP BY 1
)
SELECT 
    COALESCE(s.url_sitemap, l.url_log) AS url_avaliada,
    CASE 
        WHEN s.url_sitemap IS NOT NULL AND l.url_log IS NOT NULL THEN 'Rastreada e no Sitemap'
        WHEN s.url_sitemap IS NOT NULL AND l.url_log IS NULL THEN 'Zumbi: No Sitemap mas Não Rastreada'
        WHEN s.url_sitemap IS NULL AND l.url_log IS NOT NULL THEN 'Órfã: Rastreada mas Fora do Sitemap'
    END AS classificacao_rastreamento,
    COALESCE(l.total_hits_googlebot, 0) AS hits_recebidos,
    l.data_ultimo_hit
FROM sitemap_urls s
FULL OUTER JOIN logs_unicos l ON s.url_sitemap = l.url_log
ORDER BY hits_recebidos DESC;

As páginas classificadas como zumbis devem ser investigadas imediatamente. Se uma URL estratégica não recebe requisições do Googlebot há semanas, a causa quase sempre está ligada à profundidade de clique na estrutura do site. Correlacionar essa lista com o cálculo de PageRank interno usando NetworkX comprova visualmente se essas páginas esquecidas estão isoladas da malha principal de links internos.

Automação da rotina com Parquet particionado

Manter arquivos JSON comprimidos no storage atende análises esporádicas, mas à medida que os meses acumulam, processar JSON consome mais CPU do que o necessário. O formato Apache Parquet armazena dados de forma colunar nativa com compressão de alta densidade, reduzindo o tempo de consulta no DuckDB em até dez vezes.

Um script Python agendado semanalmente pode ler os lotes brutos de JSON do Cloudflare R2, filtrar os bots legítimos e salvar tabelas Parquet particionadas por ano, mês e dia:

import duckdb

con = duckdb.connect(database=":memory:")

con.execute("""
    INSTALL httpfs;
    LOAD httpfs;
    SET s3_endpoint = 'seu_account_id.r2.cloudflarestorage.com';
    SET s3_access_key_id = 'sua_access_key';
    SET s3_secret_access_key = 'sua_secret_key';
    SET s3_url_style = 'path';
""")

query_consolidacao = """
COPY (
    SELECT 
        make_date(
            date_part('year', to_timestamp(EdgeStartTimestamp / 1000000000)::TIMESTAMP)::INT,
            date_part('month', to_timestamp(EdgeStartTimestamp / 1000000000)::TIMESTAMP)::INT,
            date_part('day', to_timestamp(EdgeStartTimestamp / 1000000000)::TIMESTAMP)::INT
        ) AS data_evento,
        ClientIP,
        ClientRequestHost,
        ClientRequestPath,
        ClientRequestURI,
        ClientRequestUserAgent,
        EdgeResponseStatus,
        EdgeResponseBytes,
        CacheCacheStatus
    FROM read_ndjson_objects('s3://meu-bucket-logs/2026/09/*/*.json.gz')
    WHERE ClientRequestUserAgent LIKE '%Googlebot%'
) TO 's3://meu-bucket-logs/parquet_consolidado/' 
(FORMAT PARQUET, PARTITION_BY (data_evento), OVERWRITE_OR_IGNORE 1);
"""

con.execute(query_consolidacao)
print("Consolidação em Parquet finalizada com sucesso.")

Com essa estrutura particionada, consultas futuras que avaliem apenas um dia ou uma semana leem exclusivamente as pastas correspondentes, permitindo que relatórios operacionais rodem em menos de um segundo.

Da análise de dados às decisões de negócio em SEO

A análise técnica de logs só gera impacto real quando os dados orientam correções na arquitetura do site. Métricas isoladas de hits e megabytes transferidos não aumentam o tráfego orgânico por si sós. O objetivo central é direcionar a atenção do robô de busca para as páginas com maior potencial de retorno financeiro.

Quando os dados do DuckDB apontarem que o Googlebot consome 40% do seu tempo de rastreamento em parâmetros de busca interna ou URLs com códigos de erro, a prioridade não é discutir teoria de SEO, mas implementar correções técnicas no código da aplicação e nas diretivas de cabeçalho. Ao eliminar o desperdício, o buscador passa a descobrir produtos recém-publicados em horas em vez de semanas.

Se você gerencia um portal com alto volume de URLs e precisa transformar milhões de registros de log em uma estratégia acionável de crawl budget e arquitetura de dados, conte com a minha consultoria especializada para desenhar esse pipeline do zero. Conheça as opções em nossa página de serviços especializados ou entre em contato direto para realizarmos um diagnóstico inicial.