Código e Automação

26 de mar. de 2026

Go back

Como validar XML Sitemap automaticamente com Python (SEO técnico)

Aprenda a validar XML Sitemap com Python verificando status HTTP, indexabilidade e erros técnicos para otimizar SEO e crawl do Google.

A crazy but friendly cartoon scientist with messy hair, wearing a white lab coat, holding a giant glowing CODEBOOK

Fique por dentro do que há de mais relavante no Marketing Digital, assine a nossa newsletter:

Se você trabalha com SEO técnico, sabe que um sitemap pode facilmente se tornar um problema invisível.

URLs quebradas, páginas com noindex, redirecionamentos… tudo isso pode estar dentro do seu sitemap sem você perceber.

E isso impacta diretamente:

  • crawl budget

  • indexação

  • performance orgânica

Neste post, vamos criar um script em Python para validar automaticamente um XML Sitemap, verificando:

  • status HTTP

  • indexabilidade

  • redirecionamentos

  • erros críticos

O que vamos validar

O script vai analisar:

  • URLs com erro (404, 500)

  • redirecionamentos (301, 302)

  • páginas não indexáveis

  • tempo de resposta

  • estrutura do sitemap

Requisitos

Antes de começar:

Script completo

import requests
import xml.etree.ElementTree as ET
import pandas as pd
from concurrent.futures import ThreadPoolExecutor

# Configurações
SITEMAP_URL = "https://adrock.com.br/sitemap.xml"
HEADERS = {"User-Agent": "Mozilla/5.0"}
TIMEOUT = 10
MAX_THREADS = 10


def get_sitemap_urls(sitemap_url):
    response = requests.get(sitemap_url, headers=HEADERS)
    root = ET.fromstring(response.content)

    urls = []

    for sitemap in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}sitemap"):
        loc = sitemap.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.extend(get_sitemap_urls(loc))

    for url in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}url"):
        loc = url.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.append(loc)

    return urls


def check_url(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT, allow_redirects=True)
        status = response.status_code
        final_url = response.url

        return {
            "url": url,
            "status": status,
            "final_url": final_url,
            "redirect": url != final_url,
            "response_time": response.elapsed.total_seconds()
        }

    except Exception as e:
        return {
            "url": url,
            "status": "ERROR",
            "final_url": None,
            "redirect": False,
            "response_time": None
        }


def validate_sitemap():
    print("🔍 Carregando sitemap...")
    urls = get_sitemap_urls(SITEMAP_URL)

    print(f"📊 Total de URLs: {len(urls)}")

    results = []

    with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        results = list(executor.map(check_url, urls))

    df = pd.DataFrame(results)

    return df


def generate_report(df):
    print("\n📈 RELATÓRIO\n")

    print("URLs OK (200):", len(df[df["status"] == 200]))
    print("Redirecionamentos:", len(df[df["redirect"] == True]))
    print("Erros:", len(df[df["status"] != 200]))

    df.to_csv("sitemap_report.csv", index=False)
    print("\n📁 Relatório salvo em sitemap_report.csv")


if __name__ == "__main__":
    df = validate_sitemap()
    generate_report(df)
import requests
import xml.etree.ElementTree as ET
import pandas as pd
from concurrent.futures import ThreadPoolExecutor

# Configurações
SITEMAP_URL = "https://adrock.com.br/sitemap.xml"
HEADERS = {"User-Agent": "Mozilla/5.0"}
TIMEOUT = 10
MAX_THREADS = 10


def get_sitemap_urls(sitemap_url):
    response = requests.get(sitemap_url, headers=HEADERS)
    root = ET.fromstring(response.content)

    urls = []

    for sitemap in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}sitemap"):
        loc = sitemap.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.extend(get_sitemap_urls(loc))

    for url in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}url"):
        loc = url.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.append(loc)

    return urls


def check_url(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT, allow_redirects=True)
        status = response.status_code
        final_url = response.url

        return {
            "url": url,
            "status": status,
            "final_url": final_url,
            "redirect": url != final_url,
            "response_time": response.elapsed.total_seconds()
        }

    except Exception as e:
        return {
            "url": url,
            "status": "ERROR",
            "final_url": None,
            "redirect": False,
            "response_time": None
        }


def validate_sitemap():
    print("🔍 Carregando sitemap...")
    urls = get_sitemap_urls(SITEMAP_URL)

    print(f"📊 Total de URLs: {len(urls)}")

    results = []

    with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        results = list(executor.map(check_url, urls))

    df = pd.DataFrame(results)

    return df


def generate_report(df):
    print("\n📈 RELATÓRIO\n")

    print("URLs OK (200):", len(df[df["status"] == 200]))
    print("Redirecionamentos:", len(df[df["redirect"] == True]))
    print("Erros:", len(df[df["status"] != 200]))

    df.to_csv("sitemap_report.csv", index=False)
    print("\n📁 Relatório salvo em sitemap_report.csv")


if __name__ == "__main__":
    df = validate_sitemap()
    generate_report(df)
import requests
import xml.etree.ElementTree as ET
import pandas as pd
from concurrent.futures import ThreadPoolExecutor

# Configurações
SITEMAP_URL = "https://adrock.com.br/sitemap.xml"
HEADERS = {"User-Agent": "Mozilla/5.0"}
TIMEOUT = 10
MAX_THREADS = 10


def get_sitemap_urls(sitemap_url):
    response = requests.get(sitemap_url, headers=HEADERS)
    root = ET.fromstring(response.content)

    urls = []

    for sitemap in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}sitemap"):
        loc = sitemap.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.extend(get_sitemap_urls(loc))

    for url in root.findall("{http://www.sitemaps.org/schemas/sitemap/0.9}url"):
        loc = url.find("{http://www.sitemaps.org/schemas/sitemap/0.9}loc").text
        urls.append(loc)

    return urls


def check_url(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT, allow_redirects=True)
        status = response.status_code
        final_url = response.url

        return {
            "url": url,
            "status": status,
            "final_url": final_url,
            "redirect": url != final_url,
            "response_time": response.elapsed.total_seconds()
        }

    except Exception as e:
        return {
            "url": url,
            "status": "ERROR",
            "final_url": None,
            "redirect": False,
            "response_time": None
        }


def validate_sitemap():
    print("🔍 Carregando sitemap...")
    urls = get_sitemap_urls(SITEMAP_URL)

    print(f"📊 Total de URLs: {len(urls)}")

    results = []

    with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor:
        results = list(executor.map(check_url, urls))

    df = pd.DataFrame(results)

    return df


def generate_report(df):
    print("\n📈 RELATÓRIO\n")

    print("URLs OK (200):", len(df[df["status"] == 200]))
    print("Redirecionamentos:", len(df[df["redirect"] == True]))
    print("Erros:", len(df[df["status"] != 200]))

    df.to_csv("sitemap_report.csv", index=False)
    print("\n📁 Relatório salvo em sitemap_report.csv")


if __name__ == "__main__":
    df = validate_sitemap()
    generate_report(df)

O que esse script faz

1. Lê o sitemap automaticamente

  • suporta sitemap index

  • percorre todos os arquivos

2. Testa cada URL

Para cada página:

  • verifica status HTTP

  • identifica redirecionamento

  • mede tempo de resposta

3. Gera relatório

Saída em CSV com:

  • URL

  • status

  • redirecionamento

  • tempo

Como usar na prática

Rodar localmente

Automatizar (cron)

0 2
0 2
0 2

Monitoramento contínuo

Você pode integrar com:

  • Google Sheets

  • Looker Studio

  • alertas por e-mail

Melhorias recomendadas

Você pode evoluir o script para:

  • verificar meta robots (index/noindex)

  • detectar canonical incorreto

  • validar conteúdo duplicado

  • integrar com Search Console API

Por que isso é importante

Segundo o Google:

  • apenas páginas indexáveis devem estar no sitemap

  • erros reduzem eficiência de rastreamento

Fonte oficial:
https://developers.google.com/search/docs/crawling-indexing/sitemaps/overview

O erro que você evita

Sem validação:

  • sitemap cheio de erro

  • desperdício de crawl

  • páginas importantes ignoradas

O papel da Ad Rock

Na Ad Rock, usamos automação para:

  • validar SEO técnico

  • monitorar indexação

  • escalar análise de sites

Porque SEO moderno não é manual.

É programático.

Conclusão

Validar sitemap não é opcional.

É parte do SEO técnico avançado.

E com Python, você consegue:

  • automatizar

  • escalar

  • detectar problemas rapidamente

Quer implementar isso no seu projeto?

👉 https://adrock.com.br/contato

Adendo: validação avançada — canonical e conteúdo duplicado

Além das verificações básicas de status HTTP, existem dois pontos críticos que impactam diretamente SEO:

  • canonical incorreto

  • conteúdo duplicado

Abaixo estão scripts complementares para evoluir sua análise.

1. Detectar canonical incorreto

Esse script verifica:

  • se a página possui canonical

  • se o canonical aponta para outra URL

  • se existe inconsistência

from bs4 import BeautifulSoup

def check_canonical(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        soup = BeautifulSoup(response.text, "html.parser")

        canonical_tag = soup.find("link", rel="canonical")

        if canonical_tag:
            canonical_url = canonical_tag.get("href")

            return {
                "url": url,
                "canonical": canonical_url,
                "canonical_ok": url == canonical_url
            }
        else:
            return {
                "url": url,
                "canonical": None,
                "canonical_ok": False
            }

    except:
        return {
            "url": url,
            "canonical": None,
            "canonical_ok": False
        }
from bs4 import BeautifulSoup

def check_canonical(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        soup = BeautifulSoup(response.text, "html.parser")

        canonical_tag = soup.find("link", rel="canonical")

        if canonical_tag:
            canonical_url = canonical_tag.get("href")

            return {
                "url": url,
                "canonical": canonical_url,
                "canonical_ok": url == canonical_url
            }
        else:
            return {
                "url": url,
                "canonical": None,
                "canonical_ok": False
            }

    except:
        return {
            "url": url,
            "canonical": None,
            "canonical_ok": False
        }
from bs4 import BeautifulSoup

def check_canonical(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        soup = BeautifulSoup(response.text, "html.parser")

        canonical_tag = soup.find("link", rel="canonical")

        if canonical_tag:
            canonical_url = canonical_tag.get("href")

            return {
                "url": url,
                "canonical": canonical_url,
                "canonical_ok": url == canonical_url
            }
        else:
            return {
                "url": url,
                "canonical": None,
                "canonical_ok": False
            }

    except:
        return {
            "url": url,
            "canonical": None,
            "canonical_ok": False
        }

O que analisar

  • canonical apontando para outra URL → possível duplicação

  • ausência de canonical → risco de indexação incorreta

2. Detectar conteúdo duplicado (hash simples)

Aqui usamos um hash do conteúdo da página para identificar duplicação.

import hashlib

def get_content_hash(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        content = response.text

        content_hash = hashlib.md5(content.encode("utf-8")).hexdigest()

        return {
            "url": url,
            "hash": content_hash
        }

    except:
        return {
            "url": url,
            "hash": None
        }
import hashlib

def get_content_hash(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        content = response.text

        content_hash = hashlib.md5(content.encode("utf-8")).hexdigest()

        return {
            "url": url,
            "hash": content_hash
        }

    except:
        return {
            "url": url,
            "hash": None
        }
import hashlib

def get_content_hash(url):
    try:
        response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
        content = response.text

        content_hash = hashlib.md5(content.encode("utf-8")).hexdigest()

        return {
            "url": url,
            "hash": content_hash
        }

    except:
        return {
            "url": url,
            "hash": None
        }

3. Identificar duplicados

Depois de coletar os hashes:

def find_duplicates(df):
    duplicates = df[df.duplicated("hash", keep=False)]
    return duplicates.sort_values("hash")
def find_duplicates(df):
    duplicates = df[df.duplicated("hash", keep=False)]
    return duplicates.sort_values("hash")
def find_duplicates(df):
    duplicates = df[df.duplicated("hash", keep=False)]
    return duplicates.sort_values("hash")

Limitações importantes

Esse método:

  • detecta duplicação exata

  • não identifica conteúdo parcialmente duplicado

  • não ignora elementos como menu ou footer

Evoluções recomendadas

Para análise mais avançada:

  • remover HTML e analisar apenas texto

  • usar similaridade semântica (NLP / embeddings)

  • ignorar elementos comuns do layout

Por que isso importa

Segundo o Google:

  • canonical ajuda a consolidar sinais de ranking

  • conteúdo duplicado dilui autoridade

Fonte oficial:
https://developers.google.com/search/docs/crawling-indexing/consolidate-duplicate-urls

Conclusão do adendo

Com essas duas validações, você adiciona uma camada crítica ao seu SEO técnico:

  • evita indexação errada

  • consolida autoridade

  • melhora eficiência de crawl

SEO técnico eficiente hoje exige esse nível de controle.

Conteúdo original pesquisado e redigido pelo autor. Ferramentas de IA podem ter sido utilizadas para auxiliar na edição e no aprimoramento.

Conteúdo original pesquisado e redigido pelo autor. Ferramentas de IA podem ter sido utilizadas para auxiliar na edição e no aprimoramento.

Posts relacionados:

Posts relacionados:

Compartilhe!

Go back

Deixe a IA fazer o trabalho para Você Crescer Mais Rápido

Agende uma conversa hoje e comece a automatizar.

Deixe a IA fazer o trabalho para Você Crescer Mais Rápido

Agende uma conversa hoje e comece a automatizar.

© 2010 - 2026 Copyright

All Rights Reserved - Develop by Ad Rock Digital Mkt

Tecnologias utilizadas

© 2010 - 2026 Copyright

All Rights Reserved - Develop by
Ad Rock Digital Mkt

Tecnologias utilizadas