How Scrapy Crawls and Scrapes Web Data in Python
Scrapy is an open-source, asynchronous Python framework designed for extracting structured data from websites at scale. It operates through an event-driven architecture powered by the Twisted engine, allowing it to send concurrent network requests without blocking execution. This article explains Scrapy's internal architecture, the lifecycle of a crawl request, how data is parsed using selectors, and how the extracted information is processed and stored into structured formats.
Core Architecture and Components
Scrapy relies on a modular architecture where distinct components communicate through a central coordinator called the Engine.
- Scrapy Engine: Controls the data flow between all components of the system and triggers events when specific actions occur.
- Spiders: User-defined Python classes that specify the initial URLs to visit, rules for following links, and logic for parsing page content.
- Scheduler: Receives requests from the Engine, queues them, and returns them one by one when the Engine is ready to process them.
- Downloader: Fetches web pages over the internet and delivers the resulting responses back to the Engine.
- Downloader Middlewares: Hooks situated between the Engine and Downloader to alter outgoing requests (such as setting proxies or user-agents) and incoming responses.
- Spider Middlewares: Hooks situated between the Engine and Spiders that process spider inputs (responses) and outputs (items and requests).
- Item Pipeline: Responsible for processing, validating, deduplicating, and persisting the scraped data into formats like JSON, CSV, or relational and NoSQL databases.
The Crawling Workflow
The crawling process follows a continuous loop managed by the Engine:
- Initialization: The Spider generates initial
Requestobjects from a list of start URLs and sends them to the Engine. - Scheduling: The Engine forwards these requests to the Scheduler, which organizes and prioritizes the request queue.
- Downloading: The Engine requests the next URL from the Scheduler and hands it to the Downloader via Downloader Middleware.
- Response Handling: Once the Downloader retrieves
the page, it constructs a
Responseobject and sends it back to the Engine, which routes it through Spider Middleware to the Spider's callback method (defaulting toparse). - Parsing and Extraction: The Spider processes the
response:
- It extracts structured data into Scrapy
Itemobjects. - It finds new URLs to follow, converting them into new
Requestobjects.
- It extracts structured data into Scrapy
- Data Processing: Extracted items are routed by the Engine to the Item Pipeline, while new requests are sent to the Scheduler to continue the crawling loop.
Extracting Structured Data
Scrapy uses built-in selectors to extract data from HTML and XML documents using either CSS expressions or XPath queries:
import scrapy
class ProductSpider(scrapy.Spider):
name = "products"
start_urls = ["https://example.com/products"]
def parse(self, response):
for product in response.css("div.product-card"):
yield {
"title": product.css("h2.title::text").get(),
"price": product.xpath(".//span[@class='price']/text()").get(),
"url": response.urljoin(product.css("a::attr(href)").get()),
}
next_page = response.css("a.next-page::attr(href)").get()
if next_page:
yield response.follow(next_page, callback=self.parse)To enforce schema consistency, data is typically modeled using Scrapy
Item classes or standard Python dataclasses, defining the
expected fields prior to pipeline processing.
Asynchronous Execution and Performance
Scrapy achieves high performance through cooperative multitasking
rather than multi-threading. By leveraging the Twisted networking
library, the Downloader sends non-blocking requests. While waiting for a
remote server to reply to one request, the Engine dispatches other
pending requests from the Scheduler. Settings such as
CONCURRENT_REQUESTS, DOWNLOAD_DELAY, and the
AutoThrottle extension allow developers to optimize throughput while
avoiding server bans or overloading target hosts.