Scrapy vs BeautifulSoup vs Selenium vs Playwright
Beautiful Soup is a parser, Scrapy is a crawling framework, and Selenium and Playwright are browsers you drive from code, so they solve different parts of a scraping job. Use Requests with Beautiful Soup for a few pages, Scrapy for thousands of pages with concurrency, retries and exports built in, and Playwright or Selenium only when JavaScript builds the content. Any of them sends its requests through ProxyField residential proxies 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 8 sections
The four tools at a glance
All four are open source. Current Scrapy, Selenium and Playwright releases need Python 3.10 or later, and Beautiful Soup 4.15.0 needs Python 3.7 or later. Versions are from PyPI.
| Feature | Beautiful Soup | Scrapy | Selenium | Playwright |
|---|---|---|---|---|
| What it is | HTML and XML parser | Crawling and scraping framework | Browser automation (W3C WebDriver) | Browser automation (own protocol) |
| Version, license | 4.15.0, MIT | 2.19.0, BSD-3-Clause | 4.51.0, Apache-2.0 | 1.64.1, Apache-2.0 |
| Downloads pages | No; pair it with Requests or httpx | Yes, asynchronously | Yes, through a real browser | Yes, through a real browser |
| Runs JavaScript | No | No (add scrapy-playwright) | Yes | Yes |
| Concurrency | Your own code | Built in (CONCURRENT_REQUESTS) | One browser per session; Selenium Grid to scale | Many contexts per browser |
| Retries, throttling, exports | Your own code | Built in (retry middleware, AutoThrottle, feed exports to JSON, CSV, XML) | Your own code | Your own code |
| Learning curve (our estimate) | An afternoon | A few days | A day or two | A day or two |
| Best for | Small jobs, one-off scripts, parsing HTML you already have | Large crawls of static pages | Existing Selenium teams, real Safari | JavaScript-heavy pages, new projects |
Beautiful Soup describes itself as "a Python library for pulling data out of HTML and XML files" (docs); Scrapy as "a high-level Web Crawling and Web Scraping framework" (PyPI). That one line explains most of the comparison: a parser reads pages, a framework runs the whole crawl.
Scrapy vs BeautifulSoup: the same job in both
The task: collect the title and price of all 1,000 books on Books to Scrape, a practice site with 50 pages of 20 books.
With Requests and Beautiful Soup, you write the loop, the pagination and the pause yourself:
# pip install requests beautifulsoup4
import time
import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin
url = "https://books.toscrape.com/catalogue/page-1.html"
books = []
while url:
soup = BeautifulSoup(requests.get(url, timeout=30).content, "html.parser")
for card in soup.select("article.product_pod"):
books.append({"title": card.h3.a["title"], "price": card.select_one(".price_color").text})
nxt = soup.select_one("li.next a")
url = urljoin(url, nxt["href"]) if nxt else None
time.sleep(0.5) # one page at a time, with a pause
print(len(books), books[0])With Scrapy, you declare what to extract and which links to follow; the framework schedules the requests, runs several at once, retries failures and writes the file:
# pip install scrapy, then: scrapy runspider books_spider.py
import scrapy
class BooksSpider(scrapy.Spider):
name = "books"
start_urls = ["https://books.toscrape.com/catalogue/page-1.html"]
custom_settings = {"CONCURRENT_REQUESTS": 8, "AUTOTHROTTLE_ENABLED": True,
"FEEDS": {"books.json": {"format": "json"}}}
def parse(self, response):
for card in response.css("article.product_pod"):
yield {"title": card.css("h3 a::attr(title)").get(),
"price": card.css(".price_color::text").get()}
yield from response.follow_all(css="li.next a", callback=self.parse)We ran both on October 23, 2026 (Requests 2.34.2, Beautiful Soup 4.15.0, Scrapy 2.19.0). Both saved all 1,000 books. The sequential script with a 0.5 s pause took about 61 s; the Scrapy spider, with 8 concurrent requests and AutoThrottle adjusting the pace, took about 19 s. That is one run on one small site, not a benchmark: what you should take from it is that Scrapy's gain comes from running requests in parallel, which you would otherwise have to build yourself.
One detail the test caught: pass response.content (bytes) to Beautiful Soup, not response.text. Requests guessed the wrong encoding for this site and turned "£" into "£"; with bytes, Beautiful Soup reads the charset from the page.
When Beautiful Soup is enough
- You scrape tens or hundreds of pages, once or on a schedule.
- You already have the HTML, from a file, an email, an API field or a browser.
- The script lives inside a larger program, a notebook or a data pipeline, and you do not want a framework's project layout.
Beautiful Soup is forgiving with broken HTML and easy to read. For speed on large documents, use it with the lxml parser, or move to lxml or parsel directly; more in Beautiful Soup web scraping.
When Scrapy pays off
- Thousands to millions of pages, where concurrency, retries and deduplication of URLs matter.
- Crawls that follow links across a site, with rules for what to follow (
CrawlSpider). - Recurring jobs that need clean exports, item pipelines, logging and stats.
- Scrapy also parses: its CSS and XPath selectors (parsel) replace Beautiful Soup inside a spider, though you can still call Beautiful Soup from a callback if you prefer its API.
Scrapy has a learning curve: spiders, callbacks, settings and middlewares. What Scrapy is explains the parts, and the Scrapy tutorial builds a project step by step. For runs across several servers, see large-scale web scraping.
When you need Selenium or Playwright
Neither Beautiful Soup nor Scrapy runs JavaScript. If the data you want is missing from the page source, you have two routes:
-
Find the data source
Scrapy's docs advise this first: use the browser's network tool to find the request that carries the data and reproduce it (Scrapy: dynamic content). Often it is a JSON API, which is faster to scrape than any page; see how to scrape hidden APIs and embedded JSON.
-
Render the page
Use Playwright or Selenium to run the page in a browser. Inside Scrapy, the scrapy-playwright plugin (0.0.48 on PyPI) renders selected requests with Playwright while Scrapy keeps scheduling, retries and exports.
Between the two browsers, Playwright is the easier start for new Python scrapers, with auto-waiting and request blocking built in; Selenium suits teams that already run it. The full comparison is in Playwright vs Selenium vs Puppeteer, and the same parser-or-browser question for Node.js in Cheerio vs Puppeteer.
Which scraping tool should you choose?
| Your job | Pick |
|---|---|
| Under a few hundred static pages | Requests + Beautiful Soup |
| Thousands of static pages, recurring crawls | Scrapy |
| Data in a JSON API behind the page | Requests or httpx, no parser needed |
| JavaScript-built pages, a few hundred | Playwright |
| JavaScript-built pages at scale | Scrapy + scrapy-playwright, or Playwright with a job queue |
| Existing Selenium code or real Safari | Selenium |
More Python options, including httpx, lxml and selectolax, are in our guide to Python scraping libraries.
Proxies for all four
Beautiful Soup has no network code, so the proxy goes into Requests (proxies={"https": "http://<login>:<password>@gate.proxyfield.io:7000"}). Scrapy takes it per request in meta["proxy"] or through its HttpProxyMiddleware (Scrapy proxy setup). Playwright and Selenium take it at browser launch (Playwright, Selenium). With the username <login>-country-us-city-new_york, the ProxyField gateway picks an exit IP in New York. Region, session and session time go in the same username: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default), as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600; every part after the login is optional.
ProxyField gives every request a new IP, or a sticky session from 30 s to 36 h, over HTTP, HTTPS or SOCKS5, with targeting by country, region (state) and city and unlimited concurrent threads, which suits Scrapy's parallel requests. 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. Start with residential proxies and the Easy Trial: 1 GB for $2.95.
Scrapy vs BeautifulSoup vs Selenium vs Playwright FAQ
Is Scrapy better than BeautifulSoup?
ProxyField residential proxies from $1.95/GB work with both, and Scrapy is better than Beautiful Soup for crawls of thousands of pages, while Beautiful Soup suits small jobs. In one run on a practice site with 1,000 books, Scrapy with 8 concurrent requests finished in about 19 s against about 61 s for a sequential Requests and Beautiful Soup script, because Scrapy runs requests in parallel and adds retries and exports.
Can I use Scrapy with BeautifulSoup?
Yes. Inside a spider callback, pass response.text to BeautifulSoup() and parse as usual (Scrapy decodes response.text from the page's declared encoding itself), while Scrapy keeps scheduling, retries and exports. Most spiders skip this, because Scrapy's own CSS and XPath selectors do the same job without the extra parsing step.
Is Scrapy or Selenium better for scraping?
Scrapy, for static pages: it fetches many pages at once with little CPU and memory. Selenium is for pages that need a real browser to run JavaScript, log in or scroll. Many teams use Scrapy for the crawl and a browser only for the pages that need one.
Should I use Scrapy or Playwright?
Use Scrapy when the data is in the HTML or a JSON API and you need many pages; use Playwright when JavaScript builds the page or you must click and scroll. For JavaScript-heavy sites at scale, combine them with scrapy-playwright.
What is the difference between Selenium, Playwright and BeautifulSoup?
Beautiful Soup parses HTML you already downloaded and runs no JavaScript. Selenium and Playwright drive a real browser that loads the page, runs its scripts and lets you click, type and scroll; Playwright adds auto-waiting and a test runner, Selenium adds real Safari and Selenium Grid.
Which scraping framework should I choose?
In Python, Scrapy is the main full framework: scheduling, concurrency, retries, pipelines and exports in one package. Pick it when you crawl many pages regularly; for small scripts, Requests with Beautiful Soup is simpler, and for JavaScript pages add Playwright.
When should I choose Requests with BeautifulSoup over Scrapy?
When the job is small, runs inside another program or a notebook, or parses HTML you already have. A 15-line script is easier to read and change than a Scrapy project, and for a few hundred pages the speed difference rarely matters.
Is Scrapy faster than Selenium?
Yes, for pages that do not need JavaScript: Scrapy downloads only the HTML and fetches many pages concurrently, while Selenium loads every page in a full browser with its scripts, images and fonts. Selenium's speed comes from parallel browsers, which cost far more CPU and memory.
How much does it cost to operate a Selenium farm at scale vs a Scrapy crawler?
A browser farm costs much more to run, because each browser needs CPU and memory and downloads whole pages, which also raises per-GB proxy traffic. A Scrapy crawler fetches HTML only and runs hundreds of requests from one process. Measure your own pages, since page weight decides most of the difference.
Scrapy vs Puppeteer: which is best?
They do different jobs: Scrapy is a Python crawling framework for many static pages, Puppeteer a Node.js library that drives Chrome or Firefox for JavaScript-built pages. Pick Scrapy for volume and Puppeteer, or Playwright from Python, for rendering.
Related pages
Easy Trial: 1 GB for $2.95.
Try for the price of 1 GB, no subscription, traffic never expires.