Automação de Relatórios SEO com Python e Google API

· 20 min de leitura · Por Fabio Santiago

Por que automatizar relatórios SEO

Se você gasta mais de 2 horas por semana gerando relatórios manuais, está jogando tempo fora. Automatizar relatórios SEO com Python permite:

  • Consistência: mesmo formato, mesmas métricas, toda semana
  • Velocidade: de 2 horas para 30 segundos
  • Histórico: dados armazenados automaticamente para comparação
  • Escalabilidade: funciona para 1 ou 100 sites

Pré-requisitos

pip install google-api-python-client google-auth-oauthlib pandas

Configurar credenciais

  1. Acesse Google Cloud Console
  2. Crie um projeto novo
  3. Ative as APIs: Search Console API e Analytics Data API
  4. Crie credenciais OAuth 2.0 (tipo “Desktop app”)
  5. Baixe o credentials.json

Script 1: Dados do Search Console

from googleapiclient.discovery import build
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
import pandas as pd
from datetime import datetime, timedelta

SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']
SITE_URL = 'https://seusite.com.br'

def autenticar():
    flow = InstalledAppFlow.from_client_secrets_file(
        'credentials.json', SCOPES
    )
    creds = flow.run_local_server(port=0)
    return build('searchconsole', 'v1', credentials=creds)

def buscar_dados(service, dias=28):
    end_date = datetime.now().strftime('%Y-%m-%d')
    start_date = (datetime.now() - timedelta(days=dias)).strftime('%Y-%m-%d')

    response = service.searchanalytics().query(
        siteUrl=SITE_URL,
        body={
            'startDate': start_date,
            'endDate': end_date,
            'dimensions': ['query', 'page'],
            'rowLimit': 1000,
            'dimensionFilterGroups': [{
                'filters': [{
                    'dimension': 'country',
                    'expression': 'bra'
                }]
            }]
        }
    ).execute()

    rows = response.get('rows', [])
    data = []
    for row in rows:
        data.append({
            'query': row['keys'][0],
            'page': row['keys'][1],
            'clicks': row['clicks'],
            'impressions': row['impressions'],
            'ctr': round(row['ctr'] * 100, 2),
            'position': round(row['position'], 1),
        })

    return pd.DataFrame(data)

# Executar
service = autenticar()
df = buscar_dados(service)
df.to_csv(f'relatorio_seo_{datetime.now().strftime("%Y%m%d")}.csv', index=False)
print(f"Relatório gerado: {len(df)} linhas")

Script 2: Top páginas que estão caindo

def paginas_em_queda(service):
    """Compara últimos 7 dias com 7 dias anteriores"""

    def get_period(start, end):
        resp = service.searchanalytics().query(
            siteUrl=SITE_URL,
            body={
                'startDate': start,
                'endDate': end,
                'dimensions': ['page'],
                'rowLimit': 500,
            }
        ).execute()
        return {
            row['keys'][0]: row['clicks']
            for row in resp.get('rows', [])
        }

    today = datetime.now()
    current = get_period(
        (today - timedelta(days=7)).strftime('%Y-%m-%d'),
        today.strftime('%Y-%m-%d')
    )
    previous = get_period(
        (today - timedelta(days=14)).strftime('%Y-%m-%d'),
        (today - timedelta(days=7)).strftime('%Y-%m-%d')
    )

    drops = []
    for page, clicks in previous.items():
        current_clicks = current.get(page, 0)
        if clicks > 10 and current_clicks < clicks * 0.7:
            drops.append({
                'page': page,
                'previous': clicks,
                'current': current_clicks,
                'drop': f"{((clicks - current_clicks) / clicks) * 100:.0f}%"
            })

    return sorted(drops, key=lambda x: x['previous'], reverse=True)

Script 3: Oportunidades (impressões altas, CTR baixo)

def encontrar_oportunidades(df):
    """Páginas com muitas impressões mas CTR baixo = oportunidade"""
    oportunidades = df[
        (df['impressions'] > 100) &
        (df['ctr'] < 2.0) &
        (df['position'] < 20)
    ].sort_values('impressions', ascending=False)

    return oportunidades[['query', 'page', 'impressions', 'ctr', 'position']]

Script 4: Relatório em HTML

def gerar_relatorio_html(df, drops, oportunidades):
    html = f"""
    <html>
    <head><style>
        body {{ font-family: sans-serif; max-width: 800px; margin: 0 auto; }}
        table {{ border-collapse: collapse; width: 100%; }}
        th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
        th {{ background: #1a1a2e; color: white; }}
        .drop {{ color: red; font-weight: bold; }}
    </style></head>
    <body>
        <h1>Relatório SEO — {datetime.now().strftime('%d/%m/%Y')}</h1>

        <h2>Resumo</h2>
        <p>Total de clicks: <b>{df['clicks'].sum():,}</b></p>
        <p>Total de impressões: <b>{df['impressions'].sum():,}</b></p>
        <p>CTR médio: <b>{df['ctr'].mean():.2f}%</b></p>

        <h2>Páginas em queda</h2>
        <table>
            <tr><th>Página</th><th>Anterior</th><th>Atual</th><th>Queda</th></tr>
            {''.join(f"<tr><td>{d['page']}</td><td>{d['previous']}</td><td>{d['current']}</td><td class='drop'>{d['drop']}</td></tr>" for d in drops[:10])}
        </table>

        <h2>Oportunidades (impressões altas, CTR baixo)</h2>
        {oportunidades.head(15).to_html(index=False)}
    </body>
    </html>
    """

    with open(f'relatorio_{datetime.now().strftime("%Y%m%d")}.html', 'w') as f:
        f.write(html)

Agendamento com cron

# Executar todo domingo às 8h
0 8 * * 0 cd /home/user/seo-reports && python3 relatorio_seo.py >> cron.log 2>&1

Envio por email (opcional)

import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

def enviar_email(html_content, destinatario):
    msg = MIMEMultipart('alternative')
    msg['Subject'] = f'Relatório SEO - {datetime.now().strftime("%d/%m/%Y")}'
    msg['From'] = 'relatorios@seudominio.com'
    msg['To'] = destinatario

    msg.attach(MIMEText(html_content, 'html'))

    with smtplib.SMTP_SSL('smtp.gmail.com', 465) as server:
        server.login('seu@email.com', 'app_password')
        server.send_message(msg)

Alternativas sem código

Se Python não é o seu forte:

FerramentaPreçoComplexidade
Google Looker StudioGrátisBaixa
Screaming Frog$259/anoMédia
SE Ranking$65/mêsBaixa
Supermetrics$99/mêsMédia
Automate the Boring Stuff with Python
Dev R$ 145

Automate the Boring Stuff with Python

O clássico sobre automação com Python. Perfeito para quem quer começar a automatizar tarefas repetitivas.

Ver na Amazon

#anúncio · Link de afiliado Amazon. Ao comprar por este link, você apoia o site sem custo adicional.

Conclusão

Automatizar relatórios SEO com Python não é luxo — é necessidade para quem gerencia mais de 2 sites. Comece pelo Script 1 (dados brutos do Search Console), depois evolua para detecção de quedas e oportunidades. Em menos de um dia você monta um sistema que economiza horas toda semana.

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.