Developer Guides

Selenium Web Scraping: Wait, Extract and Save JSON

Use Selenium and Python to scrape dynamic content. Wait for loaded data, click Load more, extract product records and save JSON from a local example.

Download example files
Local dynamic page after loading all three fictional products, with the Load more button disabled.

Use Selenium web scraping when the data you need appears after a browser renders the page or performs an interaction. The important step is waiting for the state that makes the data usable, rather than assuming a page-load event or fixed delay means extraction can begin.

This Python example opens a fictional local catalogue, waits for the first batch, clicks Load more, waits for the next batch and saves three products. It separates browser interaction from the later validation of extracted records.

Workflow at a glance

  1. 1. Prepare the browser and local example

    Install Selenium in your Python environment:

  2. 2. Complete a Selenium web scraping workflow

    Save this as selenium_products.py:

  3. 3. Wait for the condition that completes your task

    The example waits for data-page="1", then for data-page="2". This is more specific than waiting for one product element, which would already exist before the second batch loads.

  4. 4. Re-find elements after the page changes

    Some interfaces replace a whole result container after a click. Previously located elements can become stale. The sample locates product elements after the second batch is complete, rather than keeping references captured before the interaction.

  5. 5. Validate the result before saving it downstream

    Check that the expected fields are populated, identifiers are unique where required and the number of records fits the scope you collected. Preserve the source and collection time for a recurring job.

  6. 6. Add network settings before starting the session

    For an HTTP proxy without embedded authentication, Chrome options can select the proxy before the browser starts:

1. Prepare the browser and local example

Install Selenium in your Python environment:

python -m pip install selenium

You also need a compatible installed browser and driver. Selenium's standard driver setup can use Selenium Manager; a restricted network or managed workstation may require an explicitly configured driver. Follow the official browser setup documentation for your environment.

Start the accompanying demo_server.py with python3 demo_server.py. Open http://127.0.0.1:8766/dynamic. The server sends an empty product container; JavaScript adds the initial two records and the button loads a third record.

This page deliberately exposes data-page on its product container. It changes after each batch is inserted. The attribute is part of this tutorial fixture, not a universal feature of websites.

2. Complete a Selenium web scraping workflow

Save this as selenium_products.py:

import json
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()
try:
    driver.get("http://127.0.0.1:8766/dynamic")
    wait=WebDriverWait(driver,10)
    wait.until(lambda d:d.find_element(By.ID,"products").get_attribute("data-page")=="1")
    wait.until(EC.element_to_be_clickable((By.ID,"load"))).click()
    wait.until(lambda d:d.find_element(By.ID,"products").get_attribute("data-page")=="2")
    rows=[]
    for card in driver.find_elements(By.CSS_SELECTOR,"article.product"):
        rows.append({"id":card.get_attribute("data-id"),"name":card.find_element(By.TAG_NAME,"h2").text,"price_usd":card.find_element(By.CSS_SELECTOR,".price").text.lstrip("$")})
    with open("selenium-products.json","w",encoding="utf-8") as f:json.dump(rows,f,ensure_ascii=False,indent=2)
    print(json.dumps(rows,indent=2))
finally:
    driver.quit()

Run python selenium_products.py from the directory containing the file. The script writes selenium-products.json and closes the browser in a finally block, including when an exception interrupts extraction.

The output contains three fictional records: Travel Mug, Desk Lamp and Notebook. The first two appear after the initial render; the third appears after Load more completes.

Local dynamic page after loading all three fictional products, with the Load more button disabled.
The local browser fixture displays three fictional records after Load more completes; the button is then disabled.

3. Wait for the condition that completes your task

Condition

What it establishes

What it does not establish

Element is present

A matching node exists

It is visible, enabled or populated

Element is visible

It has a displayed state and size

A business operation is complete

Element is clickable

It is visible and enabled

No overlay or later page change can interrupt the click

Expected data or state appears

Your selected completion signal is satisfied

Every record on the website was collected

The example waits for data-page="1", then for data-page="2". This is more specific than waiting for one product element, which would already exist before the second batch loads.

On another site, a useful condition might be a changed result count, a known item identifier or a loading indicator disappearing together with new rows appearing. Choose a condition you can explain from the page's behavior.

Avoid mixing implicit and explicit waits: Selenium documents that the combination can produce unexpected wait times. See its waits guide.

4. Re-find elements after the page changes

Some interfaces replace a whole result container after a click. Previously located elements can become stale. The sample locates product elements after the second batch is complete, rather than keeping references captured before the interaction.

If you hit StaleElementReferenceException, inspect when the site updates its DOM. Re-locate the element after a meaningful state transition. An unlimited loop that retries the same stale reference does not fix the underlying timing problem.

For multiple load-more operations, put a limit on clicks or collected pages and track the records already seen. Stop when the documented completion condition is met. A disabled button in this fixture is one such signal; real sites may behave differently.

5. Validate the result before saving it downstream

Check that the expected fields are populated, identifiers are unique where required and the number of records fits the scope you collected. Preserve the source and collection time for a recurring job.

If an optional field is missing, decide whether to retain a partial row. If a required field is missing, report it instead of replacing it with a value that looks genuine. Text extracted from a browser still needs the same currency, date and type checks as data from an HTTP client.

Use an HTTP client and parser when the required data is already present in the response and no browser interaction is needed. The Python scraping guide explains that lighter route.

6. Add network settings before starting the session

For an HTTP proxy without embedded authentication, Chrome options can select the proxy before the browser starts:

import os
from selenium import webdriver

options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=" + os.environ["MIYA_PROXY_URL"])
driver = webdriver.Chrome(options=options)

Use the actual protocol, host and port supplied for the endpoint. Do not assume this flag handles username/password authentication; authenticated proxy setup depends on the browser and environment. Follow the MIYAIP connection guide and verify the browser's route separately.

Configure the network route, waits and selectors separately. After changing the destination, confirm the required elements and the scope of the returned data.

Frequently asked questions

Does Selenium render JavaScript?

It controls a browser that can execute page scripts. Your code still needs to wait for the resulting state before reading it.

Should I use time.sleep() after every click?

Use a condition tied to the expected page change. A fixed delay can be unnecessarily slow on one run and too short on another.

Can headless mode change the result?

It can affect rendering or site behavior. Debug with a visible browser first, then verify the same required fields and interactions in the mode used by your job.

Keep Selenium when your application needs direct browser control. For a task already supported by MIYAIP's interface catalog, compare the asynchronous Crawler API route.

Choose who runs the collection