Skip to content

Compare

Scrapy vs BeautifulSoup: framework or parser?

Scrapy and BeautifulSoup are not competitors. One is a crawling framework; the other is an HTML parser. Learn which job each does and how they fit together.

Last updated 3 min readBy CrawlerBros Editorial

Scrapy is a crawling framework; BeautifulSoup is an HTML parser. Comparing them as alternatives is a category error. Scrapy already includes a parser (Parsel) and can use BeautifulSoup if you need its forgiveness or API. The real question is whether you need a framework at all.

The layers

Layer BeautifulSoup Scrapy
HTTP fetching No (use requests/httpx) Yes
Frontier/queue No Yes
Deduplication No Yes
Retries/backoff No Yes
Throttling No Yes (AutoThrottle)
HTML parsing Yes Yes (Parsel; BeautifulSoup optional)
CSS selectors Yes Yes
XPath No Yes
Item pipelines No Yes
Feed exports No Yes (JSONL, CSV, XML)
Browser rendering No Via scrapy-playwright

BeautifulSoup does one thing well: turn HTML into a tree you can query. Scrapy does the whole crawl: schedule, fetch, parse, retry, throttle, and export.

When BeautifulSoup + requests is enough

  • You have a list of URLs and no link discovery.
  • The crawl is small (tens to a few hundred pages).
  • You want a single script you can read in one sitting.
  • You need BeautifulSoup’s forgiving parser for messy markup.
  • You do not need a persistent queue or a project layout.

The requests + BeautifulSoup guide builds a small frontier, robots check, delay, and JSONL output. For a few hundred pages, that is often the right amount of engineering.

When Scrapy is the better tool

  • You need a queue that survives a crash.
  • You are crawling thousands of pages across many domains.
  • You want retries, throttling, and duplicate filtering without writing them.
  • You need item pipelines for validation and storage.
  • You want a project structure a team can share.

The Scrapy guide covers the spider, settings, AutoThrottle, feeds, and the retry policy.

Can you use both?

Yes. Scrapy can parse with BeautifulSoup:

from bs4 import BeautifulSoup
import scrapy


class ExampleSpider(scrapy.Spider):
    name = "example"
    start_urls = ["https://example.com/"]

    def parse(self, response):
        soup = BeautifulSoup(response.text, "html.parser")
        for card in soup.select("article.card"):
            yield {
                "title": card.select_one("h2").get_text(strip=True),
                "url": card.select_one("a")["href"],
            }

This is useful when a page is malformed enough that Parsel’s tree differs from what you see, or when a team already knows BeautifulSoup. The cost is an extra parse and a second tree in memory. For a high-volume crawl, measure it; for a small spider, the clarity may be worth it.

What about Scrapy’s own selectors?

Scrapy’s response.css() and response.xpath() are Parsel, built on lxml. They are fast and support both CSS and XPath. If you do not need BeautifulSoup’s specific forgiveness, use them; they avoid the double parse.

The practical answer

  • One page, a few fields, a small list: requests + BeautifulSoup.
  • A crawl with a queue, retries, and storage: Scrapy (with Parsel selectors).
  • A crawl where the HTML is unusually broken: Scrapy + BeautifulSoup, or requests + BeautifulSoup with a hand-written frontier.
  • A JavaScript-rendered page: neither on its own; find the JSON endpoint or use a browser.

Do not choose a framework for a one-page task, and do not hand-roll a queue for a 50,000-page crawl. Match the tool to the job.

Sources

Sources checked