Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

To find a parameter in a JavaScript-populated table with Selenium WebDriver, wait for the table’s relevant data—not just the page load—then locate the current row and cell and read its rendered text or appropriate attribute. If an update replaces rows, wait for the change and locate the new elements before reading them. The right selector and ready signal depend on the target page’s DOM.

Why a dynamic table can be empty or outdated after navigation

Selenium’s navigation command waits for a page-load event, but that does not guarantee a JavaScript application has finished fetching and rendering table data. A table may still be empty, partly populated, or showing rows from before an update when driver.get() returns. Selenium’s Waiting Strategies documentation describes this distinction and advises against mixing implicit and explicit waits.

Use an explicit wait for a condition that represents the state your script needs. Depending on the page, that may mean the table exists, a data row is visible, a known value appears in a cell, or a previous row becomes stale after refresh. A fixed delay cannot confirm any of those states; a condition-based wait can continue as soon as its condition succeeds and raises a timeout error if it does not succeed in time.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Inspect the table and decide what “ready” means

There is no universal selector for a dynamic table. Inspect the permitted target page’s DOM and identify a stable table locator, the row and cell structure, and the observable signal that the needed data has arrived. Prefer stable IDs or meaningful attributes over a long positional XPath. For example, table#results is useful only if the page actually has a table with that ID.

Choose a condition that matches the task, rather than waiting for a generic event:

  • Table inserted after navigation: wait for the table or first data row to be present or visible.
  • Table shell appears before data: wait for data rows, a minimum row count, or expected cell text.
  • Known parameter is needed: wait until the target cell contains that value, using an expected condition or a custom condition.
  • Refresh replaces existing rows: wait for an old row to become stale or for the expected new text, then locate the current row and cell again.
  • Pagination or lazy loading is involved: perform the page-specific interaction, wait for an observable change, and continue only while the interface indicates more results.

Selenium’s Expected Conditions cover presence, visibility, text, staleness, and other common signals. A custom condition is appropriate when readiness is represented by a page-specific state not covered by a built-in condition.

Wait for rows, then read their cells

This complete example shows the core pattern for extracting the second cell from each row in a visible table. Replace the URL, table selector, and cell index with values verified against the target page. It assumes Chrome and a compatible driver are available through Selenium Manager or configured in the environment. It is an illustrative pattern, not a tested recipe for a particular site.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait

URL = "https://example.com/page-with-table"
TABLE = (By.CSS_SELECTOR, "table#results")
ROWS = (By.CSS_SELECTOR, "tbody tr")

options = webdriver.ChromeOptions()
# Uncomment for a headless run:
# options.add_argument("--headless=new")

driver = webdriver.Chrome(options=options)
try:
    driver.get(URL)
    wait = WebDriverWait(driver, 10)

    table = wait.until(EC.visibility_of_element_located(TABLE))
    wait.until(lambda d: len(table.find_elements(*ROWS)) > 0)

    rows = table.find_elements(*ROWS)
    values = []
    for row in rows:
        cells = row.find_elements(By.CSS_SELECTOR, "td")
        if len(cells) > 1:
            values.append(cells[1].text)

    print(values)
finally:
    driver.quit()

The example waits for a visible table and at least one row, then reads the second cell’s displayed text. If the table shell appears before the request fills it, waiting only for the shell is not enough: change the condition to match the expected rows or text. If some rows legitimately have fewer than two cells, the length check avoids indexing an absent cell; decide separately whether to skip, record, or reject such rows for your data task.

The current Python WebDriverWait API documents until() and a default polling interval of 0.5 seconds (Selenium 4.49.0 documentation). Set the timeout to suit the site’s normal response time and the consequences of a slow response; it is a maximum wait, not a sleep that must always elapse.

Find a particular parameter by row key

When a table has a key column and a value column, locate the row by its displayed key and extract the value from that same row. Do not assume a row number remains stable after sorting, filtering, or pagination. A custom wait can poll the current DOM until the desired value appears:

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait

TABLE = (By.CSS_SELECTOR, "table#results")

def parameter_value(driver, wanted_key):
    table = driver.find_element(*TABLE)
    for row in table.find_elements(By.CSS_SELECTOR, "tbody tr"):
        cells = row.find_elements(By.CSS_SELECTOR, "td")
        if len(cells) >= 2 and cells[0].text.strip() == wanted_key:
            value = cells[1].text.strip()
            return value if value else False
    return False

value = WebDriverWait(driver, 10).until(
    lambda d: parameter_value(d, "Account status")
)
print(value)

This assumes the first cell contains the key and the second contains the value. Change those choices to match the actual table. Returning False until the value is nonempty lets the wait poll again; a timeout means the condition did not become true within the configured period, not necessarily that the key is absent forever. The target may require a longer wait, a different locator, interaction with a filter, or handling another page of results.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Choose the right value source

For text displayed in a cell, use WebElement.text. If the page stores the value in an attribute instead of rendering it as text, inspect the DOM and read the relevant attribute explicitly—for example, cell.get_attribute("data-value") if that attribute is actually present. Do not guess the attribute name or assume hidden markup is equivalent to what a visitor sees.

For tables with headings, map a cell to its column by matching the header rather than relying on a numeric position when column order can change. For complex or nested tables, scope row and cell searches to the specific table and section. These locator decisions must follow the actual DOM; an XPath copied from another site is not a reliable general solution.

Handle refreshes without reading stale elements

A Selenium element reference points to a particular DOM element. If an asynchronous refresh removes and recreates the rows, previously located row or cell objects may no longer refer to current content. Selenium’s expected conditions include staleness checks; after the old row becomes stale, locate the new row from the table again.

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

ROW = (By.CSS_SELECTOR, "table#results tbody tr[data-id='42']")
wait = WebDriverWait(driver, 10)
old_row = wait.until(EC.presence_of_element_located(ROW))

# Perform the page-specific action that triggers the refresh here.
# For example, click a refresh control located from the actual page DOM.

wait.until(EC.staleness_of(old_row))
new_row = wait.until(EC.presence_of_element_located(ROW))
value = new_row.find_elements(By.CSS_SELECTOR, "td")[1].text
print(value)

The example’s row selector is deliberately illustrative: use a stable row key that the target page really exposes. Some applications update the text inside existing elements instead of replacing them; in that case the old row may not become stale. Wait instead for the expected new cell text or another actual page-state change, then read the current element.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Pagination, lazy loading, and incomplete results

A table’s visible rows may be only one page of records. A successful extraction of those rows does not prove that every record has been retrieved. If the page uses pagination, identify its next-page control and an observable page transition such as changed row text or page indicator. After each interaction, wait for that transition and re-locate the rows. Stop according to the page’s actual end condition.

For lazy-loaded rows, determine what interaction triggers loading—such as scrolling a container or reaching a sentinel—and wait for the row count or expected content to change. The necessary interaction and completion signal are site-specific. Avoid claiming all-record coverage unless pagination, scrolling, and any other loading mechanism have been handled. If the site offers an authorized, documented data interface, assess whether that is a more suitable source than browser automation.

Troubleshoot common failures

  • Timeout waiting for the table: Confirm the selector against the live DOM, check whether the table is inside an iframe, and wait for the data rows or text rather than a shell that never appears. If the page requires navigation or a filter action first, perform it before the wait.
  • Table is found but has no rows: The JavaScript request may still be pending, or the page may show an empty state. Wait for a meaningful row count or expected value and distinguish an empty result from unfinished loading using the page’s own indicators.
  • Old values appear after clicking Next or Refresh: The script may be reading elements located before the update. Wait for staleness or a changed value, then locate the row and cell again.
  • Stale element reference: A DOM replacement invalidated an element object. Do not keep retrying reads on that object; re-find the element after the update condition succeeds.
  • Blank .text despite a visible-looking value: Inspect whether the value is rendered as text, appears in a child element, or is stored in an attribute. Read the representation the page actually uses.
  • Script stops after the first screen of rows: Handle pagination or lazy loading explicitly. The table locator alone does not fetch hidden or later-page records.
  • Wait takes longer or behaves unpredictably: Check whether an implicit wait is configured elsewhere. Selenium warns that mixing implicit and explicit waits can produce unpredictable timing; use a deliberate explicit-wait strategy for these dynamic conditions.
  • Fixed sleep works intermittently: Replace it as the primary synchronization mechanism with a condition tied to the needed table state. A fixed delay neither proves success nor adapts to a faster response.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

If you need a screenshot or PDF of a page rather than structured cell values, ScreenshotNeo is a website screenshot API and MCP server. One GET request returns a PNG, JPEG, WebP, or PDF. For a capture of a public page:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for parameters. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are not billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots per month without a card; paid plans start at $5 for 3,000 screenshots.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Sign up for 1,000 free screenshots a month, with no card required.

Run it reliably and keep the extraction in scope

Use a bounded explicit timeout, wait only for a condition relevant to the requested data, and keep browser cleanup in a finally block as in the example. This ensures driver.quit() runs even if a wait or extraction raises an error. For repeatable jobs, log enough context to diagnose a timeout—such as the URL, intended selector, and page state—without recording sensitive table contents unnecessarily.

Remember that Selenium reads the page state exposed to its browser session. Authentication, consent flows, access restrictions, and site policies may affect what is visible; automate only where you are authorized to do so. A WebDriver table extraction is not the same as a screenshot: use it to retrieve values, and use a capture tool when the desired output is a visual record.

Frequently Asked Questions

Does Selenium wait for AJAX table data when driver.get() returns?

Not necessarily. Navigation waits for a page-load event, while asynchronous JavaScript can continue updating the page afterward. Wait for the table state your task needs.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What should I use instead of time.sleep() for a dynamic table?

Use WebDriverWait with a condition such as row presence, expected cell text, or staleness of the row being replaced.

Can Selenium extract every row from a paginated table automatically?

Only if your script handles the page’s pagination or lazy-loading behavior and confirms each transition. The visible rows alone may represent only the current page.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.