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.txte 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
sleepfixo 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-testidquando 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
requestsresolve (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.
