Selenium com Python: Automatize o Navegador com o Selenium 4

Cansado de programar?

Conheça a melhor e mais completa formação de Python e Django e sinta-se um programador verdadeiramente competente. Além de Python e Django, você também vai aprender Banco de Dados, SQL, HTML, CSS, Javascript, Bootstrap e muito mais!

Quero aprender Python e Django de Verdade! Quero aprender!
Suporte

Tire suas dúvidas diretamente com o professor

Projetos práticos

Projetos práticos voltados para o mercado de trabalho

Prática profissional

Formação moderna com foco na prática profissional

Resposta rápida

Selenium é a biblioteca que controla um navegador real pelo Python: abre páginas, clica, digita e lê o conteúdo gerado por JavaScript. Instale com pip install selenium, abra com webdriver.Chrome() e localize elementos com driver.find_element(By.CSS_SELECTOR, "...").

1
2
3
4
5
6
7
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com")
print(driver.find_element(By.CLASS_NAME, "author").text)  # Albert Einstein
driver.quit()

Resumo em 30 segundos:

  • Desde o Selenium 4.6, o Selenium Manager baixa o driver sozinho: nada de ChromeDriver no PATH.
  • find_element_by_id() não existe mais: use find_element(By.ID, "...").
  • find_element lança exceção se não achar; find_elements devolve lista vazia.
  • Para conteúdo que demora, use WebDriverWait com expected_conditions, não time.sleep().
  • --headless=new roda o Chrome sem abrir janela.

Salve salve Pythonista!

Tem site que não entrega os dados de bandeja no HTML: o conteúdo só aparece depois que o JavaScript roda, depois de um login ou depois de clicar em “carregar mais”. Para esses casos, você precisa de um navegador de verdade sendo pilotado pelo seu código. É exatamente isso que o Selenium faz.

Neste guia você vai instalar o Selenium 4, entender o que mudou em relação aos tutoriais antigos (spoiler: quase tudo que você encontra por aí com find_element_by_id está quebrado), localizar elementos com By, esperar conteúdo dinâmico do jeito certo, rodar em modo headless, preencher formulários e navegar por várias páginas. No final, uma tabela comparando Selenium e Playwright, os erros mais comuns com a mensagem real e exercícios resolvidos.

Todos os exemplos foram executados com Selenium 4.49.0 (a versão mais recente no PyPI em setembro de 2026) e Chromium 153, contra o site de treino quotes.toscrape.com e uma página local servida com python -m http.server.

Então… Bora pro post! :rocket:

Vá Direto ao Assunto…

O que é o Selenium e quando usar

O Selenium é um projeto open source para automatizar navegadores. Ele implementa o padrão W3C WebDriver: o seu código Python manda comandos (“abra esta URL”, “clique neste botão”) para um driver (o chromedriver, no caso do Chrome), e o driver executa esses comandos no navegador.

Ele nasceu para testes automatizados de aplicações web, mas é muito usado também para:

  • Raspar dados de páginas que dependem de JavaScript.
  • Automatizar tarefas repetitivas: preencher formulários, baixar relatórios, conferir painéis.
  • Tirar screenshots de páginas.

Se a página entrega o conteúdo direto no HTML, você não precisa de navegador: requests + BeautifulSoup é muito mais rápido e leve, como mostramos no post de web scraping com Python e BeautifulSoup. O Selenium entra quando o HTML “cru” não tem o que você procura. Se o assunto é novo para você, comece pelo post o que é web scraping.

Instalação do Selenium 4 e o Selenium Manager

Crie um ambiente virtual (veja como no post sobre virtualenv no Python) e instale a biblioteca:

1
pip install selenium

O Selenium 4.49 exige Python 3.10 ou superior. Você também precisa de um navegador: Chrome, Chromium, Firefox ou Edge.

E o ChromeDriver? Aqui está a maior diferença em relação aos tutoriais antigos. Antes, você tinha que baixar o chromedriver na versão exata do seu Chrome, descompactar e colocar no PATH. Desde o Selenium 4.6, o Selenium Manager vem junto com a biblioteca e faz isso sozinho: descobre a versão do navegador instalado, baixa o driver compatível e guarda em cache em ~/.cache/selenium. A partir do 4.11, ele consegue até baixar o próprio navegador (Chrome, Firefox ou Edge) se não encontrar nenhum.

Na prática, isso basta:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()  # Selenium Manager encontra (ou baixa) o driver
driver.get("https://quotes.toscrape.com")

print(driver.title)

citacao = driver.find_element(By.CLASS_NAME, "quote")
texto = citacao.find_element(By.CLASS_NAME, "text").text
autor = citacao.find_element(By.CSS_SELECTOR, "small.author").text
print(texto)
print("-", autor)

driver.quit()

Saída:

1
2
3
Quotes to Scrape
“The world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.”
- Albert Einstein

Na primeira execução, o Selenium Manager baixou o chromedriver 153 compatível com o Chromium instalado. Nas seguintes, ele usa o cache.

Três pontos importantes do código:

  • driver.get(url) abre a página e espera o carregamento inicial (o evento load).
  • .text devolve o texto visível do elemento.
  • driver.quit() fecha o navegador e encerra o driver. Sem ele, processos do Chrome ficam abertos consumindo memória. Em scripts maiores, coloque o quit() num finally (veja tratamento de exceções no Python).

Se o seu navegador está num caminho fora do padrão, informe com opcoes.binary_location = "/caminho/do/navegador" (veja a seção de headless para como criar opcoes).

Como localizar elementos com By

Todo trabalho com Selenium começa por encontrar o elemento certo. Desde o Selenium 4.3, existe um único par de métodos, find_element() e find_elements(), que recebe como procurar (uma constante de By) e o que procurar. A lista completa está na documentação de localizadores.

Localizador Exemplo Encontra
By.ID find_element(By.ID, "username") <input id="username">
By.NAME find_element(By.NAME, "email") <input name="email">
By.CLASS_NAME find_element(By.CLASS_NAME, "quote") <div class="quote"> (uma classe só)
By.CSS_SELECTOR find_element(By.CSS_SELECTOR, "li.next a") Qualquer seletor CSS
By.XPATH find_element(By.XPATH, "//a[text()='Login']") Qualquer expressão XPath
By.TAG_NAME find_element(By.TAG_NAME, "h1") A primeira tag <h1>
By.LINK_TEXT find_element(By.LINK_TEXT, "(about)") Link com o texto exato
By.PARTIAL_LINK_TEXT find_element(By.PARTIAL_LINK_TEXT, "Next") Link que contém o texto

Na dúvida, prefira By.ID (quando existe, é único) e By.CSS_SELECTOR (resolve quase tudo e é o mesmo seletor que você testa no DevTools do navegador). O By.XPATH é útil para procurar pelo texto ou “subir” na árvore do HTML.

Você também pode chamar find_element a partir de um elemento, e a busca fica restrita a ele. Isso evita misturar dados de itens diferentes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com")

citacoes = driver.find_elements(By.CSS_SELECTOR, "div.quote")
print(f"{len(citacoes)} citações na página")

for citacao in citacoes[:3]:
    autor = citacao.find_element(By.CLASS_NAME, "author").text
    tags = [tag.text for tag in citacao.find_elements(By.CSS_SELECTOR, "a.tag")]
    print(autor, tags)

link_sobre = citacoes[0].find_element(By.LINK_TEXT, "(about)")
print(link_sobre.get_attribute("href"))

print(driver.find_elements(By.ID, "nao-existe"))  # lista vazia, sem erro

driver.quit()

Saída:

1
2
3
4
5
6
10 citações na página
Albert Einstein ['change', 'deep-thoughts', 'thinking', 'world']
J.K. Rowling ['abilities', 'choices']
Albert Einstein ['inspirational', 'life', 'live', 'miracle', 'miracles']
https://quotes.toscrape.com/author/Albert-Einstein
[]

Repare na última linha: find_elements devolve [] quando não encontra nada, enquanto find_element lançaria NoSuchElementException. Use essa diferença a seu favor para testar se algo existe. Para ler atributos, use get_attribute("href"), get_attribute("value") e assim por diante.

Esperas explícitas com WebDriverWait

Esse é o ponto em que a maioria dos scripts com Selenium quebra. O driver.get() espera a página carregar, mas não espera o conteúdo que o JavaScript insere depois. Se você procurar o elemento cedo demais, ganha um NoSuchElementException.

A página quotes.toscrape.com/js-delayed/ simula isso: as citações só aparecem cerca de 10 segundos depois do carregamento. A solução é uma espera explícita: você diz qual condição quer e o tempo máximo, e o Selenium verifica repetidamente (a cada 0,5 s, por padrão) até ela ser verdadeira.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com/js-delayed/")  # as citações aparecem só após ~10 s

inicio = time.perf_counter()
espera = WebDriverWait(driver, timeout=15)
citacoes = espera.until(
    EC.presence_of_all_elements_located((By.CSS_SELECTOR, "div.quote"))
)
print(f"{len(citacoes)} citações após {time.perf_counter() - inicio:.0f} s")
print(citacoes[0].find_element(By.CLASS_NAME, "author").text)

driver.quit()

Saída:

1
2
10 citações após 10 s
Albert Einstein

O until() devolve o que a condição encontrou (aqui, a lista de elementos) e segue assim que ela é satisfeita: o script esperou 10 segundos, não os 15 do limite. Se o tempo acabar, ele lança TimeoutException. Atenção: o localizador vai numa tupla (By.CSS_SELECTOR, "div.quote"), não em dois argumentos.

As condições mais usadas de expected_conditions (lista completa na documentação de esperas):

Condição Espera até que
presence_of_element_located(loc) O elemento exista no HTML (mesmo invisível)
visibility_of_element_located(loc) O elemento exista e esteja visível
element_to_be_clickable(loc) O elemento esteja visível e habilitado para clique
presence_of_all_elements_located(loc) Pelo menos um elemento exista; devolve a lista
text_to_be_present_in_element(loc, texto) O texto do elemento contenha texto
url_contains(trecho) A URL atual contenha o trecho

E o time.sleep()? Funciona, mas é ruim: se você dorme 10 segundos e o conteúdo chega em 2, perdeu 8; se chega em 11, o script quebra. Existe também a espera implícita (driver.implicitly_wait(5)), que vale para todo find_element, mas a documentação oficial recomenda não misturar esperas implícitas e explícitas, porque os tempos se somam de forma imprevisível. Fique com WebDriverWait.

Está curtindo esse conteúdo? :thumbsup:

Que tal receber 30 dias de conteúdo direto na sua Caixa de Entrada?

Sua assinatura não pôde ser validada.
Você fez sua assinatura com sucesso.

Assine as PyDicas e receba 30 dias do melhor conteúdo Python na sua Caixa de Entrada: direto e sem enrolação!

Modo headless e navegação por várias páginas

Em servidor, em CI ou numa tarefa agendada não há tela para abrir janela. O modo headless roda o navegador completo, só que sem interface. Você o ativa com ChromeOptions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from selenium import webdriver
from selenium.webdriver.common.by import By

opcoes = webdriver.ChromeOptions()
opcoes.add_argument("--headless=new")        # roda sem abrir janela
opcoes.add_argument("--window-size=1280,800")

driver = webdriver.Chrome(options=opcoes)
driver.get("https://quotes.toscrape.com")

autores = set()
while True:
    for autor in driver.find_elements(By.CLASS_NAME, "author"):
        autores.add(autor.text)
    proxima = driver.find_elements(By.CSS_SELECTOR, "li.next a")
    if not proxima:          # última página: não existe botão "Next"
        break
    proxima[0].click()

print(f"{len(autores)} autores diferentes")
print(sorted(autores)[:5])
driver.save_screenshot("ultima_pagina.png")
driver.quit()

Saída:

1
2
50 autores diferentes
['Albert Einstein', 'Alexandre Dumas fils', 'Alfred Tennyson', 'Allen Saunders', 'André Gide']

O script percorreu as 10 páginas clicando em “Next” até o botão sumir, juntou os autores num set (que descarta repetidos) e salvou um screenshot da última página. Detalhes que valem ouro:

  • Defina o tamanho da janela no headless. Sites responsivos mudam o layout em telas pequenas e o elemento que você procura pode nem existir na versão mobile.
  • find_elements + if not proxima é o jeito limpo de detectar a última página, sem try/except.
  • Nas versões recentes do Chrome, --headless e --headless=new são equivalentes (o headless antigo foi separado num binário próprio, o chrome-headless-shell). Usar =new deixa a intenção explícita.

Como preencher um formulário com Selenium

Para testar formulário sem depender de site de terceiros, crie um arquivo formulario.html com campos de nome, email, um <select> de curso, um checkbox de termos e um botão que, ao enviar, mostra uma mensagem de confirmação após 2 segundos (simulando a resposta do servidor). Sirva a pasta com:

1
python -m http.server 8000

O script abaixo preenche e envia o formulário:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select, WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("http://localhost:8000/formulario.html")

driver.find_element(By.ID, "nome").send_keys("Maria Silva")
driver.find_element(By.NAME, "email").send_keys("[email protected]")
Select(driver.find_element(By.ID, "curso")).select_by_visible_text("Django")

termos = driver.find_element(By.ID, "termos")
if not termos.is_selected():
    termos.click()

driver.find_element(By.CSS_SELECTOR, "button[type='submit']").click()

espera = WebDriverWait(driver, 5)
espera.until(EC.text_to_be_present_in_element((By.ID, "resultado"), "confirmado"))
print(driver.find_element(By.ID, "resultado").text)

driver.quit()

Saída:

1
Cadastro de Maria Silva no curso django confirmado!

O que cada peça faz:

  • send_keys("texto") digita no campo, como um usuário. Para limpar antes, use .clear().
  • Select(...) é o auxiliar para <select>: select_by_visible_text(), select_by_value() e select_by_index().
  • is_selected() diz se um checkbox ou radio está marcado; .click() alterna.
  • A espera final é essencial: a mensagem aparece 2 segundos depois do clique.

Você também pode enviar um formulário apertando Enter, com Keys. Exemplo com a página de login de treino do quotes.toscrape.com, que aceita qualquer usuário e senha:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com/login")

driver.find_element(By.ID, "username").send_keys("pythonista")
campo_senha = driver.find_element(By.ID, "password")
campo_senha.send_keys("qualquer-senha")
campo_senha.send_keys(Keys.ENTER)  # o mesmo que apertar Enter no teclado

print(driver.current_url)
print(driver.find_element(By.PARTIAL_LINK_TEXT, "Logout").text)
driver.quit()

Saída:

1
2
https://quotes.toscrape.com/
Logout

Depois do login, o site redirecionou para a home e o link “Login” virou “Logout”. Um cuidado ético e legal: automatize logins só em contas suas e respeite os termos de uso e o robots.txt dos sites.

Antes de comparar ferramentas, vale lembrar: automação boa começa com Python bem aprendido - funções, estruturas de dados, tratamento de erros. Essa base, com projetos guiados, é o que você constrói na Jornada Python:

Selenium vs Playwright

O Playwright, da Microsoft, é o principal concorrente do Selenium e já apareceu aqui no blog no post de web scraping com IA usando LangChain e Playwright. Os dois controlam navegadores reais; a diferença está no protocolo e na ergonomia.

Critério Selenium 4 Playwright
Protocolo W3C WebDriver (padrão aberto), com suporte a WebDriver BiDi Protocolo próprio sobre CDP e equivalentes
Instalação do navegador Usa o navegador instalado; Selenium Manager baixa driver (e navegador, se preciso) playwright install baixa versões próprias de Chromium, Firefox e WebKit
Esperas Explícitas, com WebDriverWait Automáticas em cada ação (auto-wait)
Navegadores Chrome, Firefox, Edge, Safari real Chromium, Firefox, WebKit (motor do Safari)
API assíncrona Não (só síncrona) Síncrona e async
Linguagens Python, Java, C#, Ruby, JavaScript, Kotlin Python, JavaScript/TypeScript, Java, .NET
Execução distribuída Selenium Grid Paralelismo nativo no test runner
Melhor para Suítes de teste existentes, Grid, Safari real, padrão W3C Projetos novos de scraping e testes, sites muito dinâmicos

Resumo prático: começando do zero um scraper de site dinâmico, o Playwright costuma dar menos trabalho por causa do auto-wait. Se a empresa já tem testes em Selenium, precisa de Grid ou de testar no Safari de verdade, fique com o Selenium. E, se a página não depende de JavaScript, nenhum dos dois: use requests + BeautifulSoup.

Erros comuns

Estes são os erros que mais aparecem com Selenium, com a última linha real da mensagem (a linha Session info e a URL da documentação que o Selenium anexa foram omitidas).

Usar a API antiga do Selenium 3 (AttributeError)

1
campo = driver.find_element_by_id("username")
1
AttributeError: 'WebDriver' object has no attribute 'find_element_by_id'

Correção: os métodos find_element_by_* foram removidos no Selenium 4.3. Use driver.find_element(By.ID, "username") com from selenium.webdriver.common.by import By.

Procurar o elemento antes de o JavaScript criá-lo (NoSuchElementException)

1
2
driver.get("https://quotes.toscrape.com/js-delayed/")
citacao = driver.find_element(By.CLASS_NAME, "quote")
1
selenium.common.exceptions.NoSuchElementException: Message: no such element: Unable to locate element: {"method":"css selector","selector":".quote"}

Correção: use WebDriverWait(driver, 15).until(EC.presence_of_element_located((By.CLASS_NAME, "quote"))). Se o erro continua mesmo com espera, confira o seletor no DevTools e verifique se o elemento está dentro de um <iframe> (nesse caso, use driver.switch_to.frame(...) antes).

A espera explícita estoura o tempo (TimeoutException)

1
WebDriverWait(driver, 3).until(EC.element_to_be_clickable((By.ID, "botao-que-nao-existe")))
1
selenium.common.exceptions.TimeoutException: Message: 

Correção: a mensagem vem vazia, então o problema é o localizador ou a condição. Confira o seletor, aumente o tempo se o site for lento e passe uma mensagem própria: until(condicao, message="Botão de enviar não apareceu").

Usar um elemento de uma página que já mudou (StaleElementReferenceException)

1
2
3
proxima = driver.find_element(By.CSS_SELECTOR, "li.next a")
proxima.click()          # a página 2 carrega
proxima.click()          # o elemento pertencia à página 1
1
selenium.common.exceptions.StaleElementReferenceException: Message: stale element reference: stale element not found

Correção: uma referência de elemento vale só para o HTML em que foi encontrada. Depois de navegar ou de o JavaScript redesenhar a página, busque o elemento de novo, como no loop de paginação acima, que chama find_elements a cada volta.

Chromium instalado via snap não inicia (SessionNotCreatedException)

1
2
3
opcoes = webdriver.ChromeOptions()
opcoes.binary_location = "/usr/bin/chromium-browser"  # Chromium do snap (Ubuntu)
driver = webdriver.Chrome(options=opcoes)
1
selenium.common.exceptions.SessionNotCreatedException: Message: session not created: DevToolsActivePort file doesn't exist

Correção: esse erro aparece quando o navegador fecha logo ao abrir. No Ubuntu com Chromium do snap, o confinamento impede o navegador de usar o perfil temporário que o driver cria em /tmp. Aponte um perfil dentro da pasta do snap: opcoes.add_argument("--user-data-dir=/home/SEU_USUARIO/snap/chromium/common/selenium-profile"). Foi assim que os exemplos deste post rodaram. Em servidores Linux rodando como root (Docker), o erro costuma se resolver com --headless=new e --no-sandbox.

Exercícios resolvidos

Tente resolver cada exercício antes de abrir a solução. Todos os códigos foram executados e as saídas conferidas. Os exercícios 5 e 6 usam o formulario.html servido localmente.

Exercício 1. Abra https://quotes.toscrape.com e imprima o título da aba e o texto do primeiro <h1> da página.

Ver solução
1
2
3
4
5
6
7
8
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com")
print(driver.title)
print(driver.find_element(By.TAG_NAME, "h1").text)
driver.quit()

Saída:

1
2
Quotes to Scrape
Quotes to Scrape

driver.title lê a tag <title>; By.TAG_NAME pega a primeira tag com aquele nome. Aqui os dois textos coincidem.

Exercício 2. Na home do quotes.toscrape.com há uma caixa “Top Ten tags”. Monte uma lista com o texto dessas 10 tags.

Ver solução
1
2
3
4
5
6
7
8
9
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com")
tags = driver.find_elements(By.CSS_SELECTOR, "span.tag-item a")
print(len(tags))
print([tag.text for tag in tags])
driver.quit()

Saída:

1
2
10
['love', 'inspirational', 'life', 'humor', 'books', 'reading', 'friendship', 'friends', 'truth', 'simile']

Cada tag do ranking fica num <span class="tag-item">. O seletor CSS span.tag-item a pega o link dentro de cada um, e a list comprehension extrai o texto.

Exercício 3. Clique no link “(about)” da primeira citação e imprima o nome do autor e a data de nascimento.

Ver solução
1
2
3
4
5
6
7
8
9
10
11
12
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com")
primeira = driver.find_element(By.CLASS_NAME, "quote")
primeira.find_element(By.LINK_TEXT, "(about)").click()

nome = driver.find_element(By.CLASS_NAME, "author-title").text
nascimento = driver.find_element(By.CLASS_NAME, "author-born-date").text
print(nome, "nasceu em", nascimento)
driver.quit()

Saída: Albert Einstein nasceu em March 14, 1879

Buscar o link a partir de primeira garante que clicamos no “(about)” da primeira citação, e não em qualquer um da página.

Exercício 4. Conte quantas citações existem na tag “humor” (https://quotes.toscrape.com/tag/humor/), somando todas as páginas.

Ver solução
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
driver.get("https://quotes.toscrape.com/tag/humor/")

total = 0
paginas = 0
while True:
    paginas += 1
    total += len(driver.find_elements(By.CLASS_NAME, "quote"))
    proxima = driver.find_elements(By.CSS_SELECTOR, "li.next a")
    if not proxima:
        break
    proxima[0].click()

print(f"{total} citações de humor em {paginas} páginas")
driver.quit()

Saída: 12 citações de humor em 2 páginas

É o mesmo padrão de paginação do post: processa a página, procura o “Next” com find_elements e para quando a lista vem vazia.

Exercício 5. No formulario.html, digite um nome, clique em Enviar sem marcar os termos e imprima a mensagem que aparece.

Ver solução
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver = webdriver.Chrome()
driver.get("http://localhost:8000/formulario.html")

driver.find_element(By.ID, "nome").send_keys("João")
driver.find_element(By.ID, "enviar").click()  # sem marcar os termos

resultado = (By.ID, "resultado")
WebDriverWait(driver, 5).until(EC.text_to_be_present_in_element(resultado, "termos"))
print(driver.find_element(*resultado).text)
driver.quit()

Saída: Você precisa aceitar os termos.

Guardar o localizador numa tupla evita repetição: ela vai direto para a condição e, desempacotada com *, para o find_element.

Exercício 6. Em modo headless, colete autor e texto das citações das páginas 1 e 2 (/page/1/ e /page/2/) e salve num arquivo citacoes.csv.

Ver solução
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import csv
from selenium import webdriver
from selenium.webdriver.common.by import By

opcoes = webdriver.ChromeOptions()
opcoes.add_argument("--headless=new")
driver = webdriver.Chrome(options=opcoes)

linhas = []
for pagina in (1, 2):
    driver.get(f"https://quotes.toscrape.com/page/{pagina}/")
    for citacao in driver.find_elements(By.CLASS_NAME, "quote"):
        linhas.append({
            "autor": citacao.find_element(By.CLASS_NAME, "author").text,
            "texto": citacao.find_element(By.CLASS_NAME, "text").text,
        })
driver.quit()

with open("citacoes.csv", "w", newline="", encoding="utf-8") as arquivo:
    escritor = csv.DictWriter(arquivo, fieldnames=["autor", "texto"])
    escritor.writeheader()
    escritor.writerows(linhas)

print(len(linhas), "linhas salvas")
print(linhas[-1]["autor"])

Saída:

1
2
20 linhas salvas
Allen Saunders

Quando a URL das páginas é previsível, navegar direto com driver.get() é mais simples que clicar em “Next”. O DictWriter é explicado no post sobre manipulação de CSV no Python.

Exercício 7 (questão de prova). No Selenium 4, qual comando localiza corretamente o elemento <input id="email">?

a) driver.find_element_by_id("email")
b) driver.find_element(By.ID, "email")
c) driver.get_element("id", "email")
d) driver.find(By.ID, "email")

Ver solução

Resposta: b. A alternativa a era a sintaxe do Selenium 3 e foi removida na versão 4.3 (gera AttributeError). Os métodos get_element e find não existem no WebDriver.

Exercício 8 (questão de prova). Um script executa driver.find_elements(By.CLASS_NAME, "produto") numa página que não tem nenhum elemento com essa classe. O que acontece?

a) Lança NoSuchElementException
b) Devolve None
c) Devolve uma lista vazia
d) Espera até o elemento aparecer

Ver solução
1
print(driver.find_elements(By.CLASS_NAME, "produto"))

Saída: []

Resposta: c. find_elements (no plural) devolve uma lista, vazia se nada casar. Quem lança NoSuchElementException é find_element (no singular). E nenhum dos dois espera por conta própria: para isso existe o WebDriverWait.

Quer praticar Python com projetos guiados, do básico à automação? Conheça o nosso curso de Python completo, a Jornada Python. E, para treinar a base, veja a nossa lista de exercícios de Python.

Conclusão

Neste guia de Selenium com Python, você aprendeu:

✅ Selenium Manager - O driver é baixado sozinho desde o Selenium 4.6
✅ By - find_element(By.X, "...") substitui os antigos find_element_by_*
✅ WebDriverWait - Esperar conteúdo dinâmico sem time.sleep()
✅ Headless - Rodar sem janela, com tamanho de tela definido
✅ Formulários - send_keys, Select, checkbox e Keys.ENTER
✅ Selenium vs Playwright - Quando escolher cada um

Se a página não depende de JavaScript, volte para o BeautifulSoup; se depende e você está começando do zero, compare com o Playwright. E, para ir além, a documentação oficial do WebDriver tem exemplos de ações de mouse, múltiplas abas, iframes e alertas.

Se ficou com alguma dúvida, fique à vontade para deixar um comentário no box aqui embaixo! Será um prazer te responder! :wink:

Perguntas frequentes

O que é o Selenium no Python?

Selenium é uma biblioteca que controla um navegador de verdade (Chrome, Firefox, Edge ou Safari) a partir do código. Com ela você abre páginas, clica, digita, preenche formulários e lê o conteúdo renderizado, inclusive o que é gerado por JavaScript. É muito usada em testes automatizados de sites e em automação de tarefas repetitivas na web.

Preciso baixar o ChromeDriver para usar o Selenium?

Não, desde o Selenium 4.6. O Selenium Manager, que vem junto com a biblioteca, descobre a versão do seu navegador, baixa o driver compatível e guarda em cache (em ~/.cache/selenium). Basta pip install selenium e webdriver.Chrome(). Só configure o driver na mão em ambientes sem internet ou com navegadores instalados de forma especial.

Por que find_element_by_id não funciona mais?

Porque os métodos find_element_by_* foram removidos no Selenium 4.3. O jeito atual é driver.find_element(By.ID, 'valor'), importando from selenium.webdriver.common.by import By. O mesmo vale para By.NAME, By.CLASS_NAME, By.CSS_SELECTOR, By.XPATH, By.TAG_NAME, By.LINK_TEXT e By.PARTIAL_LINK_TEXT. Código antigo gera AttributeError: 'WebDriver' object has no attribute 'find_element_by_id'.

Qual a diferença entre find_element e find_elements no Selenium?

find_element devolve o primeiro elemento que casa com o localizador e lança NoSuchElementException se não encontrar nada. find_elements devolve uma lista com todos os elementos encontrados e, se não houver nenhum, devolve uma lista vazia sem erro. Por isso find_elements é útil para testar se algo existe, como o botão de próxima página.

Como esperar um elemento aparecer no Selenium?

Use espera explícita: WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, 'resultado'))), importando WebDriverWait de selenium.webdriver.support.ui e expected_conditions as EC de selenium.webdriver.support. O código segue assim que a condição é verdadeira ou lança TimeoutException depois de 10 segundos. Evite time.sleep() fixo, que é lento e frágil.

Como rodar o Selenium sem abrir a janela do navegador?

Use o modo headless: crie opcoes = webdriver.ChromeOptions(), chame opcoes.add_argument('--headless=new') e passe webdriver.Chrome(options=opcoes). O navegador roda normalmente, só que sem interface gráfica, o que é ideal para servidores, CI e tarefas agendadas. Para o Firefox, use FirefoxOptions com add_argument('-headless').

Selenium ou Playwright: qual escolher?

Para projetos novos de automação e scraping em Python, o Playwright costuma ser mais simples, porque espera os elementos automaticamente e já instala os navegadores. O Selenium é a escolha quando você precisa do padrão W3C WebDriver, de Selenium Grid, de Safari real ou de compatibilidade com uma suíte de testes existente em várias linguagens. Os dois controlam navegadores reais.

Em uma questão de prova, qual comando localiza corretamente um elemento pelo id no Selenium 4?

É driver.find_element(By.ID, 'id_do_elemento'). A forma driver.find_element_by_id('id_do_elemento') era a do Selenium 3 e foi removida. Não existem os métodos get_element nem find no WebDriver. Lembre de importar By com from selenium.webdriver.common.by import By.

Começe agora sua Jornada na Programação!

Não deixe para amanhã o sucesso que você pode começar a construir hoje!

#newsletter Olá :wave: Curtiu o artigo? Então faça parte da nossa Newsletter! Privacidade Não se preocupe, respeitamos sua privacidade. Você pode se descadastrar a qualquer momento.