Awwwards Nominee Awwwards Nominee

How to create Scalable Web Scraping Pipelines Using Python and Scrapy

by : deepak-chauhan Category : Web Scraping Date :
How to create Scalable Web Scraping Pipelines Using Python and Scrapy

Web scraping has become an essential technique for extracting data from websites, but as data needs grow, the ability to scale efficiently becomes critical. Scalability ensures that a scraping pipeline in scalable web scraping can handle increasing workloads without failures, delays, or excessive resource consumption.

Python, along with Scrapy, offers a powerful framework for building scalable web scraping pipelines. Scrapy provides an asynchronous architecture, efficient data handling, and built-in support for exporting data in various formats. We will explore how to create a scalable web scraping pipeline using Python and Scrapy while optimizing performance, handling anti-scraping measures, and ensuring reliability.

Challenges in Large-Scale Web Scraping

A small-scale scraper is easy to build, but scaling it up introduces challenges:
IP Bans & Rate Limiting – Websites block excessive requests
Dynamic Content – JavaScript-based sites require special handling
Data Cleaning & Storage – Extracted data needs structuring and storage
Scalability – Scrapers must process millions of pages efficiently

Integrating custom API development can help streamline data exchange between scraping systems and other applications, ensuring structured, real-time data flow.

Why Use Scrapy for Scalable Web Scraping?

scrapy tool image

Scrapy is a Python-based web scraping framework designed for large-scale data collection. It offers:

  • Asynchronous request handling for high-speed scraping
  • Built-in data pipelines to clean, validate, and store data
  • Middleware support for handling proxies, user agents, cookies
  • Auto-throttling to prevent bans and optimize performance

For seamless integration of scraped data into your systems, leveraging Backend Development can enhance data management, storage, and real-time processing capabilities. This ensures that large volumes of data are efficiently handled and accessible for business applications. Leveraging custom web development services can provide a user-friendly dashboard for managing and visualizing scraped data in real-time, enhancing control and monitoring capabilities.

Setting Up a Scrapy Environment

Installing Scrapy and Dependencies

To install Scrapy and related libraries, run:

pip install scrapy scrapy-rotating-proxies scrapy-selenium pandas psycopg2 pymongo requests lxml beautifulsoup4

Creating a Scrapy Project

Initialize a new Scrapy project:

scrapy startproject scalable_scraper

cd scalable_scraper

Writing a Scalable Scrapy Spider

A Scrapy Spider controls:

  • Which pages to scrape
  • How data is extracted
  • How pagination is handled
Creating a Product Scraper

Navigate to spiders/ and create product_spider.py:

import scrapy

class ProductSpider(scrapy.Spider):
    name = "products"
    start_urls = ["https://example.com/products"]

    def parse(self, response):
        for item in response.css("div.product"):
            yield {
                "name": item.css("h2::text").get(),
                "price": item.css("span.price::text").get(),
                "url": response.urljoin(item.css("a::attr(href)").get()),
            }

        # Handling pagination
        next_page = response.css("a.next::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)
Running the Scraper

Run the spider with:

scrapy crawl products -o output.json

This will save the extracted data in output.json.

Optimizing Scrapy for Large-Scale Scraping

To scrape thousands of pages efficiently, optimize settings.py:

CONCURRENT_REQUESTS = 64  # Increase parallel requests
DOWNLOAD_DELAY = 0.2  # Prevents overloading the server
AUTOTHROTTLE_ENABLED = True  # Dynamically adjusts request speed
AUTOTHROTTLE_START_DELAY = 1
AUTOTHROTTLE_TARGET_CONCURRENCY = 5

Handling Anti-Scraping Techniques

Websites employ various techniques to block scrapers:

  • IP Bans – Blocking repeated requests from the same IP
  • CAPTCHAs – Requiring human interaction
  • JavaScript-rendered Content – Hiding data behind scripts

Rotating User Agents

Modify settings.py to randomize user agents:

USER_AGENT_LIST = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
    "Mozilla/5.0 (Linux; Android 10)"
]

DOWNLOADER_MIDDLEWARES.update({
    'scrapy.downloadermiddlewares.useragent.UserAgentMiddleware': None,
    'scrapy_user_agents.middlewares.RandomUserAgentMiddleware': 400,
})

Using Proxy Rotation

Install the scrapy-rotating-proxies package:

pip install scrapy-rotating-proxies

Modify settings.py to use proxies:

ROTATING_PROXY_LIST = [
    "http://proxy1:port",
    "http://proxy2:port",
]

Storing Scraped Data Efficiently

Once data is scraped, it must be stored properly for analysis. Integrating custom CMS development can help businesses organize, categorize, and update scraped data with ease, providing a more manageable and editable data platform. Common storage options:

  • PostgreSQL – Best for structured, relational storage
  • MongoDB – Ideal for flexible, NoSQL document storage
  • CSV/JSON – Good for basic file-based storage

Storing Scraped Data in PostgreSQL

Install PostgreSQL Driver
pip install psycopg2
Create a PostgreSQL Database
CREATE DATABASE scraped_data;
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name TEXT,
    price TEXT,
    url TEXT
);
Modify pipelines.py to Store Data
import psycopg2

class PostgresPipeline:
    def open_spider(self, spider):
        self.connection = psycopg2.connect(
            dbname="scraped_data",
            user="your_user",
            password="your_password",
            host="localhost"
        )
        self.cursor = self.connection.cursor()

    def process_item(self, item, spider):
        self.cursor.execute(
            "INSERT INTO products (name, price, url) VALUES (%s, %s, %s)",
            (item["name"], item["price"], item["url"])
        )
        self.connection.commit()
        return item

    def close_spider(self, spider):
        self.cursor.close()
        self.connection.close()

Modify settings.py to enable this pipeline:

ITEM_PIPELINES = {
    'scalable_scraper.pipelines.PostgresPipeline': 300,
}

Storing Scraped Data in MongoDB

While PostgreSQL is great for structured data, MongoDB is ideal for storing semi-structured data like JSON. It’s widely used for large-scale scraping projects that need flexibility in data storage.

Installing MongoDB Driver

To interact with MongoDB in Python, install the pymongo library:

pip install pymongo

Setting Up a MongoDB Database

Start MongoDB and create a new database:

mongo
use scraped_data
db.createCollection("products")

Modifying pipelines.py for MongoDB

Edit pipelines.py to store data in MongoDB:

import pymongo

class MongoDBPipeline:
    def open_spider(self, spider):
        self.client = pymongo.MongoClient("mongodb://localhost:27017/")
        self.db = self.client["scraped_data"]
        self.collection = self.db["products"]

    def process_item(self, item, spider):
        self.collection.insert_one(dict(item))
        return item

    def close_spider(self, spider):
        self.client.close()

Enabling the MongoDB Pipeline

Modify settings.py to enable MongoDB:

ITEM_PIPELINES = {
    'scalable_scraper.pipelines.MongoDBPipeline': 300,
}

Data Cleaning and Processing with Pandas

Once data is scraped, it needs cleaning before analysis. Pandas is the best Python library for this.

Installing Pandas

pip install pandas

Cleaning and Formatting Data

Modify pipelines.py to clean scraped data before saving:

import pandas as pd
class DataCleaningPipeline:
    def process_item(self, item, spider):
        # Remove extra spaces from product name
        item["name"] = item["name"].strip() if item["name"] else "N/A"

        # Convert price to float
        try:
            item["price"] = float(item["price"].replace("$", ""))
        except:
            item["price"] = None
        return item

Exporting Data to CSV

You can also save data in CSV format for analysis:

class SaveToCSV:
    def open_spider(self, spider):
        self.data = []

    def process_item(self, item, spider):
        self.data.append(item)
        return item

    def close_spider(self, spider):
        df = pd.DataFrame(self.data)
        df.to_csv("scraped_data.csv", index=False)

Modify settings.py to enable both cleaning and CSV saving:

ITEM_PIPELINES = {
    'scalable_scraper.pipelines.DataCleaningPipeline': 200,
    'scalable_scraper.pipelines.SaveToCSV': 300,
}

Logging & Error Handling in Scrapy

To make your scraper more robust, implement logging and error handling.

Enabling Logging in Scrapy

Modify settings.py to enable logs:

LOG_LEVEL = "INFO"  # Options: DEBUG, INFO, WARNING, ERROR
LOG_FILE = "scrapy_log.txt"

Adding Error Handling in Spiders

Modify product_spider.py to handle errors:

import scrapy
import logging

class ProductSpider(scrapy.Spider):
    name = "products"
    start_urls = ["https://example.com/products"]

    def parse(self, response):
        try:
            for item in response.css("div.product"):
                yield {
                    "name": item.css("h2::text").get(default="N/A"),
                    "price": item.css("span.price::text").get(default="N/A"),
                    "url": response.urljoin(item.css("a::attr(href)").get(default="")),
                }

            next_page = response.css("a.next::attr(href)").get()
            if next_page:
                yield response.follow(next_page, self.parse)

        except Exception as e:
            logging.error(f"Error in parsing: {e}")

Running the Full Scraping Pipeline

Now that everything is set up, run the full pipeline:

scrapy crawl products

To store output in JSON format:

scrapy crawl products -o output.json

To run silently with logs:

scrapy crawl products --nolog

Deploying Scrapy on a Cloud Server

Once your web scraper is working locally, you’ll need to deploy it on a cloud server to run at scale. Common options include:

  • AWS EC2 – Flexible and scalable compute power
  • DigitalOcean Droplets – Affordable and easy to set up
  • Google Cloud Compute Engine – Powerful infrastructure

Setting Up a Cloud Server

For DigitalOcean, create a droplet with Ubuntu:

ssh root@your_server_ip

For AWS, launch an EC2 instance and connect:

ssh -i your-key.pem ubuntu@your-ec2-instance

Installing Scrapy on the Server

Update system packages and install dependencies:

sudo apt update && sudo apt upgrade -y
sudo apt install python3-pip
pip install scrapy scrapy-rotating-proxies scrapy-selenium pymongo psycopg2 pandas

Running Scrapy on the Cloud

Upload your Scrapy project:

scp -r scalable_scraper/ root@your_server_ip:/home/

Run the scraper in the background using nohup:

nohup scrapy crawl products > output.log 2>&1 &

Scheduling Scrapers with Cron Jobs

To automate your scraping pipeline, schedule it using cron jobs on Linux.

Editing Crontab

Run:

crontab -e

Add a job to run the scraper every day at 3 AM:

0 3 * * * cd /home/scalable_scraper && scrapy crawl products > output.log 2>&1

Advanced Scrapy Middleware for Anti-Ban Protection

To avoid bans, Scrapy allows custom middleware for handling headers, proxies, and delays.

Custom Headers Middleware

Modify middlewares.py to randomize request headers:

from scrapy import signals
import random
class RandomHeaderMiddleware:
    user_agents = [
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
        "Mozilla/5.0 (Linux; Android 10)",
    ]

    def process_request(self, request, spider):
        request.headers["User-Agent"] = random.choice(self.user_agents)

Enabling the Middleware

Modify settings.py:

DOWNLOADER_MIDDLEWARES.update({
    'scalable_scraper.middlewares.RandomHeaderMiddleware': 400,
})

Using Scrapy-Selenium for JavaScript-Rendered Content

Many modern websites use JavaScript to load content, making it invisible to Scrapy’s default parser. Selenium allows you to scrape JavaScript-heavy websites. Although Python development is the foremost choice for web scraping, Frontend Development plays a critical role in understanding how dynamic content is rendered. Additionally, NodeJS development for scraping projects is also well-suited for websites that depend heavily on JavaScript.

Installing Selenium and WebDriver

pip install scrapy-selenium
sudo apt install chromium-chromedriver  # Ubuntu/Linux users

Configuring Selenium in Scrapy

Modify settings.py:

from shutil import which

SELENIUM_DRIVER_NAME = 'chrome'
SELENIUM_DRIVER_EXECUTABLE_PATH = which("chromedriver")
SELENIUM_BROWSER_EXECUTABLE_PATH = which("chromium-browser")

DOWNLOADER_MIDDLEWARES.update({
    'scrapy_selenium.SeleniumMiddleware': 800,
})

Using Selenium in a Spider

Modify product_spider.py:

from scrapy_selenium import SeleniumRequest
import scrapy

class JSProductSpider(scrapy.Spider):
    name = "js_products"
    
    def start_requests(self):
        yield SeleniumRequest(url="https://example.com/products", callback=self.parse)

    def parse(self, response):
        for item in response.css("div.product"):
            yield {
                "name": item.css("h2::text").get(),
                "price": item.css("span.price::text").get(),
                "url": response.urljoin(item.css("a::attr(href)").get()),
            }

Monitoring and Maintaining Scrapy Pipelines

A well-designed scraping pipeline needs continuous monitoring to:

  • Detect errors before they impact data collection
  • Optimize performance for faster scraping
  • Adapt to website structural changes

Monitoring Logs with Log Rotation

Modify settings.py to enable log rotation:

LOG_FILE = "scrapy.log"
LOG_LEVEL = "INFO"
LOG_ENABLED = True

Use tail to monitor logs in real time:

tail -f scrapy.log

Auto-Restarting Scrapy on Failure

To auto-restart Scrapy if it crashes, use a bash script:

Create restart_scrapy.sh:

#!/bin/bash
while true; do
    scrapy crawl products
    sleep 10
done

Run it:

nohup bash restart_scrapy.sh > scrapy_restart.log 2>&1 &

Scrapy Benchmarking & Performance Optimization

As your scraper grows in complexity, optimizing performance becomes essential. Slow scrapers consume more resources and can trigger bans.

Enabling Asynchronous Requests

Scrapy is designed to be asynchronous, meaning it can send multiple requests simultaneously. Increase concurrency to speed up the scraping process.

Modify settings.py:

CONCURRENT_REQUESTS = 32
CONCURRENT_REQUESTS_PER_DOMAIN = 16
DOWNLOAD_DELAY = 0.5  # Avoid bans

Enabling HTTP Compression

Many websites support gzip compression, which reduces response size and speeds up requests. Enable it in settings.py:

COMPRESSION_ENABLED = True

Using Caching for Faster Development

When debugging spiders, you don’t need to fetch pages repeatedly. Scrapy has a built-in cache system.

Enable caching in settings.py:

HTTPCACHE_ENABLED = True
HTTPCACHE_EXPIRATION_SECS = 3600  # 1 hour
HTTPCACHE_DIR = 'httpcache'

Using Scrapy Stats for Performance Monitoring

Scrapy collects statistics for request speed, response time, and item counts.

To enable it, modify settings.py:

STATS_DUMP = True

After running a scraper, check statistics:

scrapy crawl products --loglevel=INFO

Scaling Web Scraping with Distributed Crawlers

For very large projects, running multiple scrapers in parallel can increase efficiency.

Running Multiple Spiders Simultaneously

Instead of running Scrapy one spider at a time, use crawlall.py to run multiple spiders in parallel.

Create crawlall.py:

from scrapy.crawler import CrawlerProcess
from scalable_scraper.spiders.product_spider import ProductSpider
from scalable_scraper.spiders.js_product_spider import JSProductSpider

process = CrawlerProcess()
process.crawl(ProductSpider)
process.crawl(JSProductSpider)
process.start()

Run it:

python crawlall.py

Distributing Scrapers Across Multiple Servers

If your scraping workload is too large for a single server, distribute it.

  • Use AWS Auto Scaling to dynamically allocate resources
  • Split work across multiple servers using a message queue (e.g., RabbitMQ)
  • Run independent scrapers and merge results in a database

Best Practices for Scalable Scraping Pipelines

To maintain a stable and efficient web scraper, follow these best practices:

Respect Robots.txt

Before scraping a website, check its robots.txt file.

Example:

https://example.com/robots.txt

If Disallow: /products, then do not scrape the page.

Implement Rotating Proxies

To avoid getting blocked, rotate proxies after each request.

Install Scrapy-Rotating-Proxies:

pip install scrapy-rotating-proxies

Enable in settings.py:

DOWNLOADER_MIDDLEWARES.update({
    'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,
    'rotating_proxies.middlewares.BanDetectionMiddleware': 620,
})

Randomize Request Timing

Avoid making requests too quickly. Use DOWNLOAD_DELAY:

DOWNLOAD_DELAY = 1.5
RANDOMIZE_DOWNLOAD_DELAY = True

Monitor IP Bans & Captchas

If a website blocks your IP, use a proxy service like BrightData or ScraperAPI.

Check response status codes:

def parse(self, response):
    if response.status == 403:  # Forbidden
        self.logger.warning("Blocked! Changing proxy...")

Store Data Efficiently

For large-scale scraping, use databases instead of files:

Storage OptionUse Case
MongoDBJSON-like, scalable storage
PostgreSQLStructured relational data
AWS S3Cloud storage for CSV/JSON

Conclusion

Building a scalable web scraping pipeline with Python and Scrapy requires a structured approach, combining efficiency, reliability, and adaptability. By optimizing Scrapy settings, leveraging proxy rotation, and implementing middleware for anti-bot protection, scrapers can run efficiently without frequent interruptions. Using tools like Selenium for JavaScript-heavy websites, caching responses, and distributing workloads across multiple servers enhances performance and scalability. Proper scheduling with cron jobs ensures continuous data extraction, while logging and monitoring help detect and resolve issues in real-time. By following these best practices, businesses and developers can build robust web scrapers capable of handling large datasets while minimizing the risk of bans.

However, it is crucial to follow ethical and legal considerations when scraping websites. Always check and respect a website’s robots.txt file, avoid overloading servers with excessive requests, and ensure compliance with data privacy regulations. Implementing responsible scraping practices not only protects against legal repercussions but also ensures a sustainable and fair use of web data. As web technologies evolve, staying up to date with the latest scraping techniques and tools will help maintain efficient, scalable, and ethical data collection processes for various industries.

Deepak Chauhan About Deepak Chauhan I am a technology strategist at VOCSO with 20 years of experience in full-stack development. Specializing in Python, the MERN stack, Node.js, and Next.js, I architect scalable, high-performance applications and custom solutions. I excel at transforming ideas into innovative digital products that drive business success.


Further Reading...

We use cookies to give you the best online experience. By using our website you agree to use of cookies in accordance with VOCSO cookie policy. I Accept Cookies