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.

Use Selenium to discover the image in the browser, then download its original URL with an HTTP client such as Requests. Read currentSrc first, fall back to lazy-loading attributes, copy browser cookies when authentication is involved, validate the response, and write the bytes in binary mode. A Selenium screenshot is a rendering of the page—not the original image resource.

Choose the right output: resource bytes or a screenshot

Before writing code, decide what you need:

Goal Method What you receive
Original photo, logo, or asset Read the image URL and request it with Requests The server’s image bytes, usually at the original resolution and format
Exactly what the browser displays driver.save_screenshot() or get_screenshot_as_file() A PNG of the browser window, including layout, scaling, overlays, and only the visible viewport
Image generated in a canvas or represented by a blob URL Page-specific JavaScript or network handling Not necessarily a conventional, reusable image URL

Selenium’s screenshot APIs save the current window as PNG; they do not retrieve the underlying asset. If your deliverable is the original file, use Selenium for browser state and discovery, and an HTTP client for the transfer.

Install the Python dependencies and prepare a driver

Install Selenium and Requests in the environment that will run the script:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
python -m pip install selenium requests

Recent Selenium releases can obtain a compatible browser driver through Selenium Manager when a supported browser is installed. The examples below use Chrome with webdriver.Chrome(). Keep the browser and driver versions compatible if you manage drivers yourself.

Download the first image with Selenium and Requests

This complete example opens a page, obtains the browser’s selected image URL, transfers it with synchronized cookies, checks the content type, streams the response, and closes the browser even when an error occurs.

from pathlib import Path

import requests
from selenium import webdriver
from selenium.webdriver.common.by import By

PAGE_URL = "https://example.com/page"
OUTPUT = Path("image.jpg")

driver = webdriver.Chrome()
try:
    driver.get(PAGE_URL)

    image = driver.find_element(By.CSS_SELECTOR, "img")
    image_url = driver.execute_script(
        "return arguments[0].currentSrc || arguments[0].src || "
        "arguments[0].dataset.src || arguments[0].getAttribute('data-lazy-src');",
        image,
    )
    if not image_url:
        raise RuntimeError("No image URL found")

    session = requests.Session()
    for cookie in driver.get_cookies():
        session.cookies.set(
            cookie["name"],
            cookie["value"],
            domain=cookie.get("domain"),
            path=cookie.get("path", "/"),
        )

    with session.get(image_url, stream=True, timeout=30) as response:
        response.raise_for_status()
        content_type = response.headers.get("content-type", "")
        if not content_type.lower().startswith("image/"):
            raise ValueError(f"Unexpected content type: {content_type}")

        with OUTPUT.open("wb") as output:
            for chunk in response.iter_content(chunk_size=64 * 1024):
                if chunk:
                    output.write(chunk)

    print(f"Saved {OUTPUT} from {image_url}")
finally:
    driver.quit()

Replace PAGE_URL, the selector, and the output suffix for your site. The suffix is only a filename convention; the server’s Content-Type and, when necessary, file-signature inspection are more reliable indicators of format.

Find the correct image URL

Select a specific element

A generic img selector may pick a logo or tracking pixel. Prefer a stable class, identifier, or semantic relationship:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
image = driver.find_element(By.CSS_SELECTOR, "article.product-card img.hero")

For several images, use find_elements and process each element:

images = driver.find_elements(By.CSS_SELECTOR, "article img")
for index, image in enumerate(images, start=1):
    url = driver.execute_script(
        "return arguments[0].currentSrc || arguments[0].src || "
        "arguments[0].dataset.src || arguments[0].getAttribute('data-lazy-src');",
        image,
    )
    print(index, url)

Prefer currentSrc for responsive images

When an element has srcset and a sizes attribute, the browser chooses a candidate based on viewport and device pixel ratio. currentSrc reports that selected URL. It is generally a better choice than reading the original src, which may be a low-resolution fallback.

Handle lazy-loading attributes

Sites commonly put the real address in data-src or data-lazy-src while the initial src is a placeholder. Trigger lazy loading before reading the element:

from selenium.webdriver.support.ui import WebDriverWait

# Scroll the element into view, which often triggers IntersectionObserver loading.
driver.execute_script(
    "arguments[0].scrollIntoView({block: 'center'});", image
)

WebDriverWait(driver, 15).until(
    lambda d: d.execute_script(
        "return arguments[0].currentSrc || arguments[0].src || "
        "arguments[0].dataset.src || arguments[0].getAttribute('data-lazy-src');",
        image,
    )
)

If the page loads images only after scrolling, scroll through the relevant container or page in increments. For an image whose URL is still absent, inspect additional site-specific attributes and the page’s JavaScript.

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

Download authenticated images

A direct requests.get() can return a login page or an authorization error even though the browser can display the image. Copying Selenium cookies into a Requests session, as in the main example, preserves the browser’s authenticated session. Some servers also require a matching Referer, user agent, authorization header, or other request header:

session.headers.update({
    "User-Agent": driver.execute_script("return navigator.userAgent"),
    "Referer": PAGE_URL,
})

Only copy headers that the site legitimately requires, and respect its access controls and terms. Cookies can expire between discovery and download, and signed image URLs may have a short lifetime.

Use Selenium’s cookie-synchronized request context

Where your Selenium Python binding and driver support the documented request API, you can request the resource through Selenium’s browser-cookie context:

response = driver.request.get(image_url)
response.raise_for_status()
Path("image.jpg").write_bytes(response.body())

The Selenium API describes this as an API request context for making HTTP requests with browser cookie synchronization. Check your installed Selenium version’s API documentation if driver.request is unavailable.

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

Browser-managed downloads

If clicking an image or link starts a download rather than exposing a URL, configure the browser’s download directory and accepted MIME types. Firefox preferences include browser.download.dir and browser.helperApps.neverAsk.saveToDisk. You can inspect a candidate’s type before choosing a MIME mapping:

head = requests.head(image_url, allow_redirects=True, timeout=30)
head.raise_for_status()
print(head.headers.get("Content-Type"))

Some servers do not implement HEAD correctly; in that case use a streamed GET and inspect its headers. Browser downloads may choose a server-provided filename, so an HTTP transfer is preferable when you need deterministic naming and validation.

Validate, name, and store the file safely

  • Call raise_for_status() so a 401, 403, 404, or server error cannot be saved as if it were an image.
  • Check that Content-Type starts with image/ when the endpoint is expected to return an image. A successful response can still be HTML, such as a login or bot-check page.
  • Open the destination with "wb"; text mode corrupts binary data.
  • Use stream=True and write chunks for large files so the complete response is not held in memory.
  • Sanitize filenames derived from URLs or page text, prevent path traversal, and avoid overwriting existing files unless that is intentional.
  • For stronger validation, inspect magic bytes or decode the file with an image library after download.

Common failures and fixes

Only a placeholder or low-resolution image is saved

Cause: the real URL is in srcset, data-src, or data-lazy-src. Scroll the element into view, wait for loading, and read currentSrc before the fallback attributes.

The response is HTML instead of an image

Cause: authentication, a consent or bot page, a redirect, or an expired signed URL. Print the final URL and Content-Type, preserve cookies and required headers, and perform the request promptly after discovering the URL.

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

HTTP 403 or 401

Cause: the image endpoint checks session cookies, authorization, referrer, user agent, or anti-bot state. Reuse the browser session, add only necessary headers, and do not attempt to bypass access controls.

No src exists

The visual may be drawn on a canvas, represented by a blob: URL, inserted by JavaScript, or delivered through a framework-specific data store. A normal URL download is not guaranteed; investigate the page’s network requests or export the canvas according to the site’s design.

The element cannot be found

Wait for the page’s actual rendering state, use an explicit selector, and check whether the image is inside an iframe. Switch into the relevant frame before locating elements. A selector that matches a visible element in developer tools may still be unstable across page versions.

The browser hangs or the transfer times out

Set page and HTTP timeouts, use a streamed request, and retry transient network failures with bounded backoff. Do not retry indefinitely; a persistent timeout can indicate a blocked resource or a page that never reaches its expected state.

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

Performance, reliability, and operational choices

  • Use one browser session for related images. Reusing cookies and a warmed page avoids repeated logins and startup cost.
  • Separate discovery from transfer. Selenium handles JavaScript, consent, and authentication; Requests handles efficient byte streaming and explicit filenames.
  • Wait for a condition, not an arbitrary long sleep. Waiting for a nonempty URL or a loaded image is faster and more reliable than a fixed delay.
  • Record provenance. Store the source page, resolved URL, timestamp, response status, content type, and final path so failures can be diagnosed.
  • Respect rate limits. Add deliberate pacing and bounded concurrency, especially when downloading many assets from one site.
  • Expect expiring URLs. Resolve and download signed URLs in the same session and retry discovery rather than reusing an old address.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Or skip the browser setup

For a clean rendered page image or PDF rather than the original asset bytes, ScreenshotNeo provides a single-request API. It accepts cookie and consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Only clean shots are billed: bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits cost nothing, with the result identified by X-Page-Verdict and X-Billed headers. Its MCP server includes take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.

See the ScreenshotNeo API documentation for options such as full-page capture, lazy-image loading, CSS selectors, device presets, dark mode, custom JavaScript, waits, request blocking, cookies, headers, geolocation, resizing, caching, signed links, asynchronous jobs, and bulk capture.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' }); const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

The Free plan includes 1,000 shots per month with no card. Paid plans start at $5 for 3,000 shots, and every feature is available on every plan. Create a free ScreenshotNeo account to start.

When each approach is appropriate

Requirement Recommended approach
Original downloadable bytes Selenium discovery plus Requests (or the synchronized request context)
Authenticated image endpoint Copy Selenium cookies and required headers, then validate the response
Lazy-loaded or responsive image Trigger loading and read currentSrc with attribute fallbacks
Pixel-accurate page appearance Selenium screenshot or a screenshot API
Canvas, blob, or anti-bot protected content Page-specific handling; a conventional src download may not exist

Frequently Asked Questions

Can I save an image without Selenium?

Yes, when you already know a public, stable image URL. Selenium is useful when JavaScript, lazy loading, authentication, or responsive selection is needed to discover that URL.

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

Why does the downloaded file have a .jpg name but behave like another format?

Filename extensions do not determine the bytes. Follow the response Content-Type and, for strict workflows, inspect the file signature or decode it after saving.

Is downloading every image that a browser displays allowed?

Not automatically. Follow the site’s terms, copyright rules, robots or rate-limit guidance, and authentication boundaries; browser visibility is not permission to redistribute an asset.

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.