ScreenshotNeo

BlogHow-to

How to View Images from Multiple URLs

Learn when to use a bulk image viewer, webpage extractor, or screenshot API, with runnable Python, Node.js, cURL, troubleshooting, and batch workflows.

By the ScreenshotNeo team1 October 20268 min read

Use the tool that matches what your URLs contain. Direct links ending in .jpg, .png, .webp, .gif, .svg, or .avif can be previewed or downloaded in bulk. URLs that open webpages require a renderer or image extractor to discover images loaded by JavaScript, lazy loading, srcset, <picture>, or CSS. If you need an image of each complete webpage, use a batch screenshot service.

1. Choose the right workflow

Your input Desired result Best approach Watch for
Direct image-file URLs Preview or download those files Bulk image URL viewer or downloader Hotlink blocking, unsupported formats, redirects
Webpage URLs containing images Find image assets on each page Browser-based image extractor Login walls, JavaScript, lazy loading, infinite scroll
Webpage URLs One screenshot per page Batch screenshot API Viewport, full-page behavior, failed URLs, retention

A direct-image viewer cannot discover assets hidden inside a webpage. A webpage extractor may return many variants of the same image. A screenshot service produces a page rendering, not the original image files.

2. Prepare and validate your URL list

  1. Put one URL per line, or create a CSV or JSON array supported by your tool.
  2. Remove private URLs unless the service supports your authentication context.
  3. Check that each URL uses https:// or http:// and is publicly reachable.
  4. Separate direct image links from page links before processing.
  5. Keep the original URL beside every result so failures can be retried.
https://cdn.example.com/photos/a.jpg
https://cdn.example.com/photos/b.webp
https://example.com/gallery/product-1
https://example.com/gallery/product-2

3. View direct image URLs in bulk

For known image files, a bulk viewer usually displays a grid and lets you open, save, copy URLs, or export a ZIP. Filter by file type, dimensions, or size when the tool supports it. Some hosts reject requests that do not include an expected referrer or block automated downloads; open a failed URL in its original page to confirm whether hotlink protection is the cause.

Python: download and create a contact sheet

The script below downloads direct image URLs, skips non-image responses, and creates a simple JPEG contact sheet. Install dependencies with pip install requests pillow.

from io import BytesIO
from pathlib import Path
import math
import requests
from PIL import Image, ImageDraw

URLS = [
    "https://example.com/image-1.jpg",
    "https://example.com/image-2.png",
]
OUT = Path("downloaded-images")
OUT.mkdir(exist_ok=True)

thumbs = []
for index, url in enumerate(URLS, start=1):
    try:
        response = requests.get(
            url,
            timeout=30,
            headers={"User-Agent": "Mozilla/5.0"},
        )
        response.raise_for_status()
        content_type = response.headers.get("content-type", "")
        if not content_type.startswith("image/"):
            print(f"Skipped (not an image): {url}")
            continue
        image = Image.open(BytesIO(response.content)).convert("RGB")
        path = OUT / f"image-{index}.jpg"
        image.save(path, quality=92)
        image.thumbnail((240, 180))
        thumbs.append((image.copy(), path.name))
    except Exception as error:
        print(f"Failed {url}: {error}")

if thumbs:
    columns = 4
    cell_w, cell_h = 260, 220
    rows = math.ceil(len(thumbs) / columns)
    sheet = Image.new("RGB", (columns * cell_w, rows * cell_h), "white")
    draw = ImageDraw.Draw(sheet)
    for i, (thumb, name) in enumerate(thumbs):
        x = (i % columns) * cell_w
        y = (i // columns) * cell_h
        sheet.paste(thumb, (x + 10, y + 10))
        draw.text((x + 10, y + 195), name, fill="black")
    sheet.save("contact-sheet.jpg", quality=90)

cURL: verify a direct image

curl -L --fail --max-time 30 \
  -H "User-Agent: Mozilla/5.0" \
  "https://example.com/image.jpg" \
  -o image.jpg

Use -I first when you only need headers. Confirm the response has an image content type and a successful status.

4. Extract images from multiple webpages

Static HTML parsing finds ordinary <img src> elements, but it misses images inserted after JavaScript runs, lazy-loaded images, responsive sources, and CSS backgrounds. A browser extractor can scroll, wait for content, inspect srcset and <picture>, and collect the resulting asset URLs. Infinite feeds still have a practical boundary: a finite scroll window cannot guarantee every item.

Python: collect image sources from static HTML

Install pip install requests beautifulsoup4. This handles src, data-src, srcset, and absolute URL resolution.

from urllib.parse import urljoin
import requests
from bs4 import BeautifulSoup

PAGES = [
    "https://example.com/gallery/product-1",
    "https://example.com/gallery/product-2",
]

for page_url in PAGES:
    response = requests.get(page_url, timeout=30, headers={"User-Agent": "Mozilla/5.0"})
    response.raise_for_status()
    soup = BeautifulSoup(response.text, "html.parser")
    found = set()

    for tag in soup.select("img"):
        for attribute in ("src", "data-src", "data-lazy-src"):
            value = tag.get(attribute)
            if value:
                found.add(urljoin(page_url, value))
        srcset = tag.get("srcset") or tag.get("data-srcset")
        if srcset:
            for candidate in srcset.split(","):
                found.add(urljoin(page_url, candidate.strip().split(" ")[0]))

    print(f"\n{page_url}")
    for image_url in sorted(found):
        print(image_url)

Node.js with Playwright: render JavaScript and scroll

Install with npm install playwright and run npx playwright install chromium once.

import { chromium } from "playwright";

const pages = [
  "https://example.com/gallery/product-1",
  "https://example.com/gallery/product-2",
];

const browser = await chromium.launch({ headless: true });
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });

for (const url of pages) {
  await page.goto(url, { waitUntil: "networkidle", timeout: 60000 });
  for (let i = 0; i < 5; i++) {
    await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
    await page.waitForTimeout(800);
  }
  const images = await page.evaluate(() => {
    const absolute = value => {
      try { return new URL(value, location.href).href; } catch { return null; }
    };
    const urls = new Set();
    for (const image of document.images) {
      for (const value of [image.currentSrc, image.src, image.dataset.src, image.dataset.lazySrc]) {
        const resolved = value && absolute(value);
        if (resolved) urls.add(resolved);
      }
      for (const candidate of (image.srcset || "").split(",")) {
        const resolved = candidate && absolute(candidate.trim().split(/\s+/)[0]);
        if (resolved) urls.add(resolved);
      }
    }
    return [...urls];
  });
  console.log(JSON.stringify({ page: url, images }, null, 2));
}

await browser.close();

CSS background images require inspecting computed styles, for example getComputedStyle(element).backgroundImage. SVG may be inline markup rather than a downloadable URL, so decide whether you want the SVG source or a rendered raster image.

5. Compare complete webpages with batch screenshots

Use screenshots when the question is “How does each page look?” rather than “Which image files are embedded?” A batch service can render each URL in a browser and return one image per URL. Keep a manifest mapping input URLs to output files and record failures separately. Vendor documentation for url2image describes list, CSV/text, and JSON input, a maximum of 500 URLs per batch, one retry for a failed URL, a ZIP containing a manifest, and a not-rendered.csv; these are vendor claims and can change.

6. Or skip the browser setup

ScreenshotNeo returns a clean screenshot from one GET request and supports PNG, JPEG, WebP, or PDF output. It can capture full pages, selected elements, custom viewports, device presets, dark mode, retina scale, lazy-loaded images, custom CSS and JavaScript, waits, blocking rules, authentication headers and cookies, geolocation, time zones, transparent backgrounds, resizing, caching, signed links, asynchronous jobs, webhooks, usage reporting, and bulk capture of up to 100 URLs per call. See the ScreenshotNeo API documentation for request options.

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}`);

Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response reports the result through X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.

7. Handle incomplete results

Symptom Likely cause Fix
403 or 429 response Hotlink protection, rate limiting, or missing headers Use the original page context, slow requests, send an appropriate user agent, or use authenticated access where permitted.
HTML returned instead of an image The URL is a page, redirect, login screen, or error document Check status and Content-Type; follow redirects; authenticate if the page is yours.
Images missing from extraction JavaScript or lazy loading has not run Use a real browser, wait for network idle, scroll incrementally, and inspect srcset and CSS backgrounds.
Only the first feed items appear Infinite scroll or a finite scan window Define a maximum scroll depth, use pagination endpoints when allowed, or process batches of page URLs.
Blank or partial screenshot Slow resources, bot checks, viewport mismatch, or timeout Increase wait and timeout values, choose full-page capture, block unnecessary resources, and retry failed URLs separately.
Duplicate images Thumbnail, original, and responsive variants are all exposed Normalize URLs, compare dimensions or hashes, and retain the largest permitted variant.
Private content is absent The request has no session cookies or authorization Use a controlled authenticated browser context and confirm that sharing the content is allowed.

8. Performance, reliability, and cost

  • Batch size: Smaller groups make retries and diagnosis easier. Start with 20–100 URLs, then increase after measuring failure rates.
  • Concurrency: Limit simultaneous requests to avoid rate limits and overloaded origin servers. Use exponential backoff for 429 and transient 5xx responses.
  • Rendering: Browser rendering costs more time and memory than fetching HTML, but it is necessary for JavaScript and lazy content.
  • Caching: Cache successful downloads by normalized URL and checksum. For screenshots, choose a TTL that matches how often the source changes.
  • Failure accounting: Save status, final URL, content type, byte count, and error message for every input. Never assume a ZIP is complete without checking its manifest.
  • Hosted processing: Review retention, privacy, and terms before sending sensitive URLs or authenticated page content. Image licenses and site terms still apply to anything you download or reuse.

9. A practical checklist

  • Classify each URL as a direct image, webpage, or page to screenshot.
  • Confirm access, redirects, and content type.
  • Use browser rendering for JavaScript, lazy loading, and responsive images.
  • Set explicit timeout, viewport, scroll, and batch limits.
  • Deduplicate variants and preserve source-to-result mappings.
  • Retry failures separately and inspect login, bot-check, and hotlink causes.
  • Review permissions, copyright, privacy, and retention before exporting or sharing.

10. FAQ

Can I use a normal image viewer for webpage URLs?

No. A normal viewer expects the URL itself to return image bytes. Use an extractor for pages that contain images.

Will HTML parsing find every image?

No. It can miss JavaScript-created elements, lazy-loaded assets, CSS backgrounds, and images behind authentication.

Should I download originals or thumbnails?

Choose based on your purpose and rights. Inspect dimensions and file size, then keep the largest permitted source only when you need maximum resolution.

How do I compare page designs across many URLs?

Capture each page with the same viewport, device scale, wait policy, and full-page setting, then compare the resulting images.

What happens when one URL fails in a batch?

Record it in a manifest, retry with a slower or authenticated request when appropriate, and do not treat missing output as a successful empty result.