Quando um site não tem API e os dados são carregados dinamicamente via JavaScript, Selenium controla um navegador real — clica, preenche formulários, espera carregamentos. É a ferramenta certa para automação de portais corporativos, extração de relatórios de sistemas legados e scraping de páginas dinâmicas.

Instalação

pip install selenium webdriver-manager

webdriver-manager baixa e gerencia automaticamente o ChromeDriver compatível com a versão do Chrome instalada.

Configurando o driver

from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from webdriver_manager.chrome import ChromeDriverManager

# Modo visível (para desenvolvimento):
driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

# Modo headless (sem abrir janela — para produção/servidor):
options = webdriver.ChromeOptions()
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
driver = webdriver.Chrome(
    service=Service(ChromeDriverManager().install()),
    options=options
)

Navegação e localização de elementos

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver.get("https://sistema.empresa.com/relatorios")

# Localizar elementos:
campo_login = driver.find_element(By.ID, "username")
campo_senha = driver.find_element(By.NAME, "password")
botao = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")

# Por XPath (mais poderoso, menos frágil quando bem escrito):
tabela = driver.find_element(By.XPATH, "//table[@class='relatorio']")

# Espera explícita (mais confiável que time.sleep):
wait = WebDriverWait(driver, timeout=10)
tabela = wait.until(
    EC.presence_of_element_located((By.ID, "tabelaVendas"))
)

Preenchendo formulários e clicando

from selenium.webdriver.common.keys import Keys

# Limpar + preencher campo:
campo_login.clear()
campo_login.send_keys("meu_usuario")
campo_senha.send_keys("minha_senha")
botao.click()

# Selecionar data e pressionar Enter:
campo_data = driver.find_element(By.ID, "data_inicio")
campo_data.clear()
campo_data.send_keys("01/01/2026")
campo_data.send_keys(Keys.RETURN)

# Select (dropdown):
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element(By.ID, "regiao"))
select.select_by_visible_text("Sul")

Extraindo dados de uma tabela

import pandas as pd

# Após navegar até a página com a tabela:
tabela = driver.find_element(By.TAG_NAME, "table")

# Extrair cabeçalhos:
headers = [th.text for th in tabela.find_elements(By.TAG_NAME, "th")]

# Extrair linhas:
linhas = []
for tr in tabela.find_elements(By.CSS_SELECTOR, "tbody tr"):
    cells = [td.text for td in tr.find_elements(By.TAG_NAME, "td")]
    if cells:
        linhas.append(cells)

df = pd.DataFrame(linhas, columns=headers)
df.to_excel("relatorio_extraido.xlsx", index=False)

Boas práticas

try:
    driver.get("https://sistema.empresa.com")
    # ... automação ...
except Exception as e:
    driver.save_screenshot("erro_captura.png")  # print da tela no erro
    raise
finally:
    driver.quit()   # SEMPRE fechar o driver

Perguntas frequentes

time.sleep() vs WebDriverWait: qual usar?

Sempre prefira WebDriverWait. O sleep espera um tempo fixo — lento em páginas rápidas, falho em páginas lentas. WebDriverWait espera até que a condição seja verdadeira (elemento aparece, texto muda, URL muda) e para assim que acontecer.

Meu script quebra quando o site muda o layout. Como tornar mais estável?

Prefira localizadores baseados em IDs e atributos de dados (data-testid, data-id) a classes CSS e posição XPath. Eles tendem a ser mais estáveis durante redesigns. Evite XPath absolutos como /html/body/div[2]/div[1]/table.