Tutorial11 min read

What Is Scrapy?

Short answer

Scrapy is a free, open-source Python framework for crawling websites and extracting structured data: you write a "spider" that says which pages to fetch and what to pull from each, and Scrapy runs the requests concurrently, follows links, retries failures and exports the items to JSON, CSV or a database. Its documentation calls it "a fast high-level web crawling and web scraping framework", used "from data mining to monitoring and automated testing" (Scrapy docs). It suits large, recurring crawls of sites whose data is in the HTML, and it does not run JavaScript unless you add a browser. Scrapy sends its requests through ProxyField residential proxies with one setting, from $1.95/GB; start with the Easy Trial: 1 GB for $2.95.

residential IPs
412M+
from, traffic never expires
$1.95/GB
Easy Trial
1 GB for $2.95
On this page 11 sections
  1. Scrapy at a glance
  2. What is Scrapy used for?
  3. How Scrapy works: the parts of a project
  4. A complete spider, and what it produces
  5. Is Scrapy a crawler or a scraper?
  6. Is Scrapy good for web scraping?
  7. Limitations of Scrapy
  8. Scrapy in 2026: still used?
  9. Running Scrapy in production
  10. Proxies for Scrapy crawls
  11. What Is Scrapy? FAQ

Scrapy at a glance

FactValue
What it isA web crawling and scraping framework for Python
Current version2.19.0, released 2026-09-10 (release notes)
Python3.10 or newer (PyPI)
LicenseBSD-3-Clause
First PyPI release0.8, 2009-12-12 (PyPI release history)
EngineAsynchronous: requests are scheduled and processed without waiting for each other; the asyncio reactor is the default since 2.13
JavaScriptNot rendered; add a headless browser such as Playwright when a page needs it
Installpip install scrapy

The Scrapy documentation describes the core advantage this way: "requests are scheduled and processed asynchronously", so Scrapy "doesn't need to wait for a request to be finished and processed" before it sends the next one (Scrapy at a glance). That is why one Scrapy process can fetch many pages concurrently, as long as the target site allows that pace.

What is Scrapy used for?

Scrapy is used for jobs where you fetch many pages with the same structure and turn them into rows:

JobWhat a spider collects
Product and price monitoringprices, stock and specs from catalogs with thousands of product pages
Listingsjobs, real estate, classifieds, events: one row per listing, every day
News and content monitoringnew articles on a set of sites: headline, author, date, text
SEO and site crawlsevery URL of a site with its title, status code and links
Datasets for analysis or machine learningpublic pages turned into JSON Lines or CSV
Audits and tests of your own sitesbroken links, missing tags, pages that changed

Scrapy or something else?

Your jobUseWhy
One page or one table, onceRequests + Beautiful Soup10 lines, nothing to set up
Many pages, links to follow, recurring runsScrapyconcurrency, retries, deduplication and exports built in
Pages that show data only after JavaScript runsPlaywright, or Scrapy with scrapy-playwrighta real browser renders the page
Results without running crawlers yourselfa scraper API, such as ProxyField's Web Scraping API$0.50 per 1,000 plain HTTP results, $2.00 with JS rendering

The longer comparison is in Scrapy vs Beautiful Soup and Python libraries for web scraping.

How Scrapy works: the parts of a project

A Scrapy project is a set of small components that the engine connects. You write the spider and, when needed, pipelines and middlewares; the framework does the rest.

ComponentWhat it does
SpiderYour class: the start URLs, and a parse method that extracts items and yields new requests (next pages, detail pages)
Engine and schedulerKeep the queue of requests, drop duplicates, decide what to fetch next
DownloaderFetches pages concurrently, with retries, redirects, cookies and compression
Downloader middlewaresChange requests and responses on the way: headers, proxies, retries, caching
Spider middlewaresProcess what goes into and comes out of spiders
Items and item pipelinesDefine the fields you collect, then clean, validate, deduplicate and store them
Feed exportsWrite items to JSON, JSON Lines, CSV or XML with one command-line option
SelectorsCSS and XPath queries over the HTML, from the Parsel library
SettingsConcurrency, delays, AutoThrottle, retries, user agent, robots.txt handling

A complete spider, and what it produces

This spider collects quotes and follows the "Next" link on the practice site the Scrapy tutorial uses:

Python
# quotes.py
import scrapy


class QuotesSpider(scrapy.Spider):
    name = "quotes"
    start_urls = ["https://quotes.toscrape.com/"]

    def parse(self, response):
        for q in response.css("div.quote"):
            yield {"text": q.css("span.text::text").get(),
                   "author": q.css("small.author::text").get()}
        next_page = response.css("li.next a::attr(href)").get()
        if next_page:
            yield response.follow(next_page, self.parse)

Run it with no project:

Shell
pip install scrapy
scrapy runspider quotes.py -O quotes.json

Expect 10 pages and 100 items in quotes.json. The first two:

[
{"text": "\u201cThe world as we have created it is a process of our thinking. It cannot be changed without changing our thinking.\u201d", "author": "Albert Einstein"},
{"text": "\u201cIt is our choices, Harry, that show what we truly are, far more than our abilities.\u201d", "author": "J.K. Rowling"},

The \u201c sequences are curly quotes: JSON exports escape non-ASCII characters by default. Add -s FEED_EXPORT_ENCODING=utf-8 to write them as-is; projects made with startproject already set it.

The commands you use next:

CommandWhat it does
scrapy startproject bookscreates a project folder with settings.py, items.py, pipelines.py and a spiders/ directory
scrapy genspider books_spider books.toscrape.comadds a spider skeleton to the project
scrapy crawl quotes -O quotes.csvruns a project spider by its name and exports to CSV
scrapy shell "https://quotes.toscrape.com/"opens a console with response loaded, to try selectors
scrapy fetch --nolog URL > response.htmlsaves the HTML exactly as Scrapy receives it

If you write a custom start method, current Scrapy uses async def start(): start_requests() was deprecated in 2.13.0 (2025-05-08) and removed in 2.16.0 (2026-05-19), where it "is removed and no longer called" (release notes). Older tutorials that override start_requests() need that one change, inside the spider class:

Python
    async def start(self):
        for url in ["https://quotes.toscrape.com/page/1/", "https://quotes.toscrape.com/page/2/"]:
            yield scrapy.Request(url, callback=self.parse)

The step-by-step version, from scrapy startproject to pipelines and exports, is the Scrapy tutorial.

Is Scrapy a crawler or a scraper?

Both. A crawler finds pages by following links; a scraper extracts fields from them. Scrapy's spiders do the two in one pass: parse yields items and new requests from the same response. Its CrawlSpider class goes further and follows links that match rules you set, which is how you crawl a whole site; see how to crawl a website. The difference between the two jobs is explained in web crawling vs web scraping.

Is Scrapy good for web scraping?

Yes, for the jobs it was built for: many pages, recurring runs, data in the HTML or in JSON endpoints. What makes it good:

  • Throughput. Asynchronous downloads with per-domain concurrency limits.
  • Robustness. Built-in retries, redirect handling, duplicate filtering and a pause-and-resume mode for long crawls.
  • Politeness controls. Download delays, AutoThrottle (which adapts the pace to the server's response times) and robots.txt handling are settings, not code.
  • Clean data flow. Items go through pipelines, so cleaning and storage stay out of the spider.
  • Exports with no code. -O items.json or -O items.csv on the command line.

Limitations of Scrapy

Scrapy's three biggest limits are JavaScript (it downloads only the HTML the server sends), scale (one process uses one CPU core, and nothing built in spreads one crawl over several machines) and upkeep (selectors break when a site changes its layout). Each has a documented workaround, at a cost.

JavaScript-rendered pages

Scrapy does not run JavaScript, so a page that builds its content in the browser arrives as an empty shell. The documentation's order of work (Selecting dynamically-loaded content):

  1. See what Scrapy sees

    Scrapy fetch --nolog https://example.com > response.html. If the data is there, it is a selector problem, not JavaScript.

  2. Check it isn't just your request

    Fetch the page with curl. If curl gets the data, match its headers or USER_AGENT in your Scrapy request.

  3. Find the data request

    In the browser's network tool, find the XHR or fetch call that returns the data (often JSON). Copy it as cURL and rebuild it with scrapy.Request.from_curl("curl …"). The docs call this "the preferred approach": structured data, little parsing, little transfer.

  4. Data inside a <script> tag

    Parse the JavaScript object with chompjs, or convert it with js2xml.

  5. Only then, a headless browser

    The scrapy-playwright plugin renders selected requests in Playwright. Expect each rendered page to be much slower and heavier than a plain request, and use the plugin rather than calling playwright-python in the spider, which "circumvents most of the Scrapy components (middlewares, dupefilter, etc)".

Code for each route: Scrapy and JavaScript pages.

Very large crawls

Scrapy's defaults are tuned for crawling one site politely, and its documentation lists what breaks first at scale (Optimization, Common practices):

LimitWhat happensWhat the docs suggest
One CPU coreScrapy runs in a single process, with everything except DNS on one threadsplit the crawl across separate processes
Scheduler memorythe crawl finds requests faster than it downloads them, and the queue grows until memory runs outJOBDIR to keep scheduled requests on disk; higher priority for pages that yield no new requests
Big responsesDOWNLOAD_MAXSIZE lets one response take up to 1 GiB, multiplied by your concurrencylower it; set DOWNLOAD_WARNSIZE to find out what the site serves
Polite defaultsa startproject project sends 1 request per second per domain (DOWNLOAD_DELAY = 1, CONCURRENT_REQUESTS_PER_DOMAIN = 1)raise them gradually and watch for 429 and 503 counts in the stats
Many domainsDNS runs on a thread pool of REACTOR_THREADPOOL_MAXSIZE threads and slows broad crawlsset CONCURRENT_REQUESTS near 8 × the number of domains (80 for 10); run a local caching DNS server
No distribution"Scrapy doesn't provide any built-in facility for running crawls in a distributed (multi-server) manner"partition the URL list and run one spider per part, for example on several Scrapyd servers
robots.txt pacingScrapy does not act on Crawl-delay and Request-ratetranslate them into DOWNLOAD_DELAY and concurrency yourself

For queues shared between machines (scrapy-redis), monitoring and storage at that size, see large-scale web scraping.

Other limits

  • Upkeep. A spider is tied to a site's HTML; when the layout changes, the selectors return nothing and the item count drops. Watch it per run.
  • A learning curve. Projects, settings, callbacks and the asynchronous model take longer to learn than a 10-line Requests script.
  • Overkill for small jobs. One page or one table is quicker with Requests and Beautiful Soup.

Scrapy in 2026: still used?

Yes. Scrapy ships a release every one to two months: 2.16.0 on 2026-05-19, 2.17.0 on 2026-07-07, 2.18.0 on 2026-08-20 and 2.19.0 on 2026-09-10. 2.19.0 added a RemoteControl extension to inspect and control a running crawl. It remains the standard open-source framework for large crawls in Python. Newer tools, such as browser-based crawlers and AI extractors, sit next to it rather than replacing it: many teams use Scrapy for the crawl and a browser only for the pages that need one.

Running Scrapy in production

  • Scheduling. Run spiders from cron or a job scheduler, or deploy them to Scrapyd, an open-source service that runs spiders over an HTTP API. More in how to schedule web scrapers.
  • Pacing. Set DOWNLOAD_DELAY and enable AutoThrottle so the crawl slows down when the site does.
  • Pause and resume. Run with -s JOBDIR=crawls/run1, stop with one Ctrl-C, and start the same command again to continue.
  • Monitoring. Watch the item count and the share of failed responses per run; a sudden drop usually means a layout change.
  • Proxies. Put the proxy in each request's proxy meta key or in the http_proxy and https_proxy environment variables, which Scrapy's HttpProxyMiddleware reads.
Python
PROXY = "http://<login>-country-us-city-new_york:<password>@gate.proxyfield.io:7000"
yield scrapy.Request(url, meta={"proxy": PROXY}, callback=self.parse)

With a rotating gateway, one proxy URL is enough: each request leaves from a new IP, and <login>-country-us-city-new_york asks for exit IPs in New York. The full setup, including sticky sessions and a middleware, is in the Scrapy proxy setup guide.

Proxies for Scrapy crawls

Concurrency does not help when a site allows only a few requests per IP, so a Scrapy crawl of one site is the kind of job that hits per-IP limits first. ProxyField gives each request a new IP or keeps a sticky session from 30 s to 36 h, over HTTP, HTTPS or SOCKS5, with unlimited concurrent threads and country, region and city targeting.

Scrapy fetches HTML without images or scripts, and its end-of-run stats report downloader/response_bytes, so you can measure your own page weight: at 100 KB a page, 1 GB is about 10,000 pages. Residential proxies cost $2.95/GB for 1 GB and fall to $1.95/GB from 1,000 GB, with no subscription; traffic never expires, and all purchases are final. Scrape public data and follow the site's rules (is web scraping legal), then start with residential proxies and the Easy Trial: 1 GB for $2.95.

What Is Scrapy? FAQ

What is Scrapy, and what kinds of scraping and crawling jobs is it used for?

ProxyField's guide puts it in one line: Scrapy 2.19.0 is a free, open-source Python framework that crawls websites and turns their pages into structured data. It is used for product and price monitoring, job and real estate listings, news monitoring, SEO crawls of whole sites, datasets for analysis and audits of your own sites. It fits many similar pages and recurring runs; for one page, Requests with Beautiful Soup is quicker.

What is Scrapy?

Scrapy is a free, open-source Python framework for crawling websites and extracting structured data; its docs call it "a fast high-level web crawling and web scraping framework". You write a spider that says what to fetch and what to extract, and Scrapy runs the requests concurrently, follows links, retries failures and exports the results.

What are the main limitations of Scrapy, for example with JavaScript-rendered pages or very large crawls?

Scrapy does not run JavaScript, so find the page's data request and replay it with Request.from_curl(), or render only the pages that need it with scrapy-playwright. For very large crawls, one process uses one CPU core, the request queue can outgrow memory (JOBDIR keeps it on disk), and nothing built in distributes one crawl over several machines. A spider also needs upkeep whenever a site changes its layout.

Is Scrapy good for web scraping?

Yes, for large and recurring jobs on pages whose data is in the HTML or in JSON requests. It brings concurrency, retries, duplicate filtering, pacing controls and exports out of the box; for a single page, Requests with Beautiful Soup is quicker to write.

What are spiders in Scrapy?

A spider is the Python class you write: it lists the start URLs and has a parse method that extracts items from each response and yields new requests to follow. Scrapy runs one or many spiders and calls their methods as pages arrive.

What are Scrapy item pipelines?

Pipelines are classes that receive every item a spider yields, in order, to clean, validate, deduplicate or store it. They keep that work out of the spider, so the spider only extracts.

What are Scrapy middlewares?

Hooks between the engine and the downloader or the spiders. Downloader middlewares change requests and responses, for example to set headers, proxies or retries; spider middlewares process what goes into and out of the spiders.

What is Scrapy shell, and how can I use it?

An interactive console that fetches one URL and lets you try CSS and XPath selectors on the response before you put them in a spider. Start it with scrapy shell "https://example.com" and test queries such as response.css("h1::text").get().

Is Scrapy still used?

Yes. It shipped four releases between May and September 2026, the latest 2.19.0 on 2026-09-10, and remains the standard open-source framework for large crawls in Python. Teams often pair it with a headless browser for the few pages that need JavaScript.

Is Scrapy a web crawler?

It is a crawler and a scraper in one: its spiders follow links and extract fields in the same pass. The CrawlSpider class follows links that match rules you set, which is how you crawl a whole site.

Show 4 more questionsShow fewer questions

Can Scrapy run one crawl across several machines?

Not by itself: the docs say Scrapy has no built-in facility for distributed (multi-server) crawls. The documented way is to split the URL list into parts and run one spider per part, for example on several Scrapyd servers; shared queues need an add-on such as scrapy-redis.

How do I pause and resume a Scrapy crawl?

Run the spider with -s JOBDIR=crawls/run1, stop it with one Ctrl-C, and run the same command again: Scrapy reloads the queue and the seen-request list from that folder. Use a separate folder for each run.

What is scrapy-playwright?

A plugin that makes Scrapy fetch selected requests through Playwright, so those pages are rendered in a real browser before your spider parses them. The rest of the crawl keeps using Scrapy's fast HTTP downloader.

What is Scrapyd?

An open-source service that runs Scrapy spiders on a server and lets you schedule, start and stop them through an HTTP API. It is a common way to run spiders in production without a separate job platform.

Easy Trial: 1 GB for $2.95.

Try for the price of 1 GB, no subscription, traffic never expires.