ScreenshotNeo

BlogComparisons

Playwright vs Selenium for Capturing Product Prices After Variant Selection

Compare Playwright and Selenium for reliable price capture after a product variant changes, with runnable examples and a guide to choosing the right wait condition.

By the ScreenshotNeo team4 October 202610 min read

Short answer: For a new workflow that captures a product price after variant selection, Playwright is a reasonable default: locator actions wait for elements to be actionable, assertions can retry until the price reaches the expected state, and the page API can wait for a matching network response. Selenium can do the same job with explicit waits and remains a sensible choice when your team already has Selenium code, browser drivers, and infrastructure.

In either framework, a successful click does not prove that the product price has updated. Wait for a meaningful post-selection condition, then read the rendered price and record which variant it belongs to. Neither framework is documented as universally more accurate at extracting prices; correctness depends on the retailer’s page and the state your automation verifies.

1. Why price capture needs two waits

Variant selection and price readiness are separate events. A click can complete while the site is still updating its product state, fetching a price, or rendering the new value. Selenium describes this as a common synchronization challenge in browser automation. Playwright’s action auto-waiting helps make the click safe, but does not establish that later application work is finished.

  1. Wait for the control to be actionable. Confirm that the desired size, color, or other variant can be selected.
  2. Select the variant. Use an accessible label, role, or stable site-specific attribute.
  3. Wait for the result. Look for the selected state, a changed product identifier, an expected displayed price, or a known response associated with that variant.
  4. Read and validate. Capture the visible price only after the condition passes. Save the variant identifier with the price when possible.

There is no universal selector for product options or prices. Inspect the actual page and adapt the selectors and expected value to that retailer.

2. Playwright: complete Node.js example

This example uses a fictional product page and selectors. Replace the URL, label, expected price, and price locator with values from the target page. It deliberately fails on an unexpected or missing price instead of silently recording the old one.

npm init -y
npm install playwright
npx playwright install chromium

Save as capture-price.js:

const { chromium } = require('playwright');

(async () => {
  const browser = await chromium.launch({ headless: true });
  const page = await browser.newPage();
  const url = 'https://shop.example/products/item';
  const variantLabel = 'Blue / Large';
  const expectedPrice = '$24.00';

  try {
    await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 30000 });

    // Replace with a locator that uniquely identifies the target variant.
    const variant = page.getByRole('button', { name: variantLabel });
    await variant.click({ timeout: 10000 });
    await page.getByRole('button', { name: variantLabel })
      .waitFor({ state: 'visible', timeout: 10000 });

    // Replace with the retailer's stable price selector.
    const price = page.locator('[data-testid="product-price"]');
    await price.waitFor({ state: 'visible', timeout: 10000 });
    await page.waitForFunction(
      ({ selector, expected }) => {
        const el = document.querySelector(selector);
        return el && el.textContent.trim() === expected;
      },
      { selector: '[data-testid="product-price"]', expected: expectedPrice },
      { timeout: 15000 }
    );

    const capturedPrice = (await price.innerText()).trim();
    if (capturedPrice !== expectedPrice) {
      throw new Error(`Unexpected price for ${variantLabel}: ${capturedPrice}`);
    }
    console.log(JSON.stringify({ url, variant: variantLabel, price: capturedPrice }));
  } catch (error) {
    console.error(`Price capture unresolved for ${variantLabel}: ${error.message}`);
    process.exitCode = 1;
  } finally {
    await browser.close();
  }
})();

Run it with node capture-price.js. In a real workflow, the expected price may not be known ahead of time. In that case, wait for a site-specific signal that proves an update occurred—for example, a product ID associated with the chosen option or a loading indicator disappearing after the option changes—then read and validate the rendered price format.

Wait for a known price response

If selecting a variant triggers a known endpoint, register the response wait before clicking; otherwise the response could arrive before the wait starts. Then verify that the response belongs to the selected variant and confirm the rendered price as well.

const responsePromise = page.waitForResponse(response =>
  response.url().includes('/api/product-price') &&
  response.request().method() === 'GET' &&
  response.status() === 200,
  { timeout: 15000 }
);
await page.getByRole('button', { name: 'Blue / Large' }).click();
const response = await responsePromise;
const payload = await response.json();

// Adapt this check to the retailer's response schema.
if (payload.variant !== 'blue-large') {
  throw new Error('Price response did not match the selected variant');
}
// Still wait for and read the rendered price before treating the capture as complete.

A broad URL match can catch an unrelated request. Match a stable endpoint and, where available, inspect a variant identifier in the response. The response is a useful synchronization signal, but checking the visible value ensures the page reached the state your downstream process expects.

3. Selenium: complete Python example

Selenium explicit waits poll for a condition chosen by your script. This example uses a product option button and a site-specific price element. Install Selenium and a compatible browser/driver setup for your environment.

python -m pip install selenium

Save as capture_price.py:

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

URL = "https://shop.example/products/item"
VARIANT_LABEL = "Blue / Large"
EXPECTED_PRICE = "$24.00"
PRICE_SELECTOR = '[data-testid="product-price"]'

options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
driver = webdriver.Chrome(options=options)
wait = WebDriverWait(driver, 15)

try:
    driver.set_page_load_timeout(30)
    driver.get(URL)

    # Prefer a stable accessible label or site-specific attribute.
    variant = wait.until(EC.element_to_be_clickable(
        (By.XPATH, "//button[normalize-space()='Blue / Large']")
    ))
    variant.click()

    # Wait for the post-selection price state, not merely element presence.
    def expected_price_visible(browser):
        elements = browser.find_elements(By.CSS_SELECTOR, PRICE_SELECTOR)
        if not elements:
            return False
        value = elements[0].text.strip()
        return elements[0] if value == EXPECTED_PRICE else False

    price_element = wait.until(expected_price_visible)
    captured_price = price_element.text.strip()
    print({"url": URL, "variant": VARIANT_LABEL, "price": captured_price})
except Exception as exc:
    raise RuntimeError(
        f"Price capture unresolved for {VARIANT_LABEL}: {exc}"
    ) from exc
finally:
    driver.quit()

For sites where the expected value is not known in advance, replace the exact-price predicate with a condition tied to the page’s update: for example, the selected variant’s product ID changes, a loading marker disappears after selection, or the price text changes from its previous value. Avoid treating mere element presence as proof of an update if the old price element remains in the DOM.

Selenium warns against mixing implicit and explicit waits because their combined timing can be unpredictable. This example uses an explicit wait and leaves the implicit wait at its default of zero. See the official Selenium waiting strategies and expected conditions.

4. Which framework should you choose?

Decision Playwright Selenium Implication for price capture
Action readiness Locator actions auto-wait for actionability; web-first assertions retry conditions. Explicit waits poll for conditions selected by your code. Playwright provides convenient defaults; both need a post-selection condition.
Price response observation Page APIs can wait for matching requests and responses. The reviewed Selenium material covers waits; the sources used here do not establish an equivalent one-call response-wait API. If a known endpoint signals the update, Playwright has an integrated response-wait pattern. Verify the response matches the chosen option.
Browser coverage Documents Chromium, Firefox, WebKit, branded browsers, and emulated devices. Coverage depends on WebDriver browser and driver setup. Match the real target environment and test the retailer in the browser configuration you will run.
Existing infrastructure Good fit when its language and runtime fit a new workflow. Good fit when the team already has WebDriver code, drivers, and operational knowledge. Migration cost is specific to your codebase and team.
Remote execution BrowserStack and Sauce Labs document Playwright options, with compatibility constraints depending on connection mode. Both services document Selenium support. Hosted grids can supply test environments; they do not establish extraction correctness.

For a new dynamic-page capture flow, start with Playwright unless your project has a strong Selenium foundation or a runtime requirement that favors WebDriver. For either choice, run against the target retailer, choose a specific state condition, and keep a bounded timeout. Framework documentation does not guarantee identical retailer behavior across browsers.

Playwright references: locators, actionability and auto-waiting, retrying assertions, network response waits, and browser support. Playwright discourages using networkidle as a general test readiness signal; prefer an assertion or page-specific signal.

5. Choosing a reliable readiness condition

Page behavior Useful condition What to verify
Price changes to a known value Wait for the displayed text to equal the expected formatted price. Currency, decimal separators, and whether the value is a sale or unit price.
Price is not known in advance Wait for a variant ID change, an update marker, or the price to differ from its pre-click value. That the signal corresponds to the selected option, not a page refresh or unrelated update.
Selection calls a known endpoint Start waiting for a matching response before clicking, then inspect its status and variant identifier. The rendered price agrees with the response or the retailer’s displayed state.
Page uses a loading indicator Observe its appearance/disappearance around the selection, if the page reliably exposes it. Do not accept a never-shown indicator disappearing immediately as evidence; pair it with another state check.

Set separate, bounded navigation and update timeouts. A timeout means the capture is unresolved; do not silently save the previous option’s price. Store useful diagnostics such as URL, selected variant, observed text, and failure stage, while avoiding sensitive customer or account data.

6. Configuration, performance, and operational notes

  • Timeouts: Choose limits based on your workflow and target sites, and report navigation timeouts separately from variant-update timeouts. Do not use long arbitrary sleeps to mask uncertainty.
  • Browser choice: Playwright documents Chromium, Firefox, and WebKit, and emulated devices; Selenium’s practical options depend on the browser/driver combination. Validate the exact combinations you deploy.
  • Headless versus headed: Headless mode is convenient for scheduled captures. If behavior differs from a visible session, reproduce with a headed browser and inspect the selected control, resulting DOM, and network activity.
  • Concurrency: Reuse browser processes where your framework and application design allow, while keeping page/session state isolated. Limit parallel requests to a level the retailer permits and your machine can sustain.
  • Retries: Retry transient navigation or infrastructure failures with a cap and backoff. Re-evaluate the selected variant and price after each retry; do not repeat a purchase or other consequential action.
  • Cost: Self-hosted runs consume compute, browser installation/storage, and maintenance time. Hosted browser grids can add service costs. The evidence here establishes no comparative runtime or extraction-accuracy benchmark, so measure your own pages if those affect cost.
  • Site requirements: This is framework guidance, not a test against a live retailer. Check the target site’s requirements, terms, rate limits, and your operating context before automating captures.

7. Troubleshooting

Symptom Likely cause Fix
Click times out or is intercepted The option is hidden, disabled, covered, or the locator matches multiple controls. Use a unique accessible locator; wait for the intended option to be visible and enabled; inspect overlays and duplicate controls.
Capture returns the old price The script reads immediately after the click or waits only for the price element to exist. Wait for the expected price, changed variant ID, or a matching update signal before reading.
Wait succeeds on the wrong response The URL predicate matches background or unrelated price traffic. Match the endpoint and request method more narrowly, then validate the response’s variant identifier and rendered price.
Price wait times out even though the page updated The expected text differs in whitespace, currency formatting, locale, or sale-price structure. Inspect the actual DOM text; normalize only known formatting differences and identify the correct price node.
Flaky timeout length in Selenium Implicit and explicit waits are mixed. Use one wait strategy; for explicit conditions keep the implicit wait at zero.
Playwright’s network-idle wait never settles Analytics, polling, or persistent requests keep the page active. Wait for a relevant price or application state instead of using network idle as a blanket signal.
Works locally, fails in CI Browser binaries or system dependencies are missing, or the deployed browser differs. Install the matching browser/driver and dependencies; log browser versions and reproduce with the same configuration.
Price appears blank or duplicated The selector points to a placeholder, hidden responsive copy, or multiple price nodes. Scope the locator to the active product panel, require a unique match, and validate the resulting text.

8. Or skip the browser setup

If the goal is a visual record of a product page after it has already reached the correct variant state, ScreenshotNeo can return a screenshot or PDF through one GET request. It is a screenshot API and MCP server, not a substitute for selecting variants or extracting structured prices: use browser automation when you need to interact with the option and verify the resulting price.

For a page whose variant is selected by its URL, or a page that opens directly in the desired state, make one request:

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. Cookie banners, popups, and chat widgets are removed before the shot; bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card, and paid plans start at $5 for 3,000.

Create a free ScreenshotNeo account.

9. FAQ

Does Playwright always capture a price more accurately than Selenium?

No. The documentation supports a convenient synchronization model in Playwright, but does not establish universal extraction accuracy for either framework.

Can I use a fixed sleep after clicking?

You can, but it may be too short on a slow page and waste time on a fast one. A state-based wait is more directly tied to whether the desired variant price is ready.

Should I wait for the whole page to finish loading?

Usually not for a client-rendered price update. Wait for the relevant price or variant state; page-wide network quiet can be unrelated to whether that state is correct.

Can a screenshot API select the variant for me?

ScreenshotNeo’s listed features include clicking a CSS-selected element before capture, but this workflow still needs a page-specific way to identify the desired option and confirm its price. Browser automation gives more control when structured extraction and explicit state validation are required.