Selenium Tutorial for Beginners
Selenium is the most widely used browser automation framework in the world, with over two decades of development behind it. Whether you want to automate repetitive browser tasks, build a web scraping pipeline, or create an automated test suite, this tutorial gives you everything you need to go from zero to writing reliable Selenium scripts. We will use Python for the examples since it has the friendliest syntax for beginners, but the concepts apply identically to Java, C#, Ruby, and JavaScript.
Step 1: Install Selenium and a Programming Language
Before writing any Selenium code, you need two things installed on your machine: a programming language runtime and the Selenium client library for that language.
If you are using Python (recommended for beginners), download Python 3.9 or later from the official Python website. Run the installer and make sure to check the option to add Python to your system PATH. Verify the installation by opening a terminal or command prompt and running python --version, which should display the version number.
With Python ready, install the Selenium library using pip, Python's package manager. Open your terminal and run pip install selenium. This downloads the Selenium client library and all its dependencies. As of Selenium 4, this also includes Selenium Manager, which will automatically download and manage browser drivers for you.
You also need a web browser installed. Chrome is the most common choice for Selenium automation, but Firefox and Edge work equally well. If you already have any of these browsers installed, you are ready to proceed. Selenium Manager will handle downloading the matching driver executable (ChromeDriver, GeckoDriver, or EdgeDriver) automatically the first time you run a script.
For Java users, add the Selenium dependency to your Maven pom.xml or Gradle build.gradle file. The Maven group ID is org.seleniumhq.selenium and the artifact ID is selenium-java. Use version 4.x or later to get Selenium Manager and full W3C WebDriver compliance.
Create a project directory for your Selenium scripts. You can use any text editor or IDE, but beginners often find VS Code (for Python) or IntelliJ IDEA (for Java) the most productive environments since both offer autocompletion and debugging support for Selenium.
Step 2: Understand How WebDriver Works
Before writing code, it helps to understand what happens behind the scenes when Selenium controls a browser. This knowledge will make debugging much easier later.
Selenium uses a three-layer architecture. Your script (written in Python, Java, or another language) is the first layer. It calls methods on the Selenium client library, like "go to this URL" or "click this button." The client library translates these method calls into HTTP requests using a protocol called W3C WebDriver.
The second layer is the browser driver, a small executable program that acts as a translator between the WebDriver protocol and the browser's internal automation interface. Chrome uses a driver called ChromeDriver, Firefox uses GeckoDriver, and Edge uses EdgeDriver. Each driver is maintained by the browser vendor, ensuring tight integration with the browser it controls.
The third layer is the browser itself. The driver receives commands from your script, translates them into native browser operations, and sends back the results. When you tell Selenium to click a button, the driver instructs the browser to perform the click exactly as a human user would, triggering all the same event handlers and DOM updates.
In Selenium 4 and later, Selenium Manager handles driver management automatically. When you create a WebDriver instance, Selenium Manager checks which browser is installed, determines the correct driver version, downloads it if necessary, and configures it for your script. This eliminates the manual process of downloading drivers, adding them to your PATH, and updating them when the browser auto-updates, which was the biggest headache for Selenium beginners before this feature existed.
Understanding this architecture explains why Selenium can control any browser (it just needs the right driver), why it supports multiple programming languages (they all speak the same HTTP-based WebDriver protocol), and why scripts need to handle timing carefully (every command requires a round trip between your script, the driver, and the browser).
Step 3: Write Your First Automation Script
Create a new file called first_script.py in your project directory. Your first Selenium script will launch Chrome, navigate to a website, read the page title, and close the browser. This minimal example demonstrates the core workflow of every Selenium script.
Start by importing the webdriver module from selenium. Then create a Chrome WebDriver instance with driver = webdriver.Chrome(). This single line launches a Chrome browser window. Behind the scenes, Selenium Manager downloads ChromeDriver if needed and starts both the driver process and the browser.
Navigate to a URL with driver.get("https://www.google.com"). This tells Chrome to load the specified page and waits until the page's load event fires. After the page loads, you can read properties like the page title with driver.title, which returns a string you can print or assert against.
Always close the browser when your script finishes by calling driver.quit(). This shuts down both the browser and the driver process. If you skip this step, you will accumulate orphaned browser and driver processes that consume system resources. A best practice is to wrap your script in a try/finally block so the browser closes even if an error occurs during execution.
Run your script from the terminal with python first_script.py. You should see a Chrome window open, navigate to Google, and close. The terminal will display whatever you printed, such as the page title. If Chrome opens but displays a warning bar saying it is being controlled by automated software, that is normal and expected, it is Selenium's way of indicating the session is automated.
To run in headless mode (without a visible browser window), create a ChromeOptions object, add the --headless=new argument, and pass it to the Chrome constructor. Headless mode is faster because the browser does not need to render pixels to a screen, and it is the standard mode for CI/CD and server environments where no display is available.
Step 4: Find Elements on a Page
The core of Selenium automation is finding HTML elements on a page and interacting with them. Every click, form fill, text read, and screenshot targets a specific element, so learning how to reliably locate elements is the most important skill in Selenium.
Selenium provides the find_element() method, which takes a locator strategy and a value. The strategy tells Selenium how to search for the element, and the value is the search term. For example, driver.find_element(By.ID, "username") finds the element with id="username" in the page's HTML.
The most common locator strategies are ID (fastest and most reliable when IDs are unique), CSS selector (flexible and familiar to anyone who writes CSS), and XPath (the most powerful, capable of matching elements based on text content, attributes, and position in the document tree). Import these from selenium.webdriver.common.by: By.ID, By.CSS_SELECTOR, By.XPATH, By.NAME, By.CLASS_NAME, By.TAG_NAME, By.LINK_TEXT, and By.PARTIAL_LINK_TEXT.
Once you have found an element, you can interact with it. The .click() method clicks the element, simulating a mouse click. The .send_keys("text") method types text into input fields. The .text property reads the visible text content of the element. The .get_attribute("href") method reads any HTML attribute value. These methods cover the vast majority of interactions you will need.
To find multiple elements that match a pattern, use find_elements() (plural), which returns a list. For example, driver.find_elements(By.CSS_SELECTOR, "a.nav-link") returns every link with the class "nav-link" on the page. You can iterate over this list to read text from each element or click specific ones.
When an element cannot be found, Selenium raises a NoSuchElementException. This usually means the element has not loaded yet (a timing issue solved with waits), the locator is wrong (inspect the page source to verify), or the element is inside an iframe (which requires switching context first). Learning to diagnose these failures quickly is a key part of becoming proficient with Selenium.
Step 5: Add Waits for Reliable Automation
Web pages load asynchronously. When you navigate to a URL, the browser starts loading HTML, then CSS, JavaScript, images, and AJAX requests, often in parallel. An element you want to interact with might not exist in the DOM yet when your script reaches the line that tries to find it. Without proper waits, your scripts will fail intermittently, sometimes passing and sometimes failing depending on how fast the page loads.
Selenium provides explicit waits through the WebDriverWait class, which is the recommended approach for handling timing. An explicit wait pauses execution until a specific condition is true or a timeout expires. Import WebDriverWait from selenium.webdriver.support.ui and expected_conditions (commonly aliased as EC) from selenium.webdriver.support.
A typical explicit wait looks like: element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "results"))). This tells Selenium to wait up to 10 seconds for an element with ID "results" to appear in the DOM. If the element appears after 2 seconds, the wait ends immediately and returns the element. If 10 seconds pass without the element appearing, a TimeoutException is raised.
Common expected conditions include presence_of_element_located (the element exists in the DOM), visibility_of_element_located (the element is both present and visible), element_to_be_clickable (the element is visible and enabled), and text_to_be_present_in_element (the element contains specific text). Choose the condition that matches what you are waiting for: if you need to click a button, wait for it to be clickable rather than just present.
Avoid using time.sleep() for waits. A fixed sleep always waits the full duration even if the element is ready immediately, which slows down your scripts. It also fails when the page takes longer than expected to load. Explicit waits are superior because they return as soon as the condition is met and fail predictably when the timeout expires.
Selenium also offers implicit waits, which set a global timeout for all find_element() calls. While simpler to set up, implicit waits can mask timing issues and interact unpredictably with explicit waits. Most Selenium experts recommend using explicit waits exclusively and avoiding implicit waits entirely.
Step 6: Run Tests Across Multiple Browsers
One of Selenium's greatest strengths is cross-browser testing. The same script that works with Chrome can run on Firefox, Edge, and Safari with minimal changes. This is possible because all browsers implement the W3C WebDriver protocol, so the commands your script sends are browser-agnostic.
To switch from Chrome to Firefox, replace webdriver.Chrome() with webdriver.Firefox(). Selenium Manager downloads GeckoDriver (Firefox's driver) automatically, just as it does for ChromeDriver. Your locators, waits, and interactions remain identical. The only changes are browser-specific options, such as headless mode flags, which differ slightly between browsers.
For Edge, use webdriver.Edge(). Since Edge is built on Chromium, it accepts the same options as Chrome and behaves nearly identically. EdgeDriver is downloaded automatically. For Safari on macOS, use webdriver.Safari(). Safari's driver is built into macOS, so no separate download is needed, but you must enable the "Allow Remote Automation" option in Safari's Develop menu first.
A clean way to support multiple browsers in a single codebase is to create a factory function that returns the correct WebDriver instance based on a configuration parameter. This lets you pass the browser name as a command-line argument or environment variable, so the same test suite can run on any browser without modifying the code.
When running tests on multiple browsers, be aware that minor rendering and timing differences between browsers can cause tests to behave differently. An element that loads instantly in Chrome might take slightly longer in Firefox. Use explicit waits consistently to absorb these timing differences. Locator strategies that depend on browser-specific rendering (such as pixel-based positioning) should be avoided in favor of semantic selectors like IDs and CSS classes.
For systematic cross-browser testing, combine Selenium with a test framework like pytest (Python) or TestNG (Java) and parameterize your tests to run across multiple browsers. Selenium Grid can distribute these parameterized tests across machines running different browsers, significantly reducing the total execution time for cross-browser test suites.
Selenium's learning curve is manageable when you approach it step by step. Install the library, understand the three-layer architecture, learn the core locator strategies, and always use explicit waits. These fundamentals will serve you whether you are writing five tests or five thousand.