0%

61- Web Automation with Selenium

Automate real browsers. Handle JavaScript-heavy websites. Click buttons, fill forms, scroll pages, and scrape dynamic content. When BeautifulSoup is not enough.

You have learned to scrape static HTML with requests and BeautifulSoup. But many modern websites are not static. They load data with JavaScript after the initial page load. Product listings appear when you scroll. Buttons trigger API calls. Content changes dynamically.
These sites cannot be scraped with requests alone. The HTML you download is empty. The data you want is added later by JavaScript.
Selenium is the solution. It automates real web browsers (Chrome, Firefox, Edge). It loads pages fully, executes JavaScript, waits for content, and lets you interact with pages as a real user would. You can click buttons, fill forms, scroll, hover, and extract data after dynamic content has loaded.
This lesson covers Selenium WebDriver, locating elements, waiting for content, handling alerts and popups, and scraping dynamic websites. Selenium is your tool for the modern, interactive web.

🕯️ Magic Note

Selenium is not a parser like BeautifulSoup. It is a browser automation tool. It launches a real browser window (visible or headless), navigates to pages, and lets you control the browser programmatically. This makes it slower than requests, but it can handle anything a human can do in a browser.

Installing Selenium and WebDrivers
Install Selenium and download the appropriate WebDriver for your browser.

Bash

# Install Selenium

pip install selenium

# For Chrome: Download chromedriver from https://chromedriver.chromium.org/

# Or use webdriver-manager (auto-manages drivers)

pip install webdriver-manager

Python

# Using webdriver-manager (recommended – no manual driver setup)

from selenium import webdriver

from selenium.webdriver.chrome.service import Service

from webdriver_manager.chrome import ChromeDriverManager

# Automatically downloads and sets up ChromeDriver

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

# For Firefox

from webdriver_manager.firefox import GeckoDriverManager

driver = webdriver.Firefox(service=Service(GeckoDriverManager().install()))

💡 The webdriver-manager package is highly recommended. It automatically downloads the correct driver version for your browser. No more manual driver management.
Basic Selenium Usage
Launch a browser, navigate to a page, and extract data.

Python

from selenium import webdriver

from selenium.webdriver.common.by import By

from webdriver_manager.chrome import ChromeDriverManager

from selenium.webdriver.chrome.service import Service

# Setup driver

driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()))

try:

# Navigate to a page

driver.get(“https://example.com”)

# Get page title

print(f”Title: {driver.title}”)

# Find elements

heading = driver.find_element(By.TAG_NAME, “h1”)

print(f”Heading: {heading.text}”)

# Get page source (for BeautifulSoup if needed)

page_source = driver.page_source

finally:

# Always close the browser

driver.quit()

⚠️ Always use try-finally or a with statement to ensure driver.quit() is called. Otherwise, browser processes may stay open in memory.
Locating Elements in Selenium
Selenium provides multiple ways to locate elements, similar to BeautifulSoup.
MethodDescriptionExample
By.IDFind by ID attributedriver.find_element(By.ID, “main”)
By.CLASS_NAMEFind by class namedriver.find_element(By.CLASS_NAME, “product”)
By.TAG_NAMEFind by tag namedriver.find_element(By.TAG_NAME, “h1”)
By.NAMEFind by name attributedriver.find_element(By.NAME, “email”)
By.CSS_SELECTORCSS selectordriver.find_element(By.CSS_SELECTOR, “#main .product”)
By.XPATHXPath expressiondriver.find_element(By.XPATH, “//div[@class=’product’]”)
By.LINK_TEXTExact link textdriver.find_element(By.LINK_TEXT, “Click Here”)
By.PARTIAL_LINK_TEXTPartial link textdriver.find_element(By.PARTIAL_LINK_TEXT, “Click”)

Python

from selenium import webdriver

from selenium.webdriver.common.by import By

# Assuming driver is already initialized

# Find by ID

header = driver.find_element(By.ID, “header”)

# Find by class name (note: returns first element with that class)

product = driver.find_element(By.CLASS_NAME, “product-item”)

# Find by CSS selector

price = driver.find_element(By.CSS_SELECTOR, “.product .price”)

# Find multiple elements

all_products = driver.find_elements(By.CLASS_NAME, “product-item”)

print(f”Found {len(all_products)} products”)

# Find by XPath (powerful but complex)

element = driver.find_element(By.XPATH, “//div[@data-id=’123′]”)

🕯️ Magic Note

CSS selectors are generally preferred over XPath for their readability. However, XPath can be more powerful for complex navigation (like finding elements by text content or parent-child relationships).

Waiting for Elements (Explicit and Implicit Waits)
One of the biggest challenges with dynamic content is timing. Elements may not be present immediately. Selenium provides wait mechanisms.

Python

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

# Implicit wait (global, applies to all find_element calls)

driver.implicitly_wait(10) # Wait up to 10 seconds for elements to appear

# Explicit wait (specific condition for a specific element)

wait = WebDriverWait(driver, 10)

# Wait for element to be present

element = wait.until(EC.presence_of_element_located((By.ID, “dynamic-content”)))

# Wait for element to be clickable

button = wait.until(EC.element_to_be_clickable((By.ID, “submit-btn”)))

# Wait for text to be present in element

wait.until(EC.text_to_be_present_in_element((By.ID, “status”), “Complete”))

# Common expected conditions

# EC.presence_of_element_located – element is in DOM

# EC.visibility_of_element_located – element is visible

# EC.element_to_be_clickable – element is visible and enabled

# EC.invisibility_of_element_located – element disappears

# EC.alert_is_present – alert dialog appears

💡 Use explicit waits for specific elements you know will appear. Implicit waits are a lazy default. Explicit waits make your code more reliable and faster (they don’t wait the full timeout when the element appears early).
Interacting with Pages: Clicks, Typing, and Scrolling
Selenium can simulate user interactions: clicks, typing, selecting, scrolling, and more.

Python

from selenium import webdriver

from selenium.webdriver.common.by import By

from selenium.webdriver.common.keys import Keys

from selenium.webdriver.common.action_chains import ActionChains

import time

# Fill a text input

search_box = driver.find_element(By.NAME, “q”)

search_box.clear() # Clear existing text

search_box.send_keys(“Python web scraping”)

# Press Enter key

search_box.send_keys(Keys.RETURN)

# Click a button or link

submit_button = driver.find_element(By.ID, “submit”)

submit_button.click()

# Click a link by text

next_link = driver.find_element(By.LINK_TEXT, “Next Page”)

next_link.click()

# Select from dropdown

from selenium.webdriver.support.ui import Select

dropdown = Select(driver.find_element(By.ID, “options”))

dropdown.select_by_visible_text(“Option 2”)

dropdown.select_by_value(“option2”)

dropdown.select_by_index(1)

# Scroll to element

element = driver.find_element(By.ID, “footer”)

driver.execute_script(“arguments[0].scrollIntoView();”, element)

# Scroll to bottom of page

driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”)

# Scroll by pixels

driver.execute_script(“window.scrollBy(0, 500);”)

# Hover over an element (ActionChains)

menu = driver.find_element(By.ID, “menu”)

hover = ActionChains(driver).move_to_element(menu)

hover.perform()

Handling Alerts and Popups
JavaScript alerts, confirmations, and prompts can be handled.

Python

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC

# Wait for alert to appear

wait = WebDriverWait(driver, 5)

alert = wait.until(EC.alert_is_present())

# Get alert text

alert_text = alert.text

print(f”Alert says: {alert_text}”)

# Accept (click OK)

alert.accept()

# Dismiss (click Cancel)

alert.dismiss()

# For prompt with input

alert.send_keys(“User input”)

alert.accept()

Handling Multiple Windows and Tabs
Switch between browser windows or tabs.

Python

# Get current window handle

main_window = driver.current_window_handle

# Open a link in a new tab (simulate Ctrl+Click)

link = driver.find_element(By.LINK_TEXT, “Open New Tab”)

link.send_keys(Keys.CONTROL + Keys.RETURN)

# Get all window handles

all_windows = driver.window_handles

# Switch to new window

for handle in all_windows:

if handle != main_window:

driver.switch_to.window(handle)

break

# Now work with the new window

print(f”New window title: {driver.title}”)

# Close current window and switch back

driver.close()

driver.switch_to.window(main_window)

Headless Mode (No Browser Window)
Run Selenium without opening a visible browser window. Faster and runs on servers.

Python

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

# Configure Chrome options

options = Options()

options.add_argument(“–headless”) # Run in headless mode

options.add_argument(“–no-sandbox”)

options.add_argument(“–disable-dev-shm-usage”)

options.add_argument(“–window-size=1920,1080”)

# Create headless driver

driver = webdriver.Chrome(options=options)

driver.get(“https://example.com”)

print(f”Title: {driver.title}”) # Works without visible browser

driver.quit()

🕯️ Magic Note

Headless mode is essential for running scrapers on servers without a graphical interface (like cloud VMs). It also makes scraping faster because rendering is skipped.

Practical Example: Scraping an Infinite Scroll Page
Many modern pages load more content as you scroll (infinite scroll). Selenium can handle this.

Python

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

def scroll_infinite_page(url, scroll_pause=2, max_scrolls=20):

driver = webdriver.Chrome()

driver.get(url)

items = []

last_height = driver.execute_script(“return document.body.scrollHeight”)

scroll_count = 0

while scroll_count < max_scrolls:

# Scroll to bottom

driver.execute_script(“window.scrollTo(0, document.body.scrollHeight);”)

# Wait for new content to load

time.sleep(scroll_pause)

# Check if we have reached the end

new_height = driver.execute_script(“return document.body.scrollHeight”)

if new_height == last_height:

print(“Reached end of page”)

break

last_height = new_height

scroll_count += 1

print(f”Scrolled {scroll_count} times”)

# Extract data after all scrolling

item_elements = driver.find_elements(By.CSS_SELECTOR, “.item-class”)

for elem in item_elements:

items.append({

“title”: elem.find_element(By.CSS_SELECTOR, “.title”).text,

“price”: elem.find_element(By.CSS_SELECTOR, “.price”).text

})

driver.quit()

return items

Practical Example: Login to a Website
Automate form submission to log into a website.

Python

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

def login_to_site(username, password):

driver = webdriver.Chrome()

try:

# Navigate to login page

driver.get(“https://example.com/login”)

# Wait for username field

username_field = WebDriverWait(driver, 10).until(

EC.presence_of_element_located((By.ID, “username”))

)

# Fill form

username_field.send_keys(username)

driver.find_element(By.ID, “password”).send_keys(password)

# Click login button

driver.find_element(By.ID, “login-btn”).click()

# Wait for login to complete (e.g., wait for logout link)

WebDriverWait(driver, 10).until(

EC.presence_of_element_located((By.LINK_TEXT, “Logout”))

)

print(“Login successful!”)

# Now we can scrape protected data

# …

return driver # Return driver for further scraping

except Exception as e:

print(f”Login failed: {e}”)

driver.quit()

return None

Combining Selenium with BeautifulSoup
After Selenium loads the page and executes JavaScript, you can get the HTML and parse it with BeautifulSoup for easier data extraction.

Python

from selenium import webdriver

from bs4 import BeautifulSoup

# Use Selenium to load dynamic content

driver = webdriver.Chrome()

driver.get(“https://example.com”)

# Wait for dynamic content to load

from selenium.webdriver.support.ui import WebDriverWait

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.common.by import By

WebDriverWait(driver, 10).until(

EC.presence_of_element_located((By.CLASS_NAME, “dynamic-content”))

)

# Get the fully rendered HTML

html = driver.page_source

# Parse with BeautifulSoup

soup = BeautifulSoup(html, “html.parser”)

# Extract data with BeautifulSoup (easier syntax)

items = soup.find_all(“div”, class_=”item”)

for item in items:

print(item.text)

driver.quit()

🕯️ Magic Note

This combination is powerful: Selenium handles JavaScript and dynamic loading, BeautifulSoup handles parsing and data extraction. Best of both worlds.

Common Mistakes with Selenium
  • Forgetting to wait for elements (causes NoSuchElementException)
  • Using time.sleep() instead of explicit waits (inefficient and unreliable)
  • Not closing the driver (processes stay open)
  • Running visible browser on servers (use headless mode)
  • Hard-coding driver paths (use webdriver-manager)
  • Scraping too fast (getting blocked; add delays between actions)
Check Your Understanding
  • When should you use Selenium instead of requests + BeautifulSoup?
  • What is the difference between implicit and explicit waits?
  • How do you run Selenium in headless mode?
  • Write code to fill a search form and click the search button.
  • How do you scroll to the bottom of an infinite scroll page?
  • What is webdriver-manager and why is it useful?

⚡ Whisper

The modern web is alive. JavaScript moves elements, loads content, responds to clicks. Static scrapers see only the skeleton. Selenium sees the living page. It opens a real browser, waits for content, clicks buttons, fills forms, scrolls to infinity. It is slower. It is heavier. But it works where other tools fail. Use it when you need it. For static pages, stick with requests and BeautifulSoup—they are faster and lighter. For dynamic pages, infinite scroll, login-required content, or JavaScript-rendered data, reach for Selenium. It is your key to the interactive web. Remember to wait. Remember to close the browser. Remember to use headless mode on servers. And when you combine Selenium with BeautifulSoup, you have the ultimate scraping toolkit. The web is dynamic. Your scrapers can be too.

Related posts