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:
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →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.
#1 Best Overall
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:
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.
Rank #2
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.
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:
Rank #3
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.
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-Typestarts withimage/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=Trueand 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.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #4
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.
Recommended Free Tools
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.
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.
Best Value
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.
Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Why 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.
Quick Recap
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.

