Artigo

Automação de Navegador com Playwright em Python

Automação de Navegador com Playwright em Python
Tempo de leitura9 minutos

Automatizar o navegador deixou de ser sinônimo de instabilidade. O Playwright, biblioteca da Microsoft, trouxe espera inteligente, seletores robustos e suporte a Chromium, Firefox e WebKit — com uma API em Python muito agradável. Neste guia mostro como automatizar tarefas reais: preencher formulários, extrair dados, tirar screenshots e baixar arquivos.

Vou usar exemplos práticos que servem tanto para scraping quanto para testes de ponta a ponta.

Playwright vs. alternativas

  • Espera automática: aguarda elementos ficarem prontos, reduzindo falhas.
  • Multi-navegador: mesmo código para Chromium, Firefox e WebKit.
  • Rápido: comunicação via protocolo, sem depender de WebDriver externo.
  • Ferramentas: gravação de ações, trace viewer e screenshots.

Instalando

python -m venv .venv && source .venv/bin/activate
pip install playwright
playwright install   # baixa os navegadores

O primeiro script

from playwright.sync_api import sync_playwright

with sync_playwright() as p:
    browser = p.chromium.launch(headless=True)
    page = browser.new_page()
    page.goto("https://example.com")
    print(page.title())
    browser.close()

Use headless=False durante o desenvolvimento para ver o que está acontecendo.

Seletores robustos

Evite seletores frágeis como div > div > span. Prefira papéis e texto, que resistem a mudanças de layout:

page.get_by_role("button", name="Entrar").click()
page.get_by_label("E-mail").fill("usuario@exemplo.com")
page.get_by_text("Bem-vindo").wait_for()

Espera inteligente

Não use sleep fixo. Espere por condições:

page.goto("https://exemplo.com/dados")
page.wait_for_selector("#tabela-carregada")
page.wait_for_load_state("networkidle")

Extraindo dados

page.goto("https://exemplo.com/produtos")
itens = page.locator(".produto")
dados = []
for i in range(itens.count()):
    el = itens.nth(i)
    dados.append({
        "nome": el.locator(".nome").inner_text(),
        "preco": el.locator(".preco").inner_text(),
    })
print(dados)

Preenchendo formulários

page.get_by_label("Nome").fill("Ana")
page.get_by_label("E-mail").fill("ana@exemplo.com")
page.get_by_role("combobox", name="Estado").select_option("SP")
page.get_by_role("button", name="Enviar").click()
page.get_by_text("Cadastro realizado").wait_for()

Screenshots e PDF

page.screenshot(path="pagina.png", full_page=True)
page.pdf(path="pagina.pdf", format="A4")  # apenas Chromium

Downloads

with page.expect_download() as info:
    page.get_by_text("Baixar relatorio").click()
download = info.value
download.save_as("relatorio.csv")

Interceptando requisições

Você pode bloquear imagens para acelerar, ou observar as chamadas de API da página:

page.route("**/*.{png,jpg,jpeg}", lambda route: route.abort())

def logar(resp):
    print(resp.status, resp.url)
page.on("response", logar)

Modo assíncrono

Para muitas páginas em paralelo, a versão async é mais eficiente:

import asyncio
from playwright.async_api import async_playwright

async def baixar(p, url):
    browser = await p.chromium.launch()
    page = await browser.new_page()
    await page.goto(url)
    titulo = await page.title()
    await browser.close()
    return titulo

async def main():
    async with async_playwright() as p:
        tarefas = [baixar(p, f"https://exemplo.com/{i}") for i in range(5)]
        print(await asyncio.gather(*tarefas))

asyncio.run(main())

Boas práticas e ética

  • Respeite robots.txt e os termos de uso do site.
  • Adicione pausas entre requisições — não sobrecarregue o servidor.
  • Identifique-se quando possível (user-agent honesto).
  • Não contorne autenticação nem captchas de forma abusiva.
  • Prefira APIs oficiais quando existirem.

Trace viewer para depurar

Quando um fluxo falha de forma intermitente, o trace viewer é o melhor amigo. Ele grava cada passo da execução — cliques, esperas, requisições — e permite revisar depois.

context = browser.new_context()
context.tracing.start(screenshots=True, snapshots=True)
page.goto("https://exemplo.com")
context.tracing.stop(path="trace.zip")
playwright show-trace trace.zip

Executando testes em paralelo

Para suítes grandes, o plugin de pytest do Playwright roda vários navegadores ao mesmo tempo, reduzindo o tempo total.

pytest --numprocesses auto

Reutilizando estado autenticado

Fazer login em cada teste é lento. Salve o estado da sessão uma vez e reutilize:

context = browser.new_context(storage_state="auth.json")
# ... apos o login em um teste dedicado:
context.storage_state(path="auth.json")

Erros comuns

  • Usar sleep fixo em vez de espera por condição.
  • Seletores frágeis que quebram a cada mudança de layout.
  • Esquecer de fechar o navegador, vazando recursos.
  • Rodar em modo headless sem inspecionar erros no desenvolvimento.
  • Ignorar limites e sobrecarregar o site alvo.
  • Refazer login em todo teste sem reutilizar estado.

Automatizando com formulários reais

Um caso de uso comum é automatizar tarefas repetitivas em painéis internos: emitir relatórios, atualizar cadastros, baixar notas. O padrão é sempre o mesmo — navegar, preencher, confirmar e verificar.

def emitir_relatorio(page, competencia: str):
    page.goto("https://painel.interno.com/relatorios")
    page.get_by_label("Competencia").fill(competencia)
    page.get_by_role("button", name="Gerar").click()
    with page.expect_download() as info:
        page.get_by_role("link", name="Baixar PDF").click()
    info.value.save_as(f"relatorio_{competencia}.pdf")

Capturando dados que carregam via JavaScript

Muitos sites carregam conteúdo depois do HTML inicial. Em vez de raspar o DOM, muitas vezes é mais confiável interceptar a resposta da API interna do site.

dados = []

def capturar(resp):
    if "/api/produtos" in resp.url:
        dados.append(resp.json())

page.on("response", capturar)
page.goto("https://loja.com/produtos")
page.wait_for_timeout(2000)
print(len(dados[0]["itens"]))

Esperando por múltiplas abas

with page.context.expect_page() as nova:
    page.get_by_text("Abrir em nova aba").click()
outra = nova.value
outra.wait_for_load_state()
print(outra.title())

Playwright para gerar dados de teste

Além de automação, o Playwright é ótimo para capturar dados reais e transformá-los em fixtures de teste. Você navega uma vez e reutiliza os dados em testes rápidos.

import json

page.goto("https://exemplo.com/produtos")
dados = [el.inner_text() for el in page.locator(".produto .nome").all()]
open("tests/fixtures/produtos.json", "w", encoding="utf-8").write(
    json.dumps(dados, ensure_ascii=False)
)

Rodando em container

Em CI, o Playwright precisa dos navegadores e dependências do sistema. A imagem oficial resolve isso.

FROM mcr.microsoft.com/playwright/python:v1.47.0-jammy
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["pytest", "-q"]

Boas práticas de manutenção

  • Centralize seletores em um arquivo de página (Page Object).
  • Use data-testid quando o site for seu.
  • Mantenha os testes independentes entre si.
  • Limpe dados criados pelos testes.
  • Rode os mais críticos a cada commit.

Quando NÃO usar Playwright

  • Quando existe uma API oficial — use a API, é mais rápida e estável.
  • Quando um simples requests resolve (HTML estático).
  • Quando o site proíbe automação nos termos de uso.
  • Quando o volume é tão alto que sobrecarrega o servidor.

Conclusão e próximos passos

Playwright é a ferramenta certa quando o conteúdo depende de JavaScript, login ou interação. Comece com um script pequeno, evolua para Page Objects e só então paralelize. Feito com responsabilidade, resolve o que requests sozinho não consegue.

Monitorando mudanças em sites

Um uso muito prático é vigiar páginas e avisar quando algo mudar: preço, disponibilidade, edital. O Playwright captura o conteúdo renderizado e você compara com a última leitura.

import hashlib, json, pathlib

def vigiar(url: str, seletor: str, chave: str):
    page.goto(url)
    conteudo = page.locator(seletor).inner_text()
    h = hashlib.sha256(conteudo.encode()).hexdigest()
    caminho = pathlib.Path(f"estado/{chave}.json")
    anterior = json.loads(caminho.read_text())["hash"] if caminho.exists() else None
    caminho.parent.mkdir(exist_ok=True)
    caminho.write_text(json.dumps({"hash": h}))
    return h != anterior

Tratando páginas com carregamento infinito

Sites modernos carregam mais itens conforme você rola. Faça scroll até estabilizar antes de extrair.

anterior = 0
while True:
    page.mouse.wheel(0, 3000)
    page.wait_for_timeout(800)
    atual = page.locator(".produto").count()
    if atual == anterior:
        break
    anterior = atual

Lidando com consentimento de cookies

Muitos sites bloqueiam conteúdo até aceitar cookies. Trate isso logo após abrir a página.

try:
    page.get_by_role("button", name="Aceitar").click(timeout=3000)
except Exception:
    pass  # banner pode nao existir

Playwright no CI sem flakiness

Testes de navegador têm fama de instáveis. Três medidas reduzem muito o problema: esperar por condições em vez de tempo, usar seletores por papel/texto e rodar com retry apenas para falhas de rede.

- run: pytest --reruns 1 --reruns-delay 2

Conclusão geral

Playwright resolve o que requests não alcança: páginas com JavaScript, login e interação. Use com responsabilidade, prefira APIs quando existirem e estruture os testes em Page Objects. É uma das ferramentas mais versáteis do Python moderno para automação.

Extraindo tabelas e convertendo para DataFrame

import pandas as pd

page.goto("https://exemplo.com/ranking")
html = page.locator("table").inner_html()
tabela = pd.read_html("" + html + "
")[0] print(tabela.head())

Preenchendo formulários com dados em lote

import csv

with open("contatos.csv", encoding="utf-8") as f:
    for linha in csv.DictReader(f):
        page.goto("https://exemplo.com/cadastro")
        page.get_by_label("Nome").fill(linha["nome"])
        page.get_by_label("Email").fill(linha["email"])
        page.get_by_role("button", name="Salvar").click()
        page.get_by_text("Cadastro salvo").wait_for()

Integrando com um pipeline de conteúdo

Um uso avançado é capturar conteúdo renderizado e alimentar uma automação de publicação ou relatório. O Playwright garante que você pegue o estado final da página, após o JavaScript rodar.

def extrair_e_salvar(url: str, seletor: str, destino: str):
    page.goto(url)
    page.wait_for_selector(seletor)
    conteudo = page.locator(seletor).inner_text()
    with open(destino, "w", encoding="utf-8") as f:
        f.write(conteudo)
    return destino

Resumo

Playwright trouxe maturidade para automação de navegador: espera inteligente, seletores legíveis e multi-navegador. Comece com um script simples, use seletores por papel/texto e espere por condições em vez de tempo fixo. Feito com responsabilidade, é uma ferramenta poderosa para scraping e testes ponta a ponta.

Anterior e próximo

Leituras recentes

Temas parecidos para continuar navegando no blog.

Conteúdo criado para leitura limpa, boa hierarquia visual e navegação clara.