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")
Script 5: Monitor de posições (Google Search)
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
Script 6: Verificador de links internos quebrados
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.