Automação de SEO com Python: Scripts Práticos para o Dia a Dia

· 19 min de leitura · Por Fabio Santiago

Por que automatizar SEO com Python

Tarefas repetitivas consomem horas que poderiam ser investidas em estratégia. Verificar status codes de centenas de URLs, extrair meta tags de um site inteiro, analisar logs de servidor — tudo isso pode ser automatizado com scripts Python simples.

Você não precisa ser programador senior. Os scripts abaixo são diretos, comentados e prontos para copiar e adaptar.

Pré-requisitos

Instale as bibliotecas necessárias:

pip install requests beautifulsoup4 pandas

Script 1: Verificador de status codes em massa

Verifica o status HTTP de uma lista de URLs e identifica problemas (404, 500, redirecionamentos).

import requests
import csv
from concurrent.futures import ThreadPoolExecutor

def check_url(url):
    try:
        r = requests.head(url.strip(), timeout=10, allow_redirects=True)
        return {
            'url': url.strip(),
            'status': r.status_code,
            'final_url': r.url,
            'redirect': url.strip() != r.url
        }
    except requests.RequestException as e:
        return {
            'url': url.strip(),
            'status': 'ERRO',
            'final_url': str(e),
            'redirect': False
        }

# Leia URLs de um arquivo (uma por linha)
with open('urls.txt', 'r') as f:
    urls = [line.strip() for line in f if line.strip()]

# Verificar em paralelo (10 threads)
with ThreadPoolExecutor(max_workers=10) as executor:
    results = list(executor.map(check_url, urls))

# Salvar resultado
with open('status_report.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=['url', 'status', 'final_url', 'redirect'])
    writer.writeheader()
    writer.writerows(results)

# Resumo
errors = [r for r in results if r['status'] != 200]
print(f"Total: {len(results)} URLs")
print(f"OK (200): {len(results) - len(errors)}")
print(f"Problemas: {len(errors)}")
for r in errors:
    print(f"  {r['status']} - {r['url']}")

Script 2: Extrator de meta tags

Extrai title, meta description e canonical de todas as páginas de um site.

import requests
from bs4 import BeautifulSoup
import csv

def extract_meta(url):
    try:
        r = requests.get(url.strip(), timeout=10)
        soup = BeautifulSoup(r.text, 'html.parser')

        title_tag = soup.find('title')
        title = title_tag.text.strip() if title_tag else ''

        desc_tag = soup.find('meta', attrs={'name': 'description'})
        description = desc_tag['content'].strip() if desc_tag and desc_tag.get('content') else ''

        canonical_tag = soup.find('link', attrs={'rel': 'canonical'})
        canonical = canonical_tag['href'].strip() if canonical_tag and canonical_tag.get('href') else ''

        h1_tag = soup.find('h1')
        h1 = h1_tag.text.strip() if h1_tag else ''

        return {
            'url': url.strip(),
            'title': title,
            'title_len': len(title),
            'description': description,
            'desc_len': len(description),
            'canonical': canonical,
            'h1': h1,
        }
    except Exception as e:
        return {'url': url.strip(), 'title': f'ERRO: {e}'}

with open('urls.txt', 'r') as f:
    urls = [line.strip() for line in f if line.strip()]

results = [extract_meta(url) for url in urls]

with open('meta_report.csv', 'w', newline='', encoding='utf-8') as f:
    writer = csv.DictWriter(f, fieldnames=['url', 'title', 'title_len', 'description', 'desc_len', 'canonical', 'h1'])
    writer.writeheader()
    writer.writerows(results)

# Alertas
for r in results:
    if r.get('title_len', 0) > 60:
        print(f"Title muito longo ({r['title_len']}): {r['url']}")
    if r.get('title_len', 0) == 0:
        print(f"Title ausente: {r['url']}")
    if r.get('desc_len', 0) > 160:
        print(f"Description muito longa ({r['desc_len']}): {r['url']}")
    if r.get('desc_len', 0) == 0:
        print(f"Description ausente: {r['url']}")

Script 3: Analisador de logs do servidor

Analisa logs de acesso Apache/Nginx para identificar padrões do Googlebot.

import re
from collections import Counter
from datetime import datetime

# Regex para log Apache/Nginx comum
log_pattern = re.compile(
    r'(?P<ip>\S+) .* \[(?P<date>.*?)\] '
    r'"(?P<method>\S+) (?P<path>\S+) \S+" '
    r'(?P<status>\d+) (?P<size>\S+) '
    r'".*?" "(?P<agent>.*?)"'
)

googlebot_hits = []
with open('access.log', 'r') as f:
    for line in f:
        match = log_pattern.match(line)
        if match and 'Googlebot' in match.group('agent'):
            googlebot_hits.append({
                'date': match.group('date'),
                'path': match.group('path'),
                'status': match.group('status'),
            })

print(f"Total de hits do Googlebot: {len(googlebot_hits)}")

# URLs mais rastreadas
paths = Counter(h['path'] for h in googlebot_hits)
print("\nTop 20 URLs mais rastreadas pelo Googlebot:")
for path, count in paths.most_common(20):
    print(f"  {count:>5}x  {path}")

# Status codes
statuses = Counter(h['status'] for h in googlebot_hits)
print("\nStatus codes:")
for status, count in statuses.most_common():
    print(f"  {status}: {count}x")

# URLs com erro
errors = [h for h in googlebot_hits if h['status'].startswith('4') or h['status'].startswith('5')]
if errors:
    print(f"\nURLs com erro ({len(errors)} hits):")
    error_paths = Counter(h['path'] for h in errors)
    for path, count in error_paths.most_common(10):
        print(f"  {count:>5}x  {path}")

Script 4: Gerador de sitemap XML

Gera um sitemap XML a partir de uma lista de URLs com data de modificação.

from datetime import datetime

urls = [
    {"loc": "https://seusite.com/", "priority": "1.0", "freq": "daily"},
    {"loc": "https://seusite.com/blog/", "priority": "0.9", "freq": "daily"},
    {"loc": "https://seusite.com/sobre/", "priority": "0.6", "freq": "monthly"},
]

today = datetime.now().strftime('%Y-%m-%d')

xml = '<?xml version="1.0" encoding="UTF-8"?>\n'
xml += '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">\n'

for url in urls:
    xml += f'  <url>\n'
    xml += f'    <loc>{url["loc"]}</loc>\n'
    xml += f'    <lastmod>{today}</lastmod>\n'
    xml += f'    <changefreq>{url["freq"]}</changefreq>\n'
    xml += f'    <priority>{url["priority"]}</priority>\n'
    xml += f'  </url>\n'

xml += '</urlset>'

with open('sitemap.xml', 'w') as f:
    f.write(xml)

print(f"Sitemap gerado com {len(urls)} URLs")

Verifica a posição aproximada do seu site para determinadas keywords via scraping do Google. Atenção: use com moderação, o Google pode bloquear requisições excessivas.

import requests
from bs4 import BeautifulSoup
import time
import random

def check_position(keyword, target_domain, num_results=30):
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    params = {
        'q': keyword,
        'num': num_results,
        'hl': 'pt-BR',
        'gl': 'br'
    }

    try:
        r = requests.get('https://www.google.com/search', params=params, headers=headers, timeout=10)
        soup = BeautifulSoup(r.text, 'html.parser')

        position = 0
        for result in soup.select('div.g'):
            position += 1
            link = result.select_one('a')
            if link and target_domain in (link.get('href', '')):
                return {'keyword': keyword, 'position': position, 'url': link['href']}

        return {'keyword': keyword, 'position': 'Nao encontrado', 'url': ''}
    except Exception as e:
        return {'keyword': keyword, 'position': f'Erro: {e}', 'url': ''}

keywords = [
    "o que e crawl budget",
    "canonical tags seo",
    "heading tags guia",
]

target = "fabiosantiago.dev.br"

for kw in keywords:
    result = check_position(kw, target)
    print(f"[{result['position']}] {result['keyword']}")
    time.sleep(random.uniform(5, 15))  # Delay entre requisicoes

Rastreia um site e identifica links internos que retornam 404.

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse

def crawl_site(start_url, max_pages=100):
    domain = urlparse(start_url).netloc
    visited = set()
    to_visit = [start_url]
    broken = []

    while to_visit and len(visited) < max_pages:
        url = to_visit.pop(0)
        if url in visited:
            continue

        try:
            r = requests.get(url, timeout=10)
            visited.add(url)

            if r.status_code != 200:
                broken.append({'url': url, 'status': r.status_code})
                continue

            soup = BeautifulSoup(r.text, 'html.parser')
            for link in soup.find_all('a', href=True):
                href = urljoin(url, link['href'])
                parsed = urlparse(href)
                if parsed.netloc == domain and href not in visited:
                    clean_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
                    if clean_url not in visited:
                        to_visit.append(clean_url)

        except Exception as e:
            broken.append({'url': url, 'status': f'Erro: {e}'})

    return visited, broken

visited, broken = crawl_site('https://seusite.com', max_pages=50)

print(f"Paginas rastreadas: {len(visited)}")
print(f"Links quebrados: {len(broken)}")
for b in broken:
    print(f"  [{b['status']}] {b['url']}")

Dicas para automatizar com eficiência

1. Use rate limiting

Nunca envie centenas de requisições por segundo. Use delays entre requisições (time.sleep()) para evitar bloqueios e ser respeitoso com os servidores.

2. Trate erros

Conexões falham, servidores demoram, respostas vêm incompletas. Use try/except em tudo e defina timeouts.

3. Salve resultados incrementalmente

Para scripts que processam muitas URLs, salve resultados a cada iteração (não apenas no final). Se o script falhar no meio, você não perde tudo.

4. Use multithreading com cuidado

ThreadPoolExecutor acelera muito verificações de URL, mas não exagere no número de threads. 5-10 é suficiente para a maioria dos casos.

5. Agende scripts com cron

# Verificar status codes todo domingo às 3h da manhã
0 3 * * 0 /usr/bin/python3 /caminho/check_status.py >> /caminho/log.txt 2>&1

Conclusão

Python é a linguagem ideal para automação de SEO: simples de aprender, rica em bibliotecas e poderosa o suficiente para qualquer tarefa. Os scripts deste artigo cobrem as necessidades mais comuns, mas são pontos de partida — adapte conforme seu caso.

A regra de ouro: se você faz a mesma tarefa manualmente mais de três vezes, automatize.

Gratuito

Gostou deste artigo?

Receba dicas exclusivas de SEO, novas ferramentas e guias toda semana. Sem spam — apenas conteúdo útil.

Sem spam. Cancele quando quiser.