Compare
BeautifulSoup vs lxml vs selectolax: measured parsing speed
A reproducible local benchmark of BeautifulSoup (html.parser and lxml), lxml, and selectolax on a 125 KB fixture, plus API, XPath, and malformed-HTML trade-offs.
Tested with Python 3.14.2, BeautifulSoup 4.14.3, lxml 6.0.2, selectolax 0.4.10 on 2 November 2026. Library versions and dates are part of the evidence, not decoration.
BeautifulSoup is a forgiving Python parsing library with a friendly tree API. lxml is a fast C-backed parser with full XPath. selectolax is a fast C-backed parser with a smaller CSS-first API. The speed difference is real and large; the ergonomics difference is also real. This page publishes our measurement rather than repeating a benchmark from elsewhere.
The benchmark
We generated a 125,598-byte HTML fixture with 1,000 product cards, parsed it, and extracted the 1,000 headings. We ran three warm-up iterations and reported the median of 15 timed runs. The machine was a Windows 11 PC with Python 3.14.2, BeautifulSoup 4.14.3, lxml 6.0.2, and selectolax 0.4.10. The script is published with this site as scripts/relaunch/benchmarks/parse-benchmark.py.
| Parser | Median time (ms) | Pages/sec (1,000-card fixture) |
|---|---|---|
| BeautifulSoup + html.parser | 133.75 | 7.5 |
| BeautifulSoup + lxml | 102.75 | 9.7 |
| lxml (XPath) | 11.32 | 88.3 |
| selectolax (CSS) | 3.88 | 257.6 |
These numbers measure parsing and one selector extraction. They do not include network time, retries, storage, or browser rendering. A real crawl is usually dominated by network and politeness delays, so a 30× parser difference does not mean a 30× faster crawl. It means the parser stops being the bottleneck when you need to process saved HTML at volume.
What the numbers mean
- BeautifulSoup is the slowest because it builds a rich Python object tree.
html.parseris pure Python; usinglxmlas the BeautifulSoup backend is faster but still pays for the BeautifulSoup wrapper. - lxml is much faster and gives you XPath. It is stricter about malformed HTML than BeautifulSoup, though
lxml.htmlis still forgiving. - selectolax is the fastest in this fixture, with a CSS-first API and a smaller surface. It is a good choice for high-volume extraction where the selectors are simple.
The benchmark is a microbenchmark. Different HTML, deeper trees, and more complex selectors change the ratios. Run it on your own corpus before making a decision.
API and ergonomics
BeautifulSoup wins on forgiveness and discoverability. soup.find, select, get_text, and the navigation API are easy to learn, and a malformed page rarely raises. It is the best choice for exploratory scraping, one-off scripts, and pages with messy markup.
lxml gives you xpath(), fast parsing, and a mature XML/HTML toolkit. The API is less friendly, and namespace handling can surprise you. It is the best choice when you need XPath or when parsing is the bottleneck.
selectolax has a small API: parse, css(), .text(), attributes. It is fast and pleasant for simple extraction. It is not the library to choose if you need intricate tree manipulation or the broadest malformed-HTML tolerance.
Feature comparison
| Feature | BeautifulSoup | lxml | selectolax |
|---|---|---|---|
| CSS selectors | Yes | Yes (via cssselect) | Yes |
| XPath | No (not natively) | Yes | No |
| Malformed HTML | Very forgiving | Forgiving | Forgiving |
| Pure Python | html.parser backend | No | No (C/Cython) |
| Speed | Slow | Fast | Fastest |
| Tree editing | Yes | Yes | Limited |
| Learning curve | Low | Medium | Low |
A practical recommendation
- Start with BeautifulSoup + html.parser for a new scraper. It is the easiest to debug and the parser is rarely the bottleneck on a polite crawl.
- Move to lxml when you need XPath or when you are parsing saved HTML at volume.
- Use selectolax for a high-throughput extraction service where the selectors are simple and the input is well-formed enough.
- Do not choose a parser because of a benchmark alone. Measure on your corpus, with your selectors.
The HTML and DOM lesson covers the tree model, and the CSS/XPath lesson covers the query languages.
Reproduce it
python -m pip install beautifulsoup4 lxml selectolax
python scripts/relaunch/benchmarks/parse-benchmark.py
The script prints the fixture size and the median/pages-per-second for each parser. Change CARDS, RUNS, and the selector to match your workload.