Selenium with Python: Complete Web Automation Guide

Published on: July 10, 2026
Reading time: 5 minutes
Automação web com Selenium usando Python

Selenium with Python allows a program to control a real web browser. A script can open pages, click buttons, fill forms, read visible content, take screenshots, and verify that an application behaves correctly. These abilities make Selenium useful for browser testing, repetitive administrative tasks, and workflows that cannot be completed through a normal API.

This guide starts with installation and continues through element locators, explicit waits, forms, multiple windows, screenshots, error handling, and maintainable project structure. Before automating a website, confirm that the activity is permitted by its terms and that the script will not overload the service.

What Selenium does

Selenium WebDriver sends commands to Chrome, Firefox, Edge, or another supported browser. Unlike the Python Requests library, which communicates directly over HTTP, Selenium renders the page and executes JavaScript. This means it can interact with dynamic interfaces, client-side forms, dialogs, and elements that appear after the initial page load.

The official Selenium documentation describes WebDriver, element interaction, browser options, waits, and testing patterns. Use it as the primary reference when an API changes.

When Selenium is the right tool

Selenium is a good choice for:

  • end-to-end testing of web applications;
  • checking login, checkout, search, and account workflows;
  • automating internal systems that have no API;
  • capturing screenshots after specific interactions;
  • reproducing browser bugs consistently.

It is usually not the best choice for downloading a simple public page. For static HTML, Requests and an HTML parser are faster and use fewer resources. Selenium should also not be used to bypass authentication, rate limits, CAPTCHAs, or access controls.

Create a virtual environment and install Selenium

Start with an isolated environment, as explained in the Python venv guide:

python -m venv .venv

Activate it and install Selenium:

python -m pip install selenium

Modern Selenium versions include Selenium Manager, which can locate or download a compatible browser driver automatically. Keep the browser updated and consult the official documentation when a company-managed computer prevents automatic driver setup.

Open a browser

The following script launches Chrome, opens a page, prints its title, and closes the browser even if an error occurs:

from selenium import webdriver


def main():
    driver = webdriver.Chrome()
    try:
        driver.get("https://example.com")
        print(driver.title)
    finally:
        driver.quit()


if __name__ == "__main__":
    main()

quit() closes every window and ends the WebDriver process. Place it in finally so the cleanup still runs when a locator or assertion fails. The difference between finally and ordinary exception handlers is covered in our Python try and except guide.

Run Chrome in headless mode

A headless browser does not display a visible window. It is useful on servers and in continuous integration:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument("--headless=new")
options.add_argument("--window-size=1440,1000")

driver = webdriver.Chrome(options=options)

Always test the workflow in visible mode first. Headless execution can expose layout differences, so define a realistic window size.

Locate elements reliably

WebDriver finds elements through strategies from the By class:

from selenium.webdriver.common.by import By

heading = driver.find_element(By.TAG_NAME, "h1")
email = driver.find_element(By.ID, "email")
submit = driver.find_element(By.CSS_SELECTOR, "button[type='submit']")

Preferred locator order is generally:

  1. a stable, unique ID;
  2. a dedicated test attribute such as data-testid;
  3. a concise CSS selector based on stable structure;
  4. XPath when text or a complex relationship is required.

Avoid selectors based on generated class names or deeply nested positions. They break when the page design changes.

Wait for dynamic elements

A common beginner mistake is adding fixed delays everywhere:

import time
time.sleep(5)

This is either too slow or unreliable. Use an explicit wait that stops as soon as the expected condition is true:

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

wait = WebDriverWait(driver, 10)
login_button = wait.until(
    EC.element_to_be_clickable((By.ID, "login-button"))
)
login_button.click()

The official waits documentation explains implicit and explicit waits. Avoid mixing them without a clear reason because combined timeout behavior can be confusing.

Fill and submit a form

Use send_keys() for text fields and click() for buttons:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select

name = driver.find_element(By.ID, "name")
name.clear()
name.send_keys("Taylor")

country = Select(driver.find_element(By.ID, "country"))
country.select_by_visible_text("Canada")

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

Do not place real credentials directly in the script. Read secrets from environment variables, keep them outside version control, and use a dedicated test account with limited permissions.

Verify a result with assertions

Automation becomes a test when it checks an expected result:

message = wait.until(
    EC.visibility_of_element_located((By.CLASS_NAME, "success-message"))
)
assert "saved" in message.text.lower()

For a larger test suite, combine Selenium with Pytest. Our Pytest beginner guide explains test discovery, assertions, fixtures, and useful commands.

Use Selenium with Pytest fixtures

import pytest
from selenium import webdriver


@pytest.fixture
def driver():
    browser = webdriver.Chrome()
    browser.set_window_size(1440, 1000)
    yield browser
    browser.quit()


def test_home_page_title(driver):
    driver.get("https://example.com")
    assert "Example" in driver.title

The fixture creates a fresh browser for each test and guarantees cleanup after the test. Teams may choose a broader fixture scope for performance, but isolated sessions reduce hidden dependencies.

Handle alerts, frames, and windows

JavaScript alerts

alert = wait.until(EC.alert_is_present())
print(alert.text)
alert.accept()

Frames

wait.until(EC.frame_to_be_available_and_switch_to_it((By.ID, "payment-frame")))
# Interact inside the frame.
driver.switch_to.default_content()

New tabs or windows

original = driver.current_window_handle

# Click an element that opens another tab.
wait.until(EC.number_of_windows_to_be(2))
new_window = next(handle for handle in driver.window_handles if handle != original)
driver.switch_to.window(new_window)

Always switch back to the intended context before locating the next element.

Take screenshots and save page evidence

from pathlib import Path

output = Path("artifacts")
output.mkdir(exist_ok=True)
driver.save_screenshot(output / "checkout-error.png")

The Python pathlib guide shows how to create cross-platform paths and folders. Screenshots, HTML, and browser logs are especially helpful when a test fails only in continuous integration.

Use the Page Object pattern

When selectors are copied across many tests, maintenance becomes difficult. A page object groups locators and actions in one class:

from selenium.webdriver.common.by import By


class LoginPage:
    EMAIL = (By.ID, "email")
    PASSWORD = (By.ID, "password")
    SUBMIT = (By.CSS_SELECTOR, "button[type='submit']")

    def __init__(self, driver):
        self.driver = driver

    def login(self, email, password):
        self.driver.find_element(*self.EMAIL).send_keys(email)
        self.driver.find_element(*self.PASSWORD).send_keys(password)
        self.driver.find_element(*self.SUBMIT).click()

The test then describes behavior rather than selector details. This pattern also creates a practical use for classes and objects, which are explained in the Academify material on Python fundamentals and advanced project organization.

Improve reliability

  • Wait for a meaningful condition: visibility, clickability, URL change, or a specific text value.
  • Use stable test data: create and clean up records instead of relying on shared mutable accounts.
  • Keep tests independent: one test should not require another test to run first.
  • Capture diagnostics: save a screenshot and current URL on failure.
  • Separate setup from assertions: helper functions and fixtures make failures easier to interpret.
  • Do not hide every exception: record enough context and let unexpected failures remain visible. The Python logging guide shows how to create useful diagnostic messages.

Common Selenium errors

NoSuchElementException usually means the selector is wrong, the element is in another frame, or it has not appeared yet. ElementClickInterceptedException often means a dialog or loading overlay covers the element. StaleElementReferenceException occurs when the page redraws an element after Selenium stored its previous reference; locate it again after the update.

Read the complete traceback and confirm the browser state before adding retries. Blind retries can hide a genuine application defect.

Conclusion

Selenium with Python is most effective when scripts use stable locators, explicit waits, reliable cleanup, and clear assertions. Begin with one small browser workflow, run it visibly, and only then add headless execution or a test framework. As the project grows, fixtures, page objects, environment variables, screenshots, and structured logs keep the automation understandable and maintainable.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Criação de bot Telegram com Python
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Build a Telegram Bot with Python

    Build a Telegram bot with Python step by step: create a bot token, handle commands and messages, add buttons, store

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Geração de arquivos PDF usando biblioteca FPDF em Python
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Create PDF Files with Python and FPDF

    Learn to create PDF files with Python and fpdf2: pages, fonts, paragraphs, images, tables, headers, footers, Unicode, invoices, paths, and

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Logos do Python e Excel lado a lado representando a importação de dados do Excel para Python.
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Import Excel Data into Python with Pandas

    Learn how to import Excel data into Python with Pandas and openpyxl, select sheets and columns, clean values, handle dates

    Ler mais

    Tempo de leitura: 4 minutos
    10/07/2026
    Python e ícone de e-mail sobre teclado de notebook
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Automate Emails with Python: Complete Guide

    Learn how to automate emails with Python using EmailMessage and smtplib, environment variables, HTML, attachments, multiple recipients, scheduling, and error

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Logo do Python com o texto 'Python argparse CLI' abaixo
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Build a Python CLI with argparse

    Build a Python CLI with argparse. Learn positional and optional arguments, flags, choices, subcommands, validation, testing, and packaging.

    Ler mais

    Tempo de leitura: 5 minutos
    10/07/2026
    Sistema de backup automático de arquivos usando Python
    Automation and Scripts
    Foto de perfil de Leandro Hirt da Academify

    Automate File Backups with Python

    Learn how to automate file backups with Python using shutil, os, pathlib, and datetime: define source/destination, copy folders, and schedule

    Ler mais

    Tempo de leitura: 3 minutos
    03/06/2026