To bulk screenshot URLs while skipping missing pages, navigate to each URL in a browser, check the main document’s HTTP status, and take a screenshot only when it is not 404. In Playwright, use the response returned by page.goto(); a 404 is a completed HTTP response, not a request failure. Log it and skip the capture explicitly.
Why a 404 needs an explicit check
An HTTP 404 means the browser reached a server and received a response saying the requested resource was not found. It is different from a timeout, DNS failure, TLS error, or unreachable host, where a response may never arrive. Playwright documents that HTTP error statuses such as 404 and 503 still complete as HTTP requests rather than triggering requestfailed. Therefore, a request-failed listener alone will not reliably identify 404 pages.
Use the main navigation response as the basis for the decision. Decide separately how your run should handle other statuses—such as 401, 403, 429, or 5xx—rather than treating every non-success status as a 404.
Bulk screenshot URLs with Playwright
The following Node.js script reads one URL per line from urls.txt, validates that each URL uses HTTP or HTTPS, follows browser navigation, skips a final 404 response, and saves screenshots and a CSV run log. It captures other HTTP statuses so you can make a distinct decision about them later. A navigation exception is recorded as an error, not mislabeled as a 404.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minutePC 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 & 11#1 Best Overall
- ULTRA HD 4K CLARITY: Stand out in every video call with breathtaking 4K video at 30fps or smooth 1080p at 60fps. Powered by a premium 1/2.5" CMOS sensor and a wide f/1.78 aperture, this webcam captures every detail with vibrant color and stunning low-light performance-so you always look your best
- FAST AUTOFOCUS & SMART LIGHT CORRECTION: No more blurry moments with this webcam for PC. Advanced Phase Detection Auto Focus (PDAF) locks onto your face instantly and keeps you sharp-even when you move. Built-in light correction adapts to your environment, balancing brightness and contrast for a flawless image in dim rooms or bright spaces
- DUAL NOISE-CANCELING MICS: Speak with confidence using this webcam with microphones. Dual microphones with intelligent noise-canceling tech isolate your voice and reduce background noise-suitable for webinars, live streams, team meetings, and virtual interviews
- WIDE-ANGLE LENS & FLEXIBLE MOUNTING OPTIONS: Capture more of your world with an 80 field of view and full 360 swivel rotation. Whether this streaming webcam is mounted on a laptop, monitor, or tripod, it allows you to find the right angle for any setup
- BUILT-IN PRIVACY COVER & PLUG-AND-PLAY SIMPLICITY: Protect your privacy with a secure sliding lens cover that blocks the camera when not in use. Setup is a breeze-just plug into any USB-A port and start streaming, chatting, or recording instantly. The USB webcam is compatible with Zoom, Microsoft Teams, Skype, OBS Studio, and all major platforms across Windows, macOS, and Linux
1. Install Playwright
In a new project directory, install Playwright and its Chromium browser:
npm init -y
npm install playwright
npx playwright install chromium
2. Create the URL list
Put one URL on each line in urls.txt. Blank lines and lines beginning with # are ignored.
https://example.com/
https://example.com/missing-page
https://www.example.org/
3. Save and run the script
Save this as bulk-screenshot.js in the same directory. It uses a small worker pool, a per-navigation timeout, stable numbered filenames, and a CSV mapping each input to its final URL, status, outcome, and error. The concurrency and timeout values below are starting choices, not performance guarantees; tune them for the sites and machine involved.
const fs = require('node:fs/promises');
const path = require('node:path');
const { chromium } = require('playwright');
const INPUT_FILE = 'urls.txt';
const OUTPUT_DIR = 'screenshots';
const LOG_FILE = 'run.csv';
const CONCURRENCY = 3;
const NAVIGATION_TIMEOUT_MS = 30_000;
function csv(value) {
return `"${String(value ?? '').replaceAll('"', '""')}"`;
}
function safeHost(rawUrl) {
try {
return new URL(rawUrl).hostname.replace(/[^a-zA-Z0-9.-]/g, '_');
} catch {
return 'invalid-url';
}
}
async function main() {
const lines = (await fs.readFile(INPUT_FILE, 'utf8'))
.split(/r?n/)
.map(line => line.trim())
.filter(line => line && !line.startsWith('#'));
const jobs = lines.map((rawUrl, index) => ({ rawUrl, index: index + 1 }));
await fs.mkdir(OUTPUT_DIR, { recursive: true });
const browser = await chromium.launch({ headless: true });
const results = new Array(jobs.length);
let next = 0;
async function worker() {
while (true) {
const jobIndex = next++;
if (jobIndex >= jobs.length) return;
const { rawUrl, index } = jobs[jobIndex];
let finalUrl = '';
let status = '';
let outcome = '';
let error = '';
let context;
try {
const parsed = new URL(rawUrl);
if (!['http:', 'https:'].includes(parsed.protocol)) {
throw new Error('URL must use http or https');
}
context = await browser.newContext();
const page = await context.newPage();
page.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT_MS);
const response = await page.goto(rawUrl, { waitUntil: 'domcontentloaded' });
finalUrl = page.url();
if (!response) {
outcome = 'no-main-response';
} else {
status = response.status();
if (status === 404) {
outcome = 'skipped-404';
} else {
const filename = `${String(index).padStart(5, '0')}-${safeHost(finalUrl)}.png`;
await page.screenshot({ path: path.join(OUTPUT_DIR, filename), fullPage: true });
outcome = `captured-${status}`;
}
}
} catch (err) {
outcome = 'navigation-or-capture-error';
error = err instanceof Error ? err.message : String(err);
} finally {
if (context) await context.close();
}
results[jobIndex] = [index, rawUrl, finalUrl, status, outcome, error];
console.log(`${index}/${jobs.length}: ${outcome} ${rawUrl}`);
}
}
try {
await Promise.all(Array.from({ length: Math.min(CONCURRENCY, jobs.length) }, () => worker()));
} finally {
await browser.close();
}
const header = ['input_number', 'requested_url', 'final_url', 'http_status', 'outcome', 'error'];
const csvText = [header, ...results].map(row => row.map(csv).join(',')).join('n');
await fs.writeFile(LOG_FILE, `${csvText}n`, 'utf8');
console.log(`Done. See ${OUTPUT_DIR}/ and ${LOG_FILE}.`);
}
main().catch(err => {
console.error(err);
process.exitCode = 1;
});
Run it with node bulk-screenshot.js. Each captured image goes in screenshots/; run.csv preserves the mapping even when two inputs share a hostname. The script treats a missing main response as its own outcome and does not call it a 404.
Adjust the status policy deliberately
The example skips only a final status of exactly 404. If you also want to skip other statuses, add explicit cases before the screenshot call and give each a distinct outcome such as skipped-401 or skipped-5xx. A non-404 response is not automatically a usable page: an authorization error, rate limit, or server error may warrant a separate skip policy.
Rank #2
- 【Efficient Quad-Core Performance】 Powered by a 1.8GHz Quad-Core processor, this mini laptop ensures smooth multitasking. With 2GB RAM and 64GB ROM (expandable to 1TB), it handles daily work and online tasks with ease.
- 【10.1" HD IPS Display & GMS Support】 Featuring a 1280x800 HD IPS screen, this cheap laptop delivers vibrant visuals. Pre-installed with Android OS and GMS, you get direct access to the Google Play Store for apps.
- 【Ultra-Portable & Lightweight Design】 Weighing only 1.76 lbs, this Blue computer is designed for mobility. Its compact form makes it an ideal companion for students and professionals for home schooling or trips.
- 【Versatile Connectivity Options】 Stay productive with dual USB 2.0 ports, a headphone jack, and a TF card slot. This computer for kids and adults features built-in Wi-Fi and Bluetooth for stable connections.
- 【Complete All-in-One Bundle】 This kid laptop kit includes the laptop, carrying bag, mouse, mouse pad, and power adapter. It is the perfect ready-to-use set for online classes, remote work, and entertainment.
Choose when to capture
The script waits for domcontentloaded, which avoids waiting for every image or background resource to finish. Pages that render important content later may need a page-specific wait, such as waiting for a known selector or a short delay before capture. No single wait condition is right for every site; a blanket wait for network idle can make a large batch slower or fail on pages with persistent network activity.
Redirects, logs, and retry policy
A redirect creates another request. The script makes its skip decision using the response returned after navigation and logs both the submitted URL and page.url(), the final URL reached by the browser. This makes cases such as an old URL redirecting to a missing destination auditable. If your rule should instead be based on an intermediate redirect response, track navigation responses explicitly and define that policy before processing the batch.
- 404: HTTP response received; skipped by the script.
- Other HTTP status: response received; captured by the example and labeled with its status.
- Navigation or capture exception: no successful completion of the operation; recorded in the error column, not classified as 404.
- No main response: separately labeled for review rather than treated as an HTTP status.
For transient failures, consider a limited retry with a delay, but retry only the errors your workflow considers transient. Do not retry a 404 as if it were a connection failure. Preserve each attempt or at least its final outcome in the log so an operator can distinguish a missing page from temporary unavailability.
Other ways to organize a multi-URL capture
Playwright
Playwright is a fit when you need the status-aware decision in your own code: navigate, inspect the main response, then capture or skip. Its page API documents navigation and screenshots, while its request documentation distinguishes response events from request failures. The script above handles the conditional logic rather than relying on a generic failure event.
shot-scraper
shot-scraper is a command-line browser screenshot utility whose documentation describes capturing multiple URLs from a configuration file. That multi-URL capability alone does not establish conditional skipping based on HTTP 404; pair it with a status-aware wrapper or verify the behavior you need before using it for this task.
Rank #3
- Webcam comes with a 3-month XSplit VCam license and no privacy shutter. XSplit VCam lets you remove, replace and blur your background without a Green Screen.
- Full HD 1080p video calling and recording at 30 fps - You’ll make a strong impression when it counts with crisp, clearly detailed and vibrantly colored video.
- Stereo audio with dual mics - Capture natural sound on calls and recorded videos.
- Custom three-capsule array: This professional USB mic produces clear, powerful, broadcast-quality sound for YouTube videos, Twitch game streaming, podcasting, Zoom meetings, music recording and more
- Blue VOICE software: Elevate your streamings and recordings with clear broadcast vocal sound and entertain your audience with enhanced effects, advanced modulation and HD audio samples
Hosted batch screenshot APIs
A hosted API can reduce browser-infrastructure work, and a reviewed vendor API documents batch submission, batch IDs for tracking, errors, and rate limits. Those capabilities do not prove that the service omits individual URLs that return 404. Confirm conditional status handling, redirect behavior, per-URL results, and plan limits with the provider before relying on it. Limits and quotas can vary by plan and change over time.
Puppeteer
Puppeteer’s Page API supports browser-page screenshots, but screenshot capability by itself is not a ready-made bulk workflow or a documented 404-skip policy. You would still need to implement URL iteration, status checks, logging, and failure handling.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server. Its one-request endpoint is useful when you want the service to render a URL instead of managing a local browser. This basic call captures one URL; it does not replace the custom per-URL 404 policy in the Playwright script, so check the response verdict/status and decide what to do for each URL in your own workflow.
See the ScreenshotNeo API documentation. Example using cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
- Cookie and consent banners are accepted like a visitor; more than 60 known consent platforms, newsletter popups, and chat widgets are removed before the shot, and each step can be turned off.
- Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed; responses include
X-Page-VerdictandX-Billedheaders. - An MCP server provides
take_screenshot,get_page_info, andcapture_pdftools for Claude, Cursor, or another MCP client. - The Free plan includes 1,000 shots per month with no card required; paid plans start at $5 for 3,000 shots. Every feature is available on every plan.
Sign up for ScreenshotNeo’s free plan to get 1,000 screenshots a month with no card.
Rank #4
- Compatible with Logitech C920x HD Pro Webcam, Full HD 1080p/30fps Video Calling. Compatible with Logitech C920 Hd Pro Webcam. Compatible with Logitech HD Pro Webcam C920 Widescreen Video Calling and Recording Webcam.
- Compatible with Logitech C930e Webcam. Compatible with Logitech C922 Pro Stream Webcam 1080P Camera for HD Video Streaming. Compatible with Logitech Privacy Cover for C920 and C930e.
- This webcam cover conveniently blocks your camera cover to protect your privacy.
- This also compatible with other popular webcams. This is also known as webcam lid, webcam cap, webcam protector, web camera privacy cover.
- ienza is a registered trademark and a registered Amazon brand. Use of the ienza trademark without the prior written consent of ienza, LLC. may constitute trademark infringement and unfair competition in violation of federal and state laws. ienza products are developed as cost-effective alternatives to OEM parts. They are not necessarily endorsed by the OEMs
Troubleshooting the batch
A 404 page is still being captured
Check that you are testing the main navigation response returned from page.goto(), and that your skip condition runs before page.screenshot(). Do not expect a requestfailed event for an HTTP 404; it is a completed response.
Free tools Windows power users keep installed
One-click scans. No signup required.
A timeout appears in the log
A timeout is not a 404. It means navigation did not finish within the configured interval. Review whether the site is slow or persistently active, adjust the timeout for your needs, and consider a limited retry. Keep the error outcome distinct from HTTP responses.
The final URL differs from the input
That can happen after redirects. Use the logged final URL to investigate where navigation ended and confirm that your policy applies to the final response rather than an earlier redirect.
Some pages are blank or incomplete
domcontentloaded does not guarantee that client-rendered content or lazy-loaded images are ready. Wait for a page-specific selector or other appropriate condition before capturing. If you change the wait condition globally, monitor its effect on run duration and timeouts.
The run stops or overwhelms a site
Reduce CONCURRENCY, keep the navigation timeout bounded, and use limited retries only for transient failures. The example’s concurrency value is a configurable starting point, not a tested capacity recommendation. Respect the target sites’ access policies and avoid sending requests faster than they can handle.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Output files are hard to map back to inputs
Use the CSV as the source of truth: it records input number, requested URL, final URL, status, and outcome. Numbered filenames avoid collisions when different paths on the same host are captured.
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.

