ScreenshotNeo

BlogHow-to

How to Capture Mobile Websites with Selenium WebDriver

Emulate a mobile device with ChromeDriver, capture the page with Selenium, and understand what desktop emulation can and cannot verify.

By the ScreenshotNeo team4 October 202610 min read

To capture a mobile website with Selenium WebDriver, start ChromeDriver with Chrome’s mobileEmulation setting, navigate to the page, wait for the content you need, and save a WebDriver screenshot. You can use a predefined device profile or specify custom screen metrics. This is useful for repeatable responsive-layout checks, but it does not reproduce every behavior of a physical phone.

1. Choose a mobile emulation profile

ChromeDriver supports two ways to set up mobile emulation:

Approach Use it when Things to check
Predefined device name You want a quick profile corresponding to a listed device. The device name must be present in the device list known to your ChromeDriver and Chrome versions. Lists can differ by version.
Custom device metrics You need a specific viewport and pixel ratio, or a profile not in the list. Set screen width, height, and pixel ratio. Decide whether touch and mobile behavior, client hints, and user agent need explicit values.

Use a predefined name for convenience when it is supported by the installed browser and driver. Choose custom metrics when exact dimensions and pixel ratio are more important than matching a named profile. ChromeDriver’s guide documents both approaches and the version-dependent device list: ChromeDriver mobile emulation.

2. Capture a mobile website with Selenium in Python

Install Selenium and a compatible Chrome/ChromeDriver setup. Selenium Manager can help manage drivers in supported Selenium versions; your browser and driver still need to be compatible. This example uses custom metrics so the viewport is explicit:

from pathlib import Path
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.support.ui import WebDriverWait

options = Options()
options.add_experimental_option("mobileEmulation", {
    "deviceMetrics": {
        "width": 390,
        "height": 844,
        "pixelRatio": 3.0
    },
    "mobile": True,
    "touch": True
})

# For a repeatable CI job, uncomment headless mode.
# options.add_argument("--headless=new")

with webdriver.Chrome(options=options) as driver:
    driver.set_page_load_timeout(60)
    driver.get("https://example.com")

    # Wait for a meaningful page state rather than assuming that navigation
    # means client-rendered content is ready. Change this selector for your page.
    WebDriverWait(driver, 20).until(
        lambda d: d.execute_script("return document.readyState") == "complete"
    )

    Path("mobile-page.png").write_bytes(driver.get_screenshot_as_png())

This captures the current browser viewport. Replace the readiness condition with a wait for the page-specific element or state that matters to your screenshot. For example:

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

WebDriverWait(driver, 20).until(
    EC.visibility_of_element_located((By.CSS_SELECTOR, "main .product-card"))
)

If a listed Chrome device profile is suitable, replace the custom settings with a device name:

options.add_experimental_option("mobileEmulation", {
    "deviceName": "Nexus 5"
})

Use a device name supported by your installed ChromeDriver/Chrome combination. An unrecognized name causes an invalid-device error. Check the current official guide for device-name examples and version notes rather than assuming every profile is available.

3. Configure fidelity details when they matter

Screen dimensions alone may not reproduce the mobile site variant you expect. ChromeDriver’s mobile emulation configuration can also represent mobile behavior and touch, and supports optional client hints and user-agent values. These settings can affect responsive breakpoints, touch-specific behavior, and server-side decisions about which resources or markup to send.

  • Width and height: CSS screen dimensions for the emulated device. Keep orientation and dimensions consistent across runs.
  • Pixel ratio: Device pixel ratio, which affects the relationship between CSS pixels and screenshot pixels.
  • Mobile and touch: Set these when the page or interaction path depends on mobile/touch behavior. They do not simulate every physical touch or hardware characteristic.
  • Client hints: Use when the site’s server or client selects behavior from browser-provided device hints. ChromeDriver can infer user-agent information from client hints on supported platforms.
  • User agent: Set explicitly only when you need a particular user-agent string. Inferring client hints from an old-style user-agent string is not reliable because the string format is ambiguous.

For JavaScript Selenium users, the Chromium Options API exposes setMobileEmulation with either a predefined deviceName or custom screen metrics; passing null disables emulation. Binding APIs and signatures vary, so use the reference for your installed Selenium version: Selenium JavaScript Options API.

4. Capture in other Selenium bindings

JavaScript (Node.js)

With Selenium’s JavaScript binding, configure mobile emulation on Chromium options before creating the driver. This example uses the documented device-name form; check the API reference for the exact methods exposed by your installed release.

const { Builder, By, until } = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');

(async function captureMobilePage() {
  const options = new chrome.Options();
  options.setMobileEmulation({ deviceName: 'Nexus 5' });
  // For CI, you can add headless mode if appropriate for your Chrome version.
  // options.addArguments('--headless=new');

  const driver = await new Builder()
    .forBrowser('chrome')
    .setChromeOptions(options)
    .build();

  try {
    await driver.manage().setTimeouts({ pageLoad: 60000 });
    await driver.get('https://example.com');
    await driver.wait(until.elementLocated(By.css('body')), 20000);
    const image = await driver.takeScreenshot();
    require('node:fs').writeFileSync('mobile-page.png', image, 'base64');
  } finally {
    await driver.quit();
  }
})();

To use custom dimensions instead, pass the custom screen metrics form accepted by your installed Chromium Options API. Confirm the exact shape in that version’s documentation. If a device-name profile is rejected, switch to explicit metrics or choose a supported name.

Java

For Java, ChromeDriver accepts the mobileEmulation experimental option. The following example uses a custom profile and saves the viewport screenshot:

import java.nio.file.Files;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.Map;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.time.Duration;

public class MobileScreenshot {
  public static void main(String[] args) throws Exception {
    Map<String, Object> metrics = new HashMap<>();
    metrics.put("width", 390);
    metrics.put("height", 844);
    metrics.put("pixelRatio", 3.0);

    Map<String, Object> emulation = new HashMap<>();
    emulation.put("deviceMetrics", metrics);
    emulation.put("mobile", true);
    emulation.put("touch", true);

    ChromeOptions options = new ChromeOptions();
    options.setExperimentalOption("mobileEmulation", emulation);
    // options.addArguments("--headless=new");

    ChromeDriver driver = new ChromeDriver(options);
    try {
      driver.manage().timeouts().pageLoadTimeout(Duration.ofSeconds(60));
      driver.get("https://example.com");
      new WebDriverWait(driver, Duration.ofSeconds(20)).until(
          d -> "complete".equals(
              ((org.openqa.selenium.JavascriptExecutor) d)
                  .executeScript("return document.readyState")));
      Files.write(Path.of("mobile-page.png"),
          driver.getScreenshotAs(OutputType.BYTES));
    } finally {
      driver.quit();
    }
  }
}

Keep Selenium, Chrome, and ChromeDriver versions compatible. Check the Selenium binding documentation for your version if its Java APIs differ.

5. Decide between viewport and full-page screenshots

The examples above capture the current visible viewport. That is usually appropriate for checking what a user sees at a particular scroll position. Full-page capture behavior depends on the Selenium binding, browser, and screenshot method; do not assume a viewport screenshot includes content below the fold. Consult the screenshot semantics for your binding and verify the resulting image.

For Chrome-specific lower-level capture control, the Chrome DevTools Protocol Page domain includes Page.captureScreenshot, along with device metric override and clearing commands. CDP is Chrome-specific. Selenium describes WebDriver BiDi as its standards-based direction for bidirectional browser communication, but the documented mobile-emulation and screenshot operations in this guide are ChromeDriver/CDP operations; do not assume portable BiDi equivalents in every binding. References: Chrome DevTools Protocol Page domain and Selenium WebDriver BiDi.

6. Make captures repeatable

  1. Record the browser, ChromeDriver, and Selenium binding versions.
  2. Record whether you used a device name or custom metrics, including width, height, pixel ratio, touch/mobile flags, and client hints or user agent where relevant.
  3. Wait for a page-specific element or application state. A completed navigation event alone may not mean client-rendered content is ready.
  4. Use consistent input data, authentication, viewport, and capture timing when comparing screenshots.
  5. Save the screenshot with enough metadata to reproduce the run. Treat emulated captures and physical-device captures as different test conditions.

7. What desktop mobile emulation can and cannot verify

Emulation is useful for repeatable responsive layout checks and automated screenshot generation without a phone. It is not a complete substitute for testing on actual hardware. ChromeDriver documents differences that include mobile GPU behavior, browser UI effects such as address-bar changes to available page height, missing disambiguation popups for nearby touch targets, and unavailable hardware APIs such as some orientation events. Validate on a physical device when those details affect the result. See the ChromeDriver documentation.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server for developers. A single GET request returns an image or PDF; its API accepts parameter names used by other screenshot APIs, which can make switching easier. See the ScreenshotNeo API documentation.

cURL

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

Python

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://example.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://example.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const fs = require('node:fs');
fs.writeFileSync('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie and consent banners, newsletter popups, and chat widgets before capture; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and the response reports the page verdict and billing status in headers. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for AI agents. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Sign up for 1,000 free screenshots a month, with no card.

9. Troubleshooting

Symptom Likely cause Fix
Invalid device name or invalid mobile emulation The active ChromeDriver/Chrome version does not know that predefined profile. Check the version-specific ChromeDriver guide. Use a supported name or switch to custom width, height, and pixel ratio.
Chrome fails to start or session creation fails Chrome and ChromeDriver are incompatible, or the installed Selenium API does not match the example. Align browser and driver versions; check the documentation for the Selenium binding and release in use.
Screenshot looks like desktop Mobile emulation was not set before the driver session started, or the site serves a variant based on user agent/client hints that do not match the intended profile. Set emulation on ChromeOptions before building the driver. Check viewport metrics, mobile/touch settings, and the site’s responsive/server-side conditions.
Wrong responsive breakpoint or image sharpness CSS viewport dimensions or pixel ratio differ from the target. Specify and record width, height, and pixel ratio. Remember that screenshot pixel dimensions and CSS viewport dimensions are related by device pixel ratio.
Screenshot is blank or missing content The page has not rendered the target state, content is lazy-loaded, or navigation completed before client-side work. Wait for a meaningful selector or application state. If content loads after scrolling, scroll it into view or use the page’s supported full-page capture approach.
Screenshot cuts off the page The selected Selenium screenshot method captures only the current viewport. Check your binding’s documented full-page behavior, use a supported Chrome-specific capture method if appropriate, or capture sections by scrolling.
Touch interaction behaves differently from a phone Desktop emulation does not reproduce all physical touch behavior, hardware, or browser UI. Use emulation for layout and repeatable automation; validate hardware-dependent behavior on an actual device.
Tests pass locally but fail in CI Browser/driver versions, headless settings, fonts, timing, or environment differ. Pin and record the browser, driver, and Selenium versions; wait for page state instead of fixed short delays; keep viewport and test data consistent.

10. Performance, reliability, and cost

Selenium requires a browser and driver session, so startup and page loading are part of the capture workflow. Reusing a session for multiple pages can avoid repeated browser startup, while isolating sessions can help contain state between tests. Wait for the specific content required, but avoid arbitrary long sleeps that add time without guaranteeing readiness. No universal timing or accuracy figure applies across sites, browser versions, and environments.

For reliable comparisons, keep browser/driver versions, profile metrics, fonts, cookies, network conditions, and page state consistent. A screenshot is evidence of one rendered state at one viewport; it does not establish correctness across all devices. The software setup avoids the cost and logistics of a phone for layout checks, while physical-device checks remain necessary when the target behavior depends on real hardware or mobile browser UI.

FAQ

Can Selenium take a screenshot of a mobile website without a phone?

Yes. ChromeDriver mobile emulation can render a page with mobile device metrics and Selenium can save a screenshot. It is a desktop emulation, not a full physical-device test.

Should I use a device name or custom dimensions?

Use a device name for a quick profile that your ChromeDriver version supports. Use custom metrics when you need explicit dimensions or the named profile is unavailable.

Does a mobile screenshot prove the site works on iPhone and Android?

No. A Chrome desktop emulation capture checks a configured rendering scenario. It does not establish behavior across browsers, devices, or hardware.

Can I use this exact setup with Firefox?

The mobileEmulation capability described here is ChromeDriver-specific. Use the browser and binding’s own documented mobile testing capabilities for other browser engines.