Destino Dev

Blog

Automação web com Playwright (Python): login, navegação e exportar CSV

Por John Pires · 19 de março de 2026

Automação web com Playwright (Python): login, navegação e exportar CSV

Introducao ao problema

O que queremos automatizar

Automação web significa controlar um navegador para executar tarefas repetitivas: fazer login, navegar em páginas, extrair dados e salvar em um arquivo CSV. Para iniciantes, isso é como ensinar um assistente a seguir passos estritamente definidos em uma página web. A diferença entre um assistente manual e a automação é que o script precisa lidar com esperas, mudanças de DOM e possíveis falhas.

Por que Playwright?

Playwright é moderno, suporta múltiplos navegadores (Chromium, Firefox, WebKit) e oferece APIs para aguardar elementos, interceptar rede e executar em headless ou headed. Para quem começa, Playwright reduz conceitos confusos como drivers separados (comparado ao Selenium) e fornece métodos de espera explícita que ajudam a evitar flakiness comum em automações.

Neste artigo adotamos o tom de code review: primeiro mostramos um jeito errado, depois mostramos a correção e explicamos por quê. Isso ajuda a entender armadilhas e boas práticas desde o início.

Dica: sempre pense em robustez. Um script que funciona só uma vez não é automação — é sorte.

Pre-requisitos e setup

Instalando Playwright

Instale Playwright para Python e rode o instalador para browsers. Use um ambiente virtual para isolar dependências. Na prática:

python -m venv .venv
source .venv/bin/activate  # mac/linux
.venv\Scripts\activate     # windows
pip install playwright
python -m playwright install

O comando `python -m playwright install` baixa os binários dos navegadores. Sem ele, o Playwright não consegue iniciar o browser.

Ferramentas auxiliares

Recomendo instalar também ferramentas básicas: um editor como VSCode, o módulo csv do Python (já incluído) e, se for expor o processo por HTTP, FastAPI e uvicorn.

pip install fastapi uvicorn

Esses pacotes são opcionais para a parte de serviço, mas úteis para integrar automações em endpoints.

Tutorial guiado passo a passo

Jeito errado: sleeps e seletores fracos

Muitos iniciantes fazem o script com timers fixos (time.sleep) e seletores frágeis (texto ou índices). Isso funciona em um ambiente estável, mas falha quando a rede é lenta ou o DOM muda ligeiramente.

Exemplo ruim: usar sleeps para aguardar carregamento. O código abaixo tenta logar usando sleeps — isso quebra em conexões lentas ou se o elemento demorar a aparecer.

from playwright.sync_api import sync_playwright
import time

# O que faz: abre o navegador e tenta logar usando time.sleep (jeito errado)
with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://example.com/login')
    time.sleep(2)  # espera estático — frágil
    page.fill('#username', 'meu_usuario')
    page.fill('#password', 'senha_insegura')
    page.click('button[type="submit"]')
    time.sleep(5)  # espera estático para garantir redirecionamento
    content = page.content()
    print('Tamanho do HTML:', len(content))
    browser.close()

O que acontece: o script pode falhar com ElementHandleNotFound se os seletores mudarem; ou pode prosseguir cedo demais se a página demorar mais do que os sleeps.

Jeito certo: usar esperas explícitas e seletores robustos

O Playwright oferece métodos como wait_for_selector e espera automática para ações. Use seletores de atributo, data-testid, ou caminhos robustos. Prefira esperar por elementos visíveis e interativos.

Segue a versão corrigida — aguarda seletor de login, usa try/except e captura erros com logs.

from playwright.sync_api import sync_playwright, TimeoutError

# O que faz: faz login de forma robusta usando esperas explícitas
def login_and_get_page(url, username, password):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.goto(url)
        try:
            # espera até o campo de usuário estar visível e interativo
            page.wait_for_selector('input[name="username"]', timeout=10000)
            page.fill('input[name="username"]', username)
            page.fill('input[name="password"]', password)
            page.click('button[type="submit"]')
            # espera até um seletor que indica login bem-sucedido
            page.wait_for_selector('nav .profile', timeout=10000)
            print('Login OK')
            return page
        except TimeoutError:
            print('Erro: elemento não apareceu no tempo esperado')
            browser.close()
            raise

O que acontece: em vez de sleeps, o script espera até os elementos realmente existirem. Isso torna o fluxo tolerante a variações de latência e reduz falsos negativos.

Extração de dados e exportar CSV

Depois do login, normalmente queremos coletar dados: tabelas, listas, cards. O código abaixo lê uma tabela HTML e exporta para CSV usando o módulo csv do Python.

import csv
from playwright.sync_api import sync_playwright

# O que faz: navega até a página de relatórios, extrai linhas de tabela e salva em CSV
def export_table_to_csv(page, table_selector, csv_path):
    # espera a tabela existir
    page.wait_for_selector(table_selector, timeout=10000)
    rows = page.query_selector_all(f'{table_selector} tbody tr')
    with open(csv_path, 'w', newline='', encoding='utf-8') as f:
        writer = csv.writer(f)
        # cabeçalho: pegar <th> se existir
        headers = [h.inner_text().strip() for h in page.query_selector_all(f'{table_selector} thead th')]
        if headers:
            writer.writerow(headers)
        for r in rows:
            cols = [c.inner_text().strip() for c in r.query_selector_all('td')]
            writer.writerow(cols)
    print(f'Exportado {len(rows)} linhas para {csv_path}')

# Exemplo de uso com página já autenticada
with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto('https://example.com/login')
    page.fill('input[name="username"]', 'meu_usuario')
    page.fill('input[name="password"]', 'senha')
    page.click('button[type="submit"]')
    page.wait_for_selector('#relatorio-table', timeout=10000)
    export_table_to_csv(page, '#relatorio-table', 'relatorio.csv')
    browser.close()

O que acontece: o script grava um CSV que pode ser aberto em Excel ou carregado em pipelines de dados. Tratamos headers se existirem e strip nos textos para limpar espaços.

Resultado final funcionando

Integração: script único e previsível

Juntando login robusto e extração, temos um script confiável. Estruture em funções: autenticar, navegar para a página alvo, extrair e salvar. Separe credenciais de código (variáveis de ambiente ou secrets manager).

Expor via FastAPI (opcional)

Para quem quer transformar o script em um endpoint acionável, crie um pequeno serviço FastAPI que recebe parâmetros e retorna o caminho do CSV ou o CSV como download. Isso é útil para integrar com pipelines ou tarefas agendadas.

from fastapi import FastAPI, BackgroundTasks, HTTPException
from pydantic import BaseModel
import os

app = FastAPI()

class ExportRequest(BaseModel):
    username: str
    password: str
    url: str

@app.post('/export')
def start_export(req: ExportRequest, background_tasks: BackgroundTasks):
    output = f"output_{req.username}.csv"
    # enfileira a tarefa para não bloquear o servidor
    background_tasks.add_task(run_export_job, req.url, req.username, req.password, output)
    return {"status": "started", "file": output}

def run_export_job(url, username, password, output):
    # Reutilize as funções de login e export
    from playwright.sync_api import sync_playwright
    from your_module import export_table_to_csv  # supondo modularização

    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        page.goto(url)
        page.fill('input[name="username"]', username)
        page.fill('input[name="password"]', password)
        page.click('button[type="submit"]')
        page.wait_for_selector('#relatorio-table', timeout=10000)
        export_table_to_csv(page, '#relatorio-table', output)
        browser.close()

O que acontece: o endpoint retorna imediatamente e executa a exportação em background. Para produção, adicione autenticação e limites de concorrência.

Erros comuns, armadilhas e dicas práticas

Selectors frágeis e mudanças no DOM

Problema: usar seletores por posição ou textos visíveis quebra quando a UI muda. Use seletores por atributos estáveis (data-testid, id, name). Se não houver, negocie com o time de produto para adicionar identificadores ou use XPath com cautela.

Falsos positivos por falta de espera

Problema: script acha que a ação foi completada quando apenas houve um redirecionamento parcial. Use waits em eventos concretos: wait_for_selector para o elemento que indica sucesso, wait_for_load_state('networkidle') se a aplicação carregar assets dinâmicos.

  • Não dependa de sleeps.
  • Use timeouts adequados e tratáveis.
  • Centralize tratamento de erros para reiniciar ou reportar falhas.
Dica prática: simule falhas locais com throttling de rede (devtools) para validar a robustez do script.

Desafio para o leitor praticar

Desafio 1: autenticação com 2FA falsa

Objetivo: adapte o script para lidar com cenários onde, após o login, é apresentada uma segunda etapa (ex.: código enviado por e-mail). Implemente uma função que aguarde um campo de código por 60s e permita injetar o código por variável de ambiente para testes.

Desafio 2: paginação e merges de CSV

Objetivo: a tabela está paginada. Faça o script percorrer todas as páginas, coletar os dados e exportar um CSV unificado. Pense em memória: se houver milhões de linhas, grave em streams ou use chunking.

  1. Identifique o botão "Próximo" com wait_for_selector;
  2. Use loop while para percorrer páginas até botão desativado;
  3. Adicione logging e checkpoint para retomar em caso de falha.

Esses desafios consolidam as boas práticas: esperas, robustez, e pensamento em produção.

Conclusao pratica

Resumo das lições

Automação web confiável exige duas linhas mestras: esperas explícitas e seletores robustos. Evite sleeps, modularize o fluxo (login, navegação, extração, persistência), e trate erros previsíveis com logs e retries controlados.

Próximos passos

Implemente os desafios acima, adicione testes de integração (rodando Playwright em CI com modo headed reduzido) e, se for usar em produção, coloque limites de uso e monitoramento. Automatizar é ótimo, desde que seja previsível e observável.

Checklist prático antes de rodar em produção:

  • Remover credenciais hardcoded — usar variáveis de ambiente;
  • Adicionar timeouts e retry limitado;
  • Registrar outputs e métricas (quantas linhas exportadas, duração, falhas);
  • Validar comportamento com rede lenta e em ambientes diferentes.

Feito isso, você tem um pipeline básico de automação com Playwright que pode ser integrado com ferramentas de data engineering ou exposto via FastAPI para acionamento remoto.

Boa prática final: mantenha scripts pequenos e testáveis. Extraia utilitários comuns (login, esperar por elementos, ler tabela) em módulos reutilizáveis. Isso facilita manutenção e code review — e evita que um script frágil quebre toda a automação.

Assuntos: Python, Testes, automacao, csv

Leia também