Skip to main content
When you need to extract data from multiple web pages, you can process a list of URLs using either sequential or parallel approaches. Sequential processing handles URLs one at a time, while parallel processing (handling multiple requests simultaneously) significantly reduces scraping time for large URL lists.
This guide assumes you already have a list of URLs ready for scraping
If you need to discover URLs from a starting page, check our Scrape and Crawl from a Seed URL.
The techniques shown in this guide work with any programming language that supports HTTPS requests (PHP, Java, Ruby, Go, C#, etc.). We’re using Python and Node.js examples for simplicity, but you can adapt these patterns to your preferred language.

Prerequisites

Python Setup

You’ll need Python 3 installed. Install the required libraries:
The requests library handles HTTP requests to ZenRows, while beautifulsoup4 parses HTML content for data extraction.

Node.js Setup

You’ll need Node.js installed. Install the required packages:
The axios library handles HTTP requests, while cheerio provides server-side HTML parsing similar to jQuery.

Sequential Processing

Sequential processing handles URLs one after another in a simple loop. This approach works well for small URL lists or when you want to avoid overwhelming target servers with concurrent requests.

Basic sequential scraping

This code sends each URL to ZenRows sequentially and prints the returned HTML. The loop processes URLs in order, waiting for each request to complete before moving to the next.

Extracting specific data

To make the scraper more useful, extract specific data like page titles and headings:
The extract_content function parses each page and extracts the title and main heading. Results are stored in a list for further processing. The function includes safety checks to handle pages missing title or H1 elements.

Adding error handling

For production use, add error handling to manage failed requests:
This version includes try-catch blocks to handle network errors, validates HTTP responses, and adds a delay between requests to be respectful to servers.

Parallel Processing

Parallel processing handles multiple URLs simultaneously, dramatically reducing total scraping time. However, you must manage concurrency carefully to avoid overwhelming servers or exceeding rate limits.
While these examples use Python and Node.js, the same parallel processing concepts apply to other languages. Most modern programming languages provide similar concurrency features (async/await, thread pools, promises, etc.).

Using standard libraries

This approach uses ThreadPoolExecutor in Python and Promise.all in Node.js to handle multiple requests simultaneously. The concurrency is limited by the max_workers parameter in Python and naturally managed by Node.js’s event loop.

Controlling concurrency with batching

For better control over concurrency, process URLs in batches:
This batching approach processes URLs in smaller groups, giving you better control over server load and memory usage. You can adjust batch size and concurrency based on your specific needs.

Using ZenRows SDKs

ZenRows SDKs simplify parallel web scraping by providing built-in concurrency management, automatic error handling, and connection pooling. When you’re scraping multiple URLs, the SDKs handle the complex orchestration automatically, so you can focus on extracting the data you need rather than managing technical details.
First, install the ZenRows Python SDK:
Python SDK
The SDKs handle connection pooling, automatic retries, and concurrency limits automatically. The concurrency=5 parameter ensures no more than 5 requests run simultaneously.

Processing large URL lists with SDKs

For very large URL lists, process them in batches to manage memory usage:
This approach processes URLs in batches of 50, reducing memory usage and providing progress updates for long-running scraping jobs.

Best Practices

Follow these guidelines for efficient URL list scraping:
  1. Start small - Test with a few URLs before scaling up
  2. Monitor performance - Track success rates and response times
  3. Handle errors gracefully - Always include error handling for production code
  4. Respect rate limits - Don’t exceed your plan’s concurrency limits
  5. Save progress - For large jobs, save results incrementally to avoid data loss
  6. Use appropriate concurrency - Balance speed with server respect and resource usage

Understanding Concurrency Limits

The concurrency level you choose affects both performance and resource usage:
  • Target server capacity - Some servers handle more concurrent requests than others
  • Your ZenRows plan limits - Higher plans support more concurrent requests
  • Data processing speed - Don’t set concurrency higher than your ability to process results
  • Memory usage - Higher concurrency uses more memory for storing responses
For detailed concurrency management and rate limiting information, check our Concurrency Guide.

Further Reading