Skip to main content
Prerequisites
  1. You’ve read the Getting started page and know how to create and run a basic spider.
This page covers the spider system’s advanced features: concurrency control, pause/resume, streaming, lifecycle hooks, statistics, and logging.

Concurrency Control

The spider system uses three class attributes to control how aggressively it crawls:
When concurrent_requests_per_domain is set, each domain gets its own concurrency limiter in addition to the global limit. This is useful when crawling multiple domains simultaneously — you can allow high global concurrency while being polite to each individual domain.

Rate Limiting Implementation

The rate limiting logic is implemented in the CrawlerEngine:
engine.py:71-77
And used during request processing:
engine.py:88-92
The download_delay parameter adds a fixed wait before every request, regardless of the domain. Use it for simple rate limiting.

Using uvloop

The start() method accepts a use_uvloop parameter to use the faster uvloop/winloop event loop implementation, if available:
This can improve throughput for I/O-heavy crawls. You’ll need to install uvloop (Linux/macOS) or winloop (Windows) separately.

Pause & Resume

The spider supports graceful pause-and-resume via checkpointing. To enable it, pass a crawldir directory to the spider constructor:

How It Works

  1. Pausing: Press Ctrl+C during a crawl. The spider waits for all in-flight requests to finish, saves a checkpoint (pending requests + a set of seen request fingerprints), and then exits.
  2. Force stopping: Press Ctrl+C a second time to stop immediately without waiting for active tasks.
  3. Resuming: Run the spider again with the same crawldir. It detects the checkpoint, restores the queue and seen set, and continues from where it left off — skipping start_requests().
  4. Cleanup: When a crawl completes normally (not paused), the checkpoint files are deleted automatically.
Checkpoints are also saved periodically during the crawl (every 5 minutes by default). You can change the interval as follows:
The writing to disk is atomic, so it’s totally safe.

Checkpoint Implementation

The pause handling logic is implemented in the engine:
engine.py:165-182
Checkpoint saving:
engine.py:184-189
Pressing Ctrl+C during a crawl always causes the spider to close gracefully, even if the checkpoint system is not enabled. Doing it again without waiting forces the spider to close immediately.

Knowing If You’re Resuming

The on_start() hook receives a resuming flag:

Streaming

For long-running spiders or applications that need real-time access to scraped items, use the stream() method instead of start():
Key differences from start():
  • stream() must be called from an async context
  • Items are yielded one by one as they’re scraped, not collected into a list
  • You can access spider.stats during iteration for real-time statistics

Streaming Implementation

The streaming logic uses memory channels:
engine.py:313-334
You can use it with the checkpoint system too, making it easy to build UIs on top of spiders with real-time data that can be paused/resumed:
You can also use spider.pause() to shut down the spider programmatically. If you use it without enabling the checkpoint system, it will just close the crawl.

Lifecycle Hooks

The spider provides several hooks you can override to add custom behavior at different stages of the crawl:

on_start

Called before crawling begins. Use it for setup tasks like loading data or initializing resources:
spider.py:164-172

on_close

Called after crawling finishes (whether completed or paused). Use it for cleanup:
spider.py:174-176

on_error

Called when a request fails with an exception. Use it for error tracking or custom recovery logic:
spider.py:178-184

on_scraped_item

Called for every scraped item before it’s added to the results. Return the item (modified or not) to keep it, or return None to drop it:
spider.py:186-188
Example usage:
This hook can also be used to direct items through your own pipelines and drop them from the spider.

start_requests

Override start_requests() for custom initial request generation instead of using start_urls:
spider.py:141-156
Example with custom login:

Results & Statistics

The CrawlResult returned by start() contains both the scraped items and detailed statistics:

CrawlStats Details

The CrawlStats dataclass tracks comprehensive information:
result.py:41-62

Detailed Stats

Logging

The spider has a built-in logger accessible via self.logger. It’s pre-configured with the spider’s name and supports several customization options:

Logger Initialization

The logger is initialized in the Spider’s __init__ method:
spider.py:101-122
The log file directory is created automatically if it doesn’t exist. Both console and file output use the same format.