Skip to main content
Tech Tutorials & Programming

Aug 24, 2026

How Do You Scrape Images From a Website With Python?

Learn how to scrape images from a website with Python, Beautiful Soup, and Playwright while handling lazy loading, srcset, proxies, and safe downloads.

How Do You Scrape Images From a Website With Python?

Meta Title: How to Scrape Images From a Website With Python Meta Description: Learn how to scrape images from a website with Python, Beautiful Soup, and Playwright while handling lazy loading, srcset, proxies, and safe downloads.

Quick Answer

To scrape images from a website with Python, use Requests to fetch HTML and Beautiful Soup to extract image URLs. Resolve relative paths with `urljoin()`, then use Requests again to download and validate the files. For images that appear only after JavaScript or scrolling, use Playwright.

Key Takeaways

  • Static pages can usually be scraped with Python Requests and Beautiful Soup.
  • JavaScript-rendered pages may require Playwright or another real browser engine.
  • Responsive images can use `srcset`, `<picture>`, and a browser-selected `currentSrc` value.
  • Relative HTML image paths use the document base, while external CSS image paths use the stylesheet URL.
  • Safe downloads need status checks, timeouts, type validation, size limits, and unique filenames.
  • Proxies solve routing and location problems, but they do not render JavaScript or repair selectors.
  • Image access and image reuse are separate questions, so check both before collecting files.

Which Image-Scraping Method Should a Beginner Use?

Requests and Beautiful Soup fit pages whose image sources already appear in the returned HTML. Playwright or Selenium fit pages that create images after JavaScript runs.

MethodCoding levelBest forMain trade-off
Browser extensionLowOne-off exports from simple pagesLimited control and weak repeatability
Requests and Beautiful SoupBeginnerImages already present in HTMLCannot execute page JavaScript
Playwright or SeleniumIntermediateLazy loading, scrolling, and rendered pagesUses more memory and processing time
ScrapyIntermediateCrawling many pages with queues and retriesMore setup than a one-page script
Documented API or feedVariesStructured, provider-supported image accessAvailability and usage rules depend on the site

Best for a first script: Requests and Beautiful Soup. Best for a page that changes after it opens: Playwright or Selenium. Best for a multi-page crawler: Scrapy or another framework with scheduling and retry controls.

A headless browser runs a browser engine without a visible window. Web scraping with Selenium provides another browser-based option. The wider list of Python libraries for web scraping helps when the source requires a different tool.

Choose the lightest tool that exposes the required source reliably. A static client is easier to debug, while a browser is justified when page state changes the result.

What Does an Image Scraper Actually Collect?

Image scrapers collect image addresses and metadata first. Separate requests then retrieve the files those addresses identify. Image scraping is one form of web scraping.

The output differs from a scraper that collects only product names or prices. An image scraper stores source URLs, alternative text, page context, and image files. These records connect each downloaded file to its original page.

The obvious source is an `<img>` element with a `src` attribute. That attribute is only one possible source. Responsive pages can give the browser several candidates through `srcset` or `<picture>`.

Lazy-loading code may keep the real address in `data-src`, `data-lazy-src`, or another custom attribute. Images can also come from Cascading Style Sheets (CSS), JavaScript-generated markup, or application programming interface (API) responses. Those responses often use JavaScript Object Notation (JSON).

A `<canvas>` element may display pixels without exposing one reusable image URL in the final HTML. Each source therefore requires a different inspection method. The table shows where common image sources appear.

Image locationWhat it containsBest way to inspect itMain limitation
`<img src>`One primary image addressBeautiful Soup or browser toolsMay hold only a thumbnail or placeholder
`srcset` or `<picture>`Several responsive candidatesParse the candidates or inspect `currentSrc`The browser chooses according to its viewport and density
Custom `data-*` attributeA lazy-loading sourceInspect the element and site scriptAttribute names are not universal
CSS `background-image`Decorative or layout imagesInspect computed styles in a browserStatic HTML may not contain the final value
JSON or API responseStructured image recordsInspect relevant network responsesThe endpoint may require session state or documented access

Start source mapping in the browser's Elements and Network panels. Reload the page and find where the required files appear. Premature selectors often collect logos, placeholders, and tracking pixels instead of the intended images.

What Is the Source-to-File Image Workflow?

The Source-to-File Image Workflow checks permission, page retrieval, source discovery, URL normalization, and file validation in that order. Its purpose is to keep discovery separate from downloading.

That separation makes failures easier to diagnose. A missing selector is not the same problem as a rejected file request. Each stage therefore has a distinct failure signal.

  1. Confirm the boundary: Identify the pages, image types, allowed request rate, and intended use. Check whether an approved API or export already supplies the files.
  2. Retrieve the correct page state: Use a Hypertext Transfer Protocol (HTTP) client for static HTML. Use a browser when JavaScript creates the required page state.
  3. Map every relevant source: Check `src`, `srcset`, `<picture>`, lazy-loading attributes, CSS, and relevant network responses.
  4. Normalize and deduplicate: Resolve relative URLs, reject unsupported schemes, and remove repeated addresses before downloading anything.
  5. Download and validate: Check the response status, media type, byte limit, filename, and final file before accepting the result.

Do not mix all five stages into one long loop at first. Print the discovered URLs and inspect a sample. Add the download step once the list is correct.

This approach stops a bad selector from producing navigation icons or duplicate thumbnails. The workflow also gives each test a clear outcome. No discovered URLs points to page rendering or selector logic.

A discovered URL returning `403` points to access or session handling. A successful HTML response points to validation rather than discovery. These outcomes identify the next test without guesswork.

What Do You Need Before Scraping Images With Python?

Image-scraping prerequisites include Python 3.10+, an isolated project folder, Requests, Beautiful Soup, and a test page. Use a page you own or a controlled fixture while building the script.

Create an isolated project folder before installing dependencies. Use these commands on macOS or Linux.

bash

Windows PowerShell uses a different activation path. Create the same isolated project there. Run these commands.

bash

Keep downloaded files outside the source package. Add the output directory to `.gitignore` when the project uses Git.

Create a file named `scrape_images.py`. Keep the target URL and request settings near the top. Use an honest User-Agent value that identifies the client when appropriate.

Replace the example contact address with one you control. Do not copy a browser identity merely to disguise automation. Set request timeouts, begin with one page, and keep the request rate low while testing.

Before writing a selector, inspect the target page in a browser. Search the HTML for `<img`, `srcset`, `data-src`, and `background-image`. Then open the Network panel and filter by image responses.

This check reveals whether each source appears before or after JavaScript runs. Pin dependency versions when the script becomes a repeatable job. Record the Python version because it makes failures easier to reproduce after library updates.

How Do You Extract Image URLs With Requests and Beautiful Soup?

Requests retrieves the page. Beautiful Soup finds image attributes. Python resolves relative addresses against the correct base URL.

The following script handles ordinary `src` attributes and several common lazy-loading attributes. It also reads conventional HTTP `srcset` lists and checks `<picture><source>` elements. The code ignores non-HTTP schemes and prints unique sources.

python

The `srcset_candidates` function reads URL tokens without splitting commas inside data URLs. The normalization step then rejects every non-HTTP scheme. The function extracts candidates but does not reproduce the browser's responsive-image selection.

Use the rendered method when viewport or media rules determine the required image. Python's URL parsing documentation explains how `urljoin` combines a base URL with a relative address. The script uses `page_response.url` because a redirect may change the base page.

The script also honors a valid HTML `<base>` element. The `set` removes duplicate URLs before any files are requested. Web scraping with Beautiful Soup also benefits from defensive attribute access.

Use `.get()` when an element may not define a value. If this code runs inside a server or shared service, do not request every discovered host blindly. Apply an approved-host allowlist before every request.

Block private or link-local destinations after Domain Name System (DNS) resolution. Repeat the validation for every redirect. A page can contain unrelated absolute URLs.

Keep query strings intact when validating and downloading a selected URL. Image services often use query strings for transformations, cache keys, or authorization. Removing them can change the returned file or make the request fail.

How Do You Download Images Without Saving Error Pages?

Image downloads should check status, declared media type, byte limit, and filename. Temporary paths keep failed transfers separate. Add the following code after the discovery block.

The code reuses the same Requests session and streams each response in chunks. A temporary file moves only after its declared type, size, and nonempty body pass the checks. Only accepted transfers reach the final path.

python

The Requests documentation recommends `iter_content` for streamed file writes. Streaming avoids loading an entire large response into memory. The running byte count enforces the limit when `Content-Length` is absent or wrong.

The media-type check rejects an ordinary HTML error response instead of saving it as `photo.jpg`. A response header cannot prove that the bytes form a valid image. Security-sensitive pipelines should inspect file signatures and decode raster images in a controlled environment before further use.

The sample rejects image redirects instead of following them automatically. Validate each redirect destination before requesting the final URL. The sample also omits the `Referer` header.

Send only a deliberate `Referer` value when the target requires one. Treat Scalable Vector Graphics (SVG) files with extra care. SVG is text-based and can reference external resources or contain active features.

Do not place untrusted SVG files into an administrative interface or public page without a sanitization policy. A filename extension should follow the accepted declared media type, not the URL suffix. The sample creates sequential names to avoid collisions.

Store the original URL, final redirected URL, response type, and saved path in a separate provenance record. Keep that record beside the collection rather than encoding every detail in filenames. This approach preserves traceability without creating unstable paths.

How Do You Handle Lazy-Loaded and Responsive Images?

Lazy-loaded images require the scraper to distinguish browser-standard sources from site-specific placeholders and deferred attributes. The `loading="lazy"` attribute does not automatically move a URL out of `src`. It tells a supporting browser that it may delay loading the resource.

Custom lazy-loading libraries can behave differently. They may keep the real URL in `data-*` until JavaScript updates `src` or `srcset`. Responsive images create another issue.

A `srcset` attribute can list several candidates with width or pixel-density descriptors. The browser combines those candidates with the viewport, display density, and `sizes` rules. The selected result appears through `currentSrc` after rendering.

SymptomLikely causeCorrect check
Only tiny images are found`src` contains a thumbnailInspect `srcset`, `<picture>`, and `currentSrc`
Placeholder files are downloadedA custom lazy loader keeps the real URL elsewhereInspect `data-*` attributes and page scripts
Images appear only after scrollingJavaScript or intersection-based loadingRender the page and scroll in controlled steps
Different images appear by screen sizeResponsive art directionSet the browser viewport and inspect `currentSrc`
No image URL appears in the tagCSS, canvas, or an API supplies the visualInspect computed styles and relevant network responses

Do not automatically choose the largest `srcset` candidate. The largest file can waste bandwidth and may not be the asset the page actually displays. Decide whether the workload needs the rendered candidate, every available resolution, or a specific width.

Infinite-scroll pages also need a stopping rule. Set a maximum number of scrolls and cap the collected URLs. These limits define the intended collection boundary.

Stop when the browser reaches the bottom without loading another batch. Without those limits, a scraper can continue beyond the intended scope. Record the stopping condition so repeated runs use the same boundary.

How Do You Scrape Images From a JavaScript-Rendered Page?

Playwright can render the page, trigger controlled scrolling, and return the image URL each browser element actually selected. Install Playwright and its Chromium browser before running the example. These commands add both requirements.

bash

Create `rendered_images.py` next. The script scrolls within fixed limits. Use the following code.

python

The Playwright documentation explains that `locator.evaluate_all()` runs code across matching page elements. Here, the browser resolves `currentSrc` instead of guessing which responsive candidate it selected. Browser automation provides the rendering layer required by the page.

Keep scroll limits, timeouts, and target-specific pacing in place. Use browser automation only after confirming that static HTML lacks the required sources.

Rendering every page consumes more memory and processing time than direct HTTP requests. The example stops after two unchanged checks at the page bottom or 20 viewport-sized scrolls. Set both limits to match the configured scope.

The stability check compares document height and selected image sources. Delayed batches reset the counter when either value changes. The rendered page may reveal signed URLs, while its browser context may hold required cookies.

A separate Requests session does not inherit browser cookies. Download within the browser context when those cookies are required. Transfer only the necessary session data when a separate client must download the files.

Never place session cookies in logs or source code. Discovery and downloading remain separate stages. Validate the response type and byte limit even when the browser supplied the URL.

How Do You Find CSS Background Images and Canvas Content?

CSS background images may need computed-style inspection, while canvas drawings may expose no reusable source URL. An inline declaration such as `style="background-image: url('/hero.jpg')"` is easy to parse. Its relative URL uses the document base.

External stylesheet URLs use the stylesheet's URL as their base. Media queries and JavaScript-added classes can also change the final background. Beautiful Soup sees HTML attributes and embedded stylesheet text.

Beautiful Soup does not calculate the final CSS cascade. A browser can inspect an element's computed `backgroundImage` value after rendering. Start with the browser's Elements panel and Computed styles tab.

Pseudo-elements require separate computed-style checks. In JavaScript, pass `"::before"` or `"::after"` as the second argument to `getComputedStyle()`. Add a tightly scoped Playwright selector when the required images consistently appear there.

Target the relevant cards or sections instead of every element. Canvas requires a different approach. A script can draw pixels from an image, video, chart, or generated data.

The canvas element does not have to retain the original source URL. Inspect relevant network requests and the page's JavaScript. If no reusable source exists, a canvas screenshot captures rendered pixels instead of the original file.

Inline SVG creates a similar distinction. An `<svg>` element can contain shapes directly, while an `<image href="...">` element can reference another file. Decide whether the project needs the source document, referenced raster image, or rendered screenshot before writing extraction logic.

Keep CSS and canvas handling separate from the basic `<img>` scraper. These sources follow different rules. One broad regular expression tends to collect gradients, icons, data URLs, and unrelated layout assets.

How Do You Scrape Images From Multiple Websites at Scale?

Scraping images from multiple websites requires bounded concurrency, reusable sessions, selective retries, and shared deduplication. Apply limits per domain because response times, capacity, and failure patterns differ.

  1. Cap concurrency: Start with a small number of simultaneous requests per domain. Increase that limit only after measuring completion rates and response times.
  2. Reuse sessions: Keep one Requests session or browser context for each related workload. Session reuse preserves cookies and reduces repeated connection setup.
  3. Retry selectively: Retry temporary timeouts, `429` responses, and suitable server errors with capped backoff. Honor `Retry-After`, and stop retries that cannot change the result.
  4. Deduplicate globally: Normalize image URLs before adding them to the queue. Hash validated files afterward because different URLs can return identical bytes.
  5. Keep related requests together: Send a page request and its dependent image downloads through the same client session. Use a sticky proxy when the exit IP must also remain consistent.
  6. Route by geography: Assign workers to the country, city, or network required by the workload. Verify the returned page and image content instead of trusting an IP label alone.

Keep the page request and its image downloads within the same work unit. Rotate routes between independent units rather than during a linked sequence.

A worker queue should cap in-flight requests and isolate repeated failures by domain. Use a shared, atomic queue keyed by normalized URL to prevent duplicate work across workers.

Geographic routing becomes part of the crawler design when websites return different images by location. A managed proxy network can provide controlled regions and consistent exits across multiple workers.

Do You Need a Proxy to Scrape Images?

Many small image-scraping jobs can use one direct connection. A proxy becomes useful when the workload needs geographic routing, worker isolation, or consistent exit identities.

A forward proxy normally becomes the network peer seen by the destination and presents its public Internet Protocol (IP) address. However, `Forwarded` or `X-Forwarded-For` headers can still disclose the client's address.

A proxy controls routing, but it does not execute JavaScript, repair selectors, or validate image files. Test the route against both the page host and image hosts before scaling.

WorkloadUseful session behaviorReason
One static public pageDirect connection or one stable proxyRotation adds little value
Independent pages from one regionControlled rotating sessionsDistributes requests while preserving location
Page plus protected image filesSticky proxy and one client sessionHolds the exit route while the client retains cookies
Location-specific catalog imagesLocation-targeted sticky or rotating sessionReturns the intended regional version
Browser flow with scrollingSticky session for the full flowPrevents route changes during one rendered visit

IP rotation should follow the page boundary rather than changing between HTML and image requests. Changing routes can invalidate downloads when the target binds signed URLs or session cookies to the exit IP. That behavior makes route continuity part of session testing.

Proxidize Residential Proxies support country, city, and ISP targeting across 195+ countries. Proxidize Mobile Proxies support country, city, and ASN/ISP targeting. Both products provide rotating and sticky sessions.

Sticky sessions are especially relevant when the page request and image download must remain on the same exit IP. Reuse the same client or browser context because proxy stickiness does not preserve cookies by itself.

Residential proxies fit broad geographic collection and city- or ISP-specific results. Mobile proxies fit workloads where a mobile network route is part of the requirement.

What Common Mistakes Break an Image Scraper?

Image scrapers often fail after inspecting the wrong page state or trusting filenames. Ignored relative paths and unlimited downloads create additional failures. The table pairs each common mistake with a safer approach.

MistakeWhat happensBetter approach
Reading only `img[src]`Some responsive, custom-lazy, CSS, and generated sources can be missedInspect `srcset`, `<picture>`, custom attributes, computed styles, and rendered elements
Saving the URL filename directlyNames collide, contain query strings, or lack an extensionGenerate controlled names and map accepted declared media types
Trusting status `200` aloneA placeholder or unexpected document can be acceptedValidate media type, bytes, and expected content
Sending unlimited concurrent downloadsThe target and local machine can be overloadedCap concurrency and apply target-specific pacing
Running a browser for every pageProcessing and memory costs rise unnecessarilyUse Requests when the initial HTML already contains the sources

Another common mistake is losing session state between discovery and download. A page may set cookies before returning signed or protected image URLs. Use the same client session when required, and never copy credentials into source code or logs.

Duplicate files need more than duplicate-URL detection. Two different URLs can return identical bytes, while one URL can change. A larger pipeline can calculate a cryptographic hash after validation.

Store that hash with the source URL, page URL, file type, and dimensions. Do not treat a selector as permanent because websites change templates, lazy-loading libraries, and class names. Keep both a controlled fixture and a known live sample.

These samples expose count or file-type changes before a full run. Run the controlled fixture after every selector change. Compare the live sample before starting a large collection.

How Do You Scrape Images Responsibly?

Responsible image scraping requires an approved purpose, limited collection, and respectful request rates. Check whether the website offers an API, feed, download tool, or licensed dataset before scraping rendered pages. Those options can provide cleaner metadata and clearer usage terms.

Collect only the pages and image types needed for the stated project. Request for Comments (RFC) 9309, published in 2022, standardizes the Robots Exclusion Protocol. It explains that robots rules are crawler instructions rather than access authorization.

Respect crawler instructions, but do not treat them as the only legal, contractual, or ethical check. Review the website's terms, copyright notices, image licenses, privacy duties, and applicable law. Images can contain copyrighted work, faces, documents, location details, or other personal information.

Public visibility does not settle whether a particular collection, storage, model-training, or republication use is permitted. Avoid bypassing logins, paywalls, CAPTCHAs, or technical controls without authorization. Do not collect private or sensitive images merely because a URL can be discovered.

Obtain qualified legal review for a high-risk or commercial use. Operational responsibility also matters throughout the collection. Use clear identification where appropriate, limit concurrency, cache successes, and stop repeated failures.

Honor `Retry-After` when supplied. Keep source and license metadata with every file. Later users should understand each file's origin and restrictions.

What Should You Remember About Scraping Website Images?

  • Image scraping starts with source discovery and ends with validated file storage.
  • Requests and Beautiful Soup fit pages whose image sources already exist in the returned HTML.
  • Playwright fits pages that require JavaScript, scrolling, or browser-selected responsive images.
  • `urljoin` resolves relative paths, but server-side collectors still need an approved-host policy.
  • Status codes, media types, byte limits, and controlled filenames prevent many broken downloads.
  • Proxies should solve a measured routing or location requirement rather than compensate for faulty extraction logic.
  • Source URLs, licenses, and validation records should remain attached to every stored file.

Frequently asked questions

Image scraping legality depends on the source, jurisdiction, access method, image rights, and intended use. Public access alone does not settle copyright, privacy, license, or contractual questions. Review the site's terms and licenses, avoid restricted material, and seek legal advice for high-risk collection.

Beautiful Soup parses HTML and locates tags or attributes, but it does not download remote files by itself. Use an HTTP client such as Requests to retrieve the page and each selected image URL. Keep discovery and downloading separate so selector errors are easier to distinguish from network or validation failures.

Beautiful Soup sees the HTML returned to the HTTP client. Browsers also run JavaScript, apply CSS, and perform lazy loading and responsive selection. When sources are missing, use Playwright or Selenium to inspect `currentSrc`, computed styles, or relevant browser network responses.

Parse each candidate without splitting commas inside URL tokens, then resolve its address and retain its descriptor. Static parsing can collect HTTP candidates but cannot reproduce browser selection. Use `currentSrc` when viewport, density, media rules, or JavaScript determine the displayed resolution.

Small image-scraping jobs may use one direct connection with conservative pacing instead of a proxy. Proxies help with measured location, routing, isolation, or workload-scale requirements in production environments. They cannot render JavaScript, fix incorrect selectors, or validate downloaded image files.

Deduplicate normalized URLs before downloading, then hash validated file bytes when content-level duplicates matter. Different URLs can return the same image, and one URL can change. Store the hash beside the source URL, page URL, media type, and dimensions so duplicate handling remains traceable.

Yes. Inline `style` values and embedded CSS blocks can be parsed without rendering, while external URLs use the stylesheet base. A browser session resolves media queries, the final cascade, and JavaScript-added classes; inspect pseudo-elements separately because their computed styles differ.

Ready to launch?

Proxies built for real operations.

For teams that depend on stability, not luck.