Configuring Multi-Session Support for Different Session Types in a Single Scrapling Spider
Override Spider.configure_sessions to register multiple session implementations—such as FetcherSession, AsyncDynamicSession, and AsyncStealthySession—then route each Request to the appropriate session via the sid parameter.
Configuring multi-session support for different session types in a single Scrapling spider allows you to combine lightweight HTTP clients, full-browser rendering, and stealth automation within one crawl. The D4Vinci/Scrapling repository provides a SessionManager that handles session registration, lazy initialization, and lifecycle management, while the Spider class exposes a configure_sessions hook for declaring which fetchers to use.
Understanding Scrapling's Session Architecture
The architecture centers on the SessionManager class in scrapling/spiders/session.py. This manager maintains a dictionary mapping session IDs to session instances, tracks which session serves as the default, and supports lazy-start semantics for resource-heavy browsers.
When a spider starts, the CrawlerEngine (defined in scrapling/spiders/engine.py) instantiates the spider, which in turn creates a SessionManager and calls self.configure_sessions(manager). The default implementation in scrapling/spiders/spider.py registers a single FetcherSession under the ID "default".
Registering Multiple Session Types in configure_sessions
To enable multi-session support, override configure_sessions in your spider subclass. Use manager.add(session_id, session_instance, *, default=False, lazy=False) to register each fetcher:
default=Truedesignates the fallback session for requests that omit an explicitsid.lazy=Truedefers the session's__aenter__(startup) until the first request actually references that session ID, conserving resources for infrequently used browsers.
from scrapling.spiders import Spider
from scrapling.fetchers import FetcherSession, AsyncDynamicSession, AsyncStealthySession
class MultiSessionSpider(Spider):
name = "multi_session"
def configure_sessions(self, manager):
# Lightweight HTTP session for API calls
manager.add("api", FetcherSession(), default=True)
# Chrome-based rendering for JavaScript-heavy pages
manager.add(
"chrome",
AsyncDynamicSession(
headless=False,
disable_resources=True,
timeout=60_000,
)
)
# Stealth session for anti-bot protected sites
manager.add(
"stealth",
AsyncStealthySession(
headless=True,
solve_cloudflare=True,
extra_flags=["--disable-blink-features=AutomationControlled"],
timeout=90_000,
),
lazy=True, # Only starts when first "stealth" request is made
)
Routing Requests to Specific Sessions
The Request object in scrapling/spiders/request.py carries an optional sid attribute. When the CrawlerEngine processes a request, it passes the request to SessionManager.fetch(request), which resolves the session ID via manager.get(request.sid) or falls back to the default session if sid is None.
Explicitly set sid in start_requests or when yielding new requests:
async def start_requests(self):
# Uses default "api" session
yield self.request("https://api.example.com/data")
# Explicitly routes to Chrome session
yield self.request(
"https://spa.example.com/dashboard",
sid="chrome",
)
# Triggers lazy initialization of stealth session
yield self.request(
"https://protected.example.com",
sid="stealth",
)
Inside parse callbacks, inspect response.session_id to determine which session fetched the page:
async def parse(self, response):
if response.session_id == "stealth":
self.logger.info("Successfully bypassed protection")
yield {"url": response.url, "session": response.session_id}
Session Lifecycle and Resource Management
The SessionManager handles asynchronous context management automatically. When a spider enters its async context (started by the engine), SessionManager.start() executes the __aenter__ method of every non-lazy session. Lazy sessions remain dormant until SessionManager.fetch first references them, at which point a lock ensures only one coroutine triggers the startup.
When the spider finishes or pauses, SessionManager.close() invokes each session's __aexit__ to shut down browsers, close HTTP connections, and release file descriptors. This lifecycle is verified in tests/spiders/test_session.py, which covers edge cases such as idempotent start() calls and concurrent lazy initialization.
Complete Working Example
The following spider demonstrates production-ready multi-session configuration, combining all three session types with explicit routing and lazy initialization:
# multi_session_spider.py
from scrapling.spiders import Spider
from scrapling.fetchers import (
FetcherSession,
AsyncDynamicSession,
AsyncStealthySession,
)
class ProductionSpider(Spider):
name = "production_spider"
start_urls = ["https://example.com"]
def configure_sessions(self, manager):
# Default: lightweight HTTP client
manager.add("http", FetcherSession(), default=True)
# Dynamic rendering for SPAs
manager.add(
"dynamic",
AsyncDynamicSession(
headless=True,
disable_resources=True,
timeout=45_000,
)
)
# Stealth mode for protected sites (lazy start)
manager.add(
"stealth",
AsyncStealthySession(
headless=True,
solve_cloudflare=True,
timeout=120_000,
),
lazy=True,
)
async def start_requests(self):
# Static/API content via default session
for url in self.start_urls:
yield self.request(url)
# JavaScript-heavy page
yield self.request(
"https://spa.example.com/products",
sid="dynamic",
)
# Anti-bot protected page
yield self.request(
"https://protected.example.com/data",
sid="stealth",
)
async def parse(self, response):
self.logger.info(
f"Parsed {response.url} using session '{response.session_id}'"
)
yield {
"url": response.url,
"session_used": response.session_id,
"title": response.css("title::text").get(),
}
Key Source Files
| File | Purpose |
|---|---|
scrapling/spiders/session.py |
SessionManager implementation – registration, lazy start, fetch routing, and lifecycle management |
scrapling/spiders/spider.py |
Abstract Spider class with configure_sessions hook and request generation |
scrapling/spiders/request.py |
Request dataclass carrying URL, sid, and metadata |
scrapling/spiders/engine.py |
CrawlerEngine orchestrating requests and session resolution |
scrapling/fetchers/requests.py |
FetcherSession – lightweight HTTP client based on curl_cffi |
scrapling/fetchers/chrome.py |
AsyncDynamicSession – Playwright Chrome rendering |
scrapling/fetchers/stealth_chrome.py |
AsyncStealthySession – stealth Chromium with anti-detection |
tests/spiders/test_session.py |
Unit tests documenting SessionManager behavior and edge cases |
Summary
- Register sessions in
configure_sessionsusingmanager.add(id, session, default=..., lazy=...)to mix HTTP clients, dynamic browsers, and stealth implementations. - Route requests by setting
sid="<session_id>"inRequestobjects; omitsidto use the default session. - Conserve resources by marking heavyweight sessions as
lazy=Trueso they only start when first referenced. - Inspect responses via
response.session_idto apply conditional parsing logic based on the fetcher used. - Lifecycle management is automatic—
SessionManagerhandles async startup and shutdown for all registered sessions.
Frequently Asked Questions
How do I set a default session for requests that don't specify a session ID?
Pass default=True when calling manager.add() in your configure_sessions method. Only one session can be the default; the last one registered with default=True wins. Requests that omit the sid parameter automatically route to this default session.
Can I use lazy loading for browser-based sessions to save memory?
Yes. When registering expensive sessions like AsyncDynamicSession or AsyncStealthySession, pass lazy=True to manager.add(). The session's __aenter__ (browser startup) is deferred until the first request explicitly referencing that session ID enters the fetch queue, preventing unnecessary resource consumption during crawls that might not need that session type.
What happens if I reference a session ID that hasn't been registered?
The SessionManager.get(sid) method raises a KeyError if the requested session ID does not exist in the registry. To avoid this, ensure all session IDs used in start_requests or parse callbacks are registered in configure_sessions, or implement error handling to fall back to the default session when appropriate.
How can I inspect which session fetched a response inside my parse callback?
The Response object returned to your parse method includes a session_id attribute populated by SessionManager.fetch() in scrapling/spiders/session.py. Check response.session_id to conditionally execute parsing logic—for example, applying additional anti-bot checks when the "stealth" session was used, or extracting JSON directly when the "api" session was used.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →