selectolax, sprawdzony: szybki parser HTML, który wyprzedza BeautifulSoup i dorównuje lxml

Ostatnia aktualizacja: July 17, 2026
selectolax, sprawdzony: szybki parser HTML, który wyprzedza BeautifulSoup i dorównuje lxml
Podsumowanie AI
Ta recenzja selectolax porównuje parser z lxml, BeautifulSoup, parsel oraz różnymi backendami pod kątem rozmiaru stron, pokrycia selektorów, zużycia pamięci i zachowania na nietypowym HTML-u. Wynika z niej, że selectolax jest wyraźnie szybszy od BeautifulSoup i konkurencyjny wobec lxml w pełnych zadaniach parsowania i zapytań, ale w tym środowisku testowym lxml potrafi być szybszy przy samym parsowaniu. Artykuł opisuje silniki Lexbor i Modest, luki w selektorach CSS, problem z zawartością template, oszczędność pamięci, kwestie licencyjne oraz scenariusze, w których selectolax jest mocnym zamiennikiem wolniejszych workflow parsujących w Pythonie.

W każdym wpisie o „najszybszym parserze HTML w Pythonie” prędzej czy później pojawia się selectolax, a cała historia zwykle kończy się na stwierdzeniu, że jest „dużo szybszy niż BeautifulSoup”. To akurat prawda. Część, której prawie nikt nie domyka, brzmi: co się stanie, gdy zestawimy selectolax z lxml — bo wtedy słowo „najszybszy” wymaga gwiazdki.

Postanowiłem więc porównać go porządnie: selectolax (na obu backendach) przeciwko lxml, BeautifulSoup z html.parser i z lxml, oraz parselowi, na pięciu rozmiarach stron od 1 KB do 10 MB. Każdy pomiar to mediana z trzech osobnych uruchomień procesu. selectolax zdeklasował BeautifulSoup i zrównał się z „czystym” lxml — a potem przegrał sam etap parsowania z lxml. Wszystkie liczby poniżej są wstępne i pochodzą z jednej maszyny (macOS arm64, Python 3.14.2); skrypty są w repo, więc zanim zacytujesz moje wyniki, uruchom je u siebie.

Czym selectolax naprawdę jest — a czym nie jest

selectolax to binding Pythona do dwóch silników C — Modest i Lexbor — które parsują HTML5 i pozwalają odpyt ywać go selektorami CSS. To nie crawler, nie przeglądarka i nie „scraper” w sensie jednego kliknięcia w przycisk. To narzędzie, któremu przekazujesz gotowy HTML po tym, jak już go pobrałeś. Sam maintainer opisuje je w skrócie jako „szybki parser HTML5 z selektorami CSS, napisany w Cythonie, wykorzystujący silniki Modest i Lexbor”.

Są dwa backendy i różnica między nimi ma większe znaczenie, niż sugeruje dokumentacja:

  • LexborHTMLParser (silnik Lexbor) — to właśnie jego README poleca używać od 2024 roku.
  • HTMLParser (silnik Modest) — oryginalna wersja, której biblioteka C, jak podaje README, „nie jest już rozwijana”.

Zanim przejdziemy do szybkości, kilka faktów wartych odnotowania. Na podstawie snapshotu repo z 2026-07-10 selectolax ma 1 653 gwiazdki, a najnowsze wydanie to v0.4.10 (maj 2026). PyPI deklaruje zgodność z Pythonem >=3.9,<3.15. Instalacja to najmniej dramatyczny element całej historii: pip install selectolax pobrał gotowe koło cp314 o rozmiarze 2,3 MB i zadziałał od razu na Pythonie 3.14 — bez pobierania przeglądarki, bez kroku doctor, bez kompilacji. To właśnie cicha przewaga parsera nad narzędziem opartym o przeglądarkę: po prostu się importuje i działa.

Warto też od razu wspomnieć o kwestii licencji: binding Pythona jest na licencji MIT, ale wheel zawiera wbudowane silniki, a one mają własne licencje — Modest jest na LGPL-2.1, Lexbor na Apache-2.0. Więc stwierdzenie „selectolax jest MIT” jest prawdziwe dla kodu Pythona, ale niepełne w odniesieniu do binarki, którą faktycznie dystrybuujesz. Jeśli dział prawny zwraca uwagę na komponenty redystrybuowane, to właśnie ten detal powinien trafić na radar.

Pytanie o szybkość, odpowiedziane twardymi liczbami

Zadanie, które mierzyłem, było następujące: sparsować ciąg HTML, pobrać cały tekst z <h3 class="title"> oraz pobrać atrybut href z każdego <a>. Opóźnienie w milisekundach podaję jako medianę z trzech osobnych uruchomień procesu; rozrzut między uruchomieniami pozostawał poniżej ok. 5% dla parserów opartych o C przy większości rozmiarów. Zanim zmierzono jakąkolwiek komórkę, wyjście każdego parsera sprowadziłem do skrótu treści, aby parser, który po cichu wykonał mniej pracy, został wykryty i wykluczony — na tych stronach wszystkie sześć wyników zgadzało się dla każdego rozmiaru, więc to rzeczywiste porównanie „jabłko do jabłka”. Pełne dane są w dołączonym bench_parse.json.

selectolax jest 12-17x szybszy niż BeautifulSoup w różnych rozmiarach stron

Stronaselectolax (Lexbor)selectolax (Modest)lxmlparselBS (lxml)BS (html.parser)
1 KB0.0270.0290.0360.0440.2810.323
10 KB0.1600.1710.1660.2281.7052.049
100 KB1.4641.5651.4232.02016.520.6
1 MB14.90116.914.17720.9181.9232.6
10 MB159.9247.9172.9231.92261.62788.7

W porównaniu z BeautifulSoup: około 12-17x szybciej, a obiegowe opinie to zaniżają

Po przeliczeniu na proporcje selectolax-Lexbor wychodzi około 12x szybszy od BeautifulSoup(html.parser) na stronie 1 KB, aż do około 17x przy 10 MB, oraz około 10-14x szybszy od BeautifulSoup(lxml) w tym samym zakresie. Liczba, która krąży po internecie — „selectolax jest jakieś 4-5x szybszy od BeautifulSoup” — jest zbyt niska względem html.parser i mniej więcej trafia tylko w zestawieniu z BeautifulSoup opartym o lxml. Prawdziwy mnożnik zależy od tego, o którą wersję BeautifulSoup chodzi i ile danych wyciągasz z każdej strony.

To też zgadza się z własnym benchmarkiem z README, który sugeruje przewagę 25,5x nad BeautifulSoup(html.parser). Żadna z tych liczb nie jest błędna. Zadanie z README (tytuł, linki, skrypty i meta z małych stron głównych) wykonuje mniej ekstrakcji na mniejszych stronach, więc bardziej waży narzut BeautifulSoup przy samym parsowaniu. Jeśli zawęzić zakres, wychodzi tak: selectolax jest mniej więcej 10-15x szybszy od BeautifulSoup przy realistycznym parsowaniu i ekstrakcji, szybciej na małych stronach i przy lżejszej ekstrakcji.

Jeśli dziś Twoim wąskim gardłem jest kod BeautifulSoup mielący strony, to taka migracja zwraca się sama. Ta teza nie budzi kontrowersji. Kolejna już tak.

W porównaniu z lxml: remis — a lxml wygrywa tę część, o której zwykle nikt nie wspomina

Spójrz znowu na wiersze dla 100 KB i 1 MB. Lexbor i lxml mieszczą się w granicy ok. 5% od siebie, ich przedziały z kolejnych uruchomień na siebie nachodzą, a według mojej metodologii to remis — bez zwycięzcy, bez „szybszy”. Jedyny moment, w którym selectolax faktycznie wychodzi z przodu, to strona 10 MB (159,9 ms vs 172,9 ms, czyli różnica 8,1% bez nakładających się przedziałów). W całym zadaniu selectolax dorównuje więc lxml, a wyprzedza go tylko na naprawdę największych dokumentach.

selectolax remisuje z lxml w całym zadaniu, ale lxml prowadzi w samym parsowaniu o 33-34 procent

Następnie oddzieliłem budowanie drzewa od zapytań CSS i wynik odwrócił się w sposób, który większość opisów pomija. W przypadku samego parsowania, bez żadnego zapytania, lxml był na tej maszynie konsekwentnie o około 33-34% szybszy niż selectolax-Lexbor — 77,9 ms wobec 116,6 ms na stronie 10 MB. W pełnym zadaniu oba narzędzia i tak się zbliżają, a moja robocza hipoteza (nieudowodniona eksperymentem przypisującym udział kosztów) jest taka, że na tych stronach zapytanie CSS stanowi niewielki fragment całkowitego czasu, więc przewaga lxml w samym parsowaniu zostaje rozmyta aż do wyrównania wyniku całkowitego.

To najbardziej podatna na podważenie teza w całym tekście i chcę jasno powiedzieć dlaczego. Odwraca popularną opinię, a jedyny opublikowany benchmark, który znalazłem dla samego parsowania — aows.jpt.sh — pokazuje wynik przeciwny, z selectolax szybszym mniej więcej 4x. Dlatego ją obwarowałem: wynik dotyczy jednej platformy (macOS arm64, Python 3.14, gotowe koła cp314 — na Linux x86_64 ani na buildzie ze źródeł nie był testowany), sprawdziłem go na czterech rozmiarach stron i na każdym utrzymywał się tak samo, a dodatkowo zweryfikowałem go ponownie na dwóch różnych API lxml, żeby wykluczyć artefakt API. Oba API lxml były szybsze od selectolax-Lexbor przy każdym rozmiarze. Nie przedstawiam więc tezy „lxml parsuje szybciej” jako prawdy objawionej — pokazuję po prostu, co wyszedł mi bench, ze skryptem w załączniku, wbrew większości opublikowanych wyników. Sprawdź to u siebie.

Jeszcze jeden przekrój: przy zapytaniu o 100 000 <a> na płaskiej stronie lxml i selectolax-Modest remisują (33,30 ms vs 34,19 ms, z nakładającymi się przedziałami), podczas gdy selectolax-Lexbor zostaje w tyle za oboma o około 15%. Wspólna cecha wszystkich trzech silników C jest taka, że są 5-7x szybsze od parsela lub BeautifulSoup przy masowym selekcjonowaniu, bo to model „jeden obiekt Pythona na każdy węzeł” naprawdę spowalnia pracę. Tak więc stwierdzenie „selectolax jest najszybszy w masowym wyborze CSS” też się nie broni — Modest tylko remisuje z lxml, a Lexbor przegrywa z nim.

Wniosek, pod którym naprawdę się podpiszę: przewaga selectolax nad lxml nie polega na szeroko rozumianej szybkości całego zadania. Wygrywa tylko na największej stronie. Jego sens wynika z innych rzeczy — ergonomii API, zachowania na śmieciowych danych i nowoczesnego CSS — i właśnie tam przenosi się dalsza część tej recenzji.

Pamięć i cold start: patrz na RSS, nie na profiler

Pamięć to obszar, w którym muszę skorygować własne wcześniejsze liczby, i właśnie dlatego ta korekta jest ważna. Mierząc różnicę RSS na stronie 10 MB z wyłączonym tracemalloc, BeautifulSoup zużywa około 1,5-1,8x więcej pamięci niż selectolax lub lxml — zakres zaczyna się od 1,51x (BS-lxml przy 218,4 MB wobec Lexbor przy 144,6 MB), a kończy na 1,75x. selectolax i lxml mieszczą się w tej samej, oszczędnej klasie; lxml wypada najoszczędniej pod względem RSS.

pamięć selectolax mierzona przez RSS przy wyłączonym profilerze

Wcześniej podałem „~3x” i ta liczba była błędna z pouczającego powodu: pomiar był wykonany z włączonym tracemalloc, a jego rejestrowanie alokacji mniej więcej podwaja widoczny RSS parsera, który najwięcej alokuje. Dlatego jeśli ktoś benchmarkuje pamięć parserów, to ważna uwaga brzmi: porównuj RSS z wyłączonym profilerem. Ustawianie parserów według szczytu tracemalloc wprowadza błędny porządek zwłaszcza dla silników C — u mnie przez to selectolax-Lexbor wyglądał na cięższy od Modest, choć w rzeczywistym RSS są do siebie bardzo zbliżone. BeautifulSoup jest tu faktycznie najcięższy, tylko nie aż trzy razy cięższy, jak sugerował zafałszowany instrument.

Cold start jest drobiazgiem, ale realnym: selectolax importuje się w około 14 ms, czyli mniej więcej tak samo jak lxml i około 2,3x szybciej niż bs4 albo parsel. Jeśli budujesz narzędzie CLI albo funkcję serverless, gdzie czas importu liczy się przy każdym wywołaniu, to ta różnica ma znaczenie.

Pokrycie selektorów CSS: mocne, ale z kilkoma realnymi lukami

Pokrycie CSS przetestowałem na macierzy 41 przypadków, gdzie każdy selektor był sprawdzany na fiksturze z wcześniej znanym poprawnym zestawem wyników, a dodatkowo wykonałem osobny test obliczony na złamanie silnika Lexbor. Każdy przypadek uruchamiałem w osobnym subprocessie, bo okazało się to konieczne — jeden z nich potrafi wywalić cały interpreter. Wyniki:

porównanie pokrycia selektorów CSS: soupsieve 41/41, Lexbor 39/41

SilnikPASSWRONGUNSUPPORTEDPROCESS_ABORT
soupsieve41000
selectolax Lexbor39020
lxml (cssselect)37130
parsel (cssselect)37130
selectolax Modest35321

Gdy dorzucimy selektory „wrogie”, Lexbor nie jest już absolutnym zwycięzcą — tym jest soupsieve, z czystym 41/41 wobec 39/41 w Lexbor. Dwie luki Lexbor to :lang(en) oraz :dir(rtl), które odrzuca błędem parsowania. Poza tym działa perfekcyjnie, w tym dla :has(), :is(), :where() oraz atrybutów niewrażliwych na wielkość liter.

Tam, gdzie Lexbor naprawdę błyszczy, jest zestaw cssselect. Flagowy selektor z README — div > :nth-child(2n+1):not(:has(a)) — zwraca poprawny zestaw zarówno w obu silnikach selectolax, jak i w soupsieve, ale zwraca zły zestaw w lxml i parsel, i to bez żadnego błędu. Scraper, który skopiuje ten selektor do Scrapy albo parsel, dostanie po cichu błędne wyniki. Dla precyzji: cssselect obsługuje :has() od wersji 1.2.0 (2022), a testowałem 1.4.0, więc to przypadek „obsługiwane, ale źle ewaluowane w złożonej kombinacji”, a nie „niewspierane”. To ciche zwrócenie złego zestawu dla tej konkretnej kombinacji nie jest opisane w trackerze cssselect, który notuje ograniczenia :has() jako błędy zgłaszane jawnie. Lexbor obsługuje też flagę atrybutu case-insensitive [data-role="LEAD" i], którą cssselect odrzuca bezpośrednio.

Są jednak dwie luki, które przesądzą o migracjach. selectolax nie wspiera w ogóle XPath — żaden backend nie udostępnia xpath() — i nie ma też pseudo-elementów ::text / ::attr(), bo to rozszerzenie parsel/Scrapy, a nie prawdziwy CSS. Jeśli Twoje obecne scrapery opierają się na XPath, to właśnie na tym się zatrzymasz; trzeba będzie przepisać selektory, a nie tylko podmienić bibliotekę. Z drugiej strony Lexbor ma pseudo-klasę :lexbor-contains("text" i) do wyszukiwania tekstu bez uwzględniania wielkości liter, której nie oferuje ani lxml, ani parsel, ani standardowy CSS — i działa ona zgodnie z dokumentacją.

Odporność na brzydki HTML, czyli tam, gdzie selectolax naprawdę pracuje na swoje miejsce

Prawdziwe scrapowanie oznacza wrzucanie parserowi śmieci i liczenie na to, że nie padnie. Przetestowałem 18 złośliwych przypadków i właśnie w tej kategorii argument za selectolax względem lxml jest najmocniejszy.

Jeśli podasz lxml.html.fromstring pusty string albo sam whitespace, dostaniesz ParserError("Document is empty"). Oba silniki selectolax zwracają natomiast prawidłowe, puste drzewo. Dla scrapera przechodzącego po liście URL-i, z których część odpowiada pustą treścią, to jeden try/except mniej do opakowywania całej logiki. selectolax bez problemu poradził sobie też z 100 000 elementów bez przepełnienia stosu.

Najostrzejszy podział pojawił się przy głębokim zagnieżdżaniu. Przy 1 000 i 5 000 poziomach zagnieżdżonych <div>, lxml po cichu gubi najgłębszą zawartość, podczas gdy selectolax ją zachowuje. libxml2 ucina głębokość parsowania mniej więcej na 256 poziomach i obcina drzewo bez błędu, więc najgłębszy tekst staje się po prostu niedostępny. Oba silniki selectolax zwracają pełne drzewo. To lustrzane odbicie pułapki <template>, do której za chwilę wrócę: tam Lexbor gubi treść, którą inne narzędzia zachowują; tutaj lxml gubi treść, którą selectolax zachowuje.

Nie każda komórka była wygraną. Backend Modest kończy cały interpreter Pythona sygnałem SIGABRT, gdy natrafi na :dir() — nie rzuca wyjątku, który da się przechwycić, tylko brutalnie zabija proces. To realne zastrzeżenie dotyczące stabilności dla każdego, kto nadal korzysta ze starszego backendu, i dokładnie ten rodzaj problemu pozostaje niewidoczny, dopóki nie położy produkcyjnego joba o 3 nad ranem.

Dwie pułapki cichej utraty danych, które warto znać przed wdrożeniem

Żadna z nich nie jest odkryciem — obie są opisane upstream — ale obie po cichu kosztują realne dane, i żadna nie jest wyraźnie zaznaczona w README.

Lexbor pomija <a> wewnątrz <template>

Na testowanej przeze mnie stronie MDN na żywo selectolax-Lexbor znalazł 497 linków, podczas gdy lxml, oba backendy BeautifulSoup, a nawet własny backend Modest selectolax znalazły 508. Brakujące jedenaście to przełącznik języka i link do dyskusji umieszczone wewnątrz elementów <template> (strona korzysta z komponentów webowych Lit).

pułapka template w selectolax Lexbor: 497 linków wobec 508 linków

Przyczyna jest technicznie poprawna: zgodnie ze specyfikacją HTML5 zawartość <template> jest parsowana do osobnego, nieaktywnego fragmentu, a nie do zwykłego DOM, i Lexbor trzyma się tego ściśle — tree.css("a") nie schodzi do zawartości template. lxml, oba backendy BeautifulSoup i Modest spłaszczają zawartość template do głównego drzewa, więc znajdują te linki. To udokumentowany otwarty problem (selectolax#146, z przyczyną na poziomie silnika w lexbor#170), i oba podejścia da się obronić — Lexbor jest wręcz bardziej zgodny ze specyfikacją. Ale dla developera korzystającego z rekomendowanego backendu te dane znikają po cichu, bez błędu. Warto też powiedzieć odwrotnie: pozostałe parsery pokazują nieaktywne treści template, których przeglądarka nigdy nie renderuje, więc mogą zwrócić Ci dane-fantomy, których użytkownik nie widzi. Awaryjny, pewny wybór dla takiej strony to backend Modest albo inna biblioteka.

Bity niebędące UTF-8 cicho psują .text()

Jeśli przekażesz selectolaxowi bajty, które nie są poprawnym UTF-8, parsowanie powiedzie się — a uszkodzenie ujawnia się później, i to gorzej niż przy uczciwym błędzie. Dla "<p>café éè</p>".encode("latin-1"), .text() w Lexbor zwraca znaki zastępcze, .text() w Modest po cichu pomija problematyczne bajty, a oba silniki rzucają UnicodeDecodeError dopiero wtedy, gdy dotkniesz .html. Binding dekoduje przy odczycie jako surowy UTF-8, a nie podczas parsowania. To jest związane ze znanym problemem selectolax dotyczącym ścisłości kodowania i dekodowania.

Naprawa jest jednolinijkowa i warto ją zapamiętać: samodzielnie zdekoduj bajty wcześniej — LexborHTMLParser(resp.content.decode("latin-1")) — a oba silniki zwrócą poprawnie 'café éè'. W praktyce zawsze przekazuj do selectolax str, nigdy surowe bytes spoza UTF-8. README nie mówi tego wprost.

Wymiary produkcyjne (jedna obserwacja, więc traktuj je jako orientacyjne)

Kolejne wyniki mierzyłem tylko raz, bez trzech powtórzeń, więc zaznaczam je jako sygnały, a nie ostateczne liczby.

Najciekawsze jest skalowanie wątków. Parsując stronę 1 MB 48 razy na czterech wątkach, selectolax dał około 3,5-3,9x przyspieszenia wall-clock — to typowy sygnał, że biblioteka zwalnia GIL podczas parsowania w C — podczas gdy BeautifulSoup(lxml) w wersji wielowątkowej zrobił się kilkukrotnie wolniejszy, co wskazuje na serializację pracy przez GIL. lxml wypadł pośrodku i wynik był niejednoznaczny. W erze free-threadingu, do której zmierza Python, selectolax, który potrafi się równoleglić między wątkami tam, gdzie BeautifulSoup nie daje rady, jest realną, choć wciąż wstępną przewagą. To jeden rozmiar strony i jeden model wątków, a mechanizm pozostaje hipotezą, nie czymś potwierdzonym przez instrumentację kodu C.

Jeśli chodzi o wycieki: w 2 000 iteracjach parsowanie-ekstrakcja-zwolnienie na 1 MB żaden z trzech parserów nie pokazał liniowego wzrostu RSS typowego dla wycieku — każdy ustabilizował się w ograniczonym paśmie roboczym. Uf i am temu wynikowi szczególnie dlatego, że puściłem przez ten sam instrument znany przypadek testowy z wyciekiem i faktycznie urósł on do +198 MB, co potwierdza, że instrument potrafiłby wyciek zobaczyć, gdyby był obecny, a po prostu go nie znalazł w parserach. A uchwyt do węzła, który zachowałem po wyjściu z zakresu drzewa nadrzędnego, nadal działał i nie powodował segfaulta. Wszystko to pojedyncze obserwacje, nic z wielogodzinnego soak testu.

Gdzie pasuje selectolax — a gdzie warto go odłożyć

Wszystko powyżej dotyczy jednego zadania: zamiany posiadanego HTML-a na dane strukturalne, szybko. I właśnie w tym selectolax jest bardzo dobry. Czego celowo nie robi, to pobierania strony, renderowania JavaScriptu, rotowania proxy, omijania CAPTCHA ani zgadywania, które elementy są Ci potrzebne. To nadal Twoja logika. selectolax to warstwa parsowania i nie udaje niczego więcej.

I właśnie w tym miejscu pojawia się granica, za którą usługa zarządzanej ekstrakcji stoi ponad parserem, zamiast go zastępować. Jeśli wolisz nie budować i nie utrzymywać samodzielnie całego stosu pobieranie-renderowanie-anti-bot-ekstrakcja, Thunderbit udostępnia to jako API, serwer MCP i CLI — POST /distill zamienia stronę w czysty Markdown, a POST /extract zwraca ustrukturyzowany JSON dopasowany do schematu, z renderowaniem JS i obsługą anti-bot po Twojej stronie. To inna warstwa tego samego problemu: selectolax wybierzesz wtedy, gdy masz już HTML i chcesz czystej szybkości parsowania pod własną kontrolą, a Thunderbit API, serwer MCP albo CLI wtedy, gdy chcesz oddać pobieranie i ekstrakcję w całości i po prostu dostać uporządkowane dane. To nie zamiana — to inny poziom tego samego stacku.

Wypróbuj Thunderbit do ekstrakcji danych z sieci

Zalety, wady i kto powinien z niego korzystać

Gdzie selectolax wygrywa:

  • Około 12-17x szybciej niż BeautifulSoup przy realistycznym parsowaniu i ekstrakcji, stabilnie na trzech rzędach wielkości rozmiaru strony.
  • Oszczędny pamięciowo (poziom lxml, około 1,5-1,8x lżejszy niż BeautifulSoup) i z importem około 14 ms.
  • Dobrze radzi sobie z wejściem, które psuje lxml — pustym, białym i patologicznie głębokim zagnieżdżeniem.
  • Nowoczesny CSS, w tym :has(), :is(), :where(), atrybuty case-insensitive oraz dostępne tylko w Lexbor :lexbor-contains().
  • DOM z bezpiecznym odczytem i zapisem None: brakujące elementy zwracają None albo [] zamiast wyjątku, a drzewo można faktycznie modyfikować i serializować ponownie.
  • Aktywne utrzymanie (v0.4.10, środek 2026) i banalna instalacja.

Gdzie nie:

  • Nie jest szeroko szybszy od lxml — na pełnym zadaniu remis, a w samym parsowaniu przegrywał na moim benchmarku.
  • Brak XPath i brak ::text/::attr() — to twarda bariera migracyjna dla scraperów opartych o XPath.
  • Dwie pułapki cichej utraty danych: zawartość <template> w Lexbor i bajty spoza UTF-8 przez .text().
  • Backend Modest to rozwiązanie legacy i potrafi zakończyć interpreter sygnałem SIGABRT na :dir().
  • Każda liczba w tym tekście pochodzi z jednej platformy (macOS arm64, Python 3.14) i ma charakter wstępny.

Czy warto go używać? Tak, jeśli chcesz wydajność klasy lxml, ale z wygodniejszym, bezpiecznym dla None API i wyraźnie lepszym zachowaniem na pustym oraz uszkodzonym wejściu — i akceptujesz świat tylko CSS. Jeśli Twój kod bazuje na XPath, koszt przepisania jest realny i trzeba go uczciwie przeliczyć. A jeśli gonisz za „jednym najszybszym parserem”, to z tego benchmarku wynika coś bardziej przyziemnego: selectolax i lxml są na tyle blisko siebie, że o wyborze decydują ergonomia i odporność, a nie czysta szybkość. I to jest często lepszy powód do wyboru narzędzia.

Wypróbuj Thunderbit do ekstrakcji danych z sieci Get Started Free

FAQ

Czy selectolax jest szybszy niż BeautifulSoup? Tak, zdecydowanie — około 12-17x szybciej niż BeautifulSoup(html.parser) i 10-14x szybciej niż BeautifulSoup(lxml) przy realistycznym zadaniu parsowania i ekstrakcji, utrzymując tę przewagę od stron 1 KB do 10 MB (macOS arm64, Python 3.14). Często cytowane „4-5x” zaniża różnicę względem html.parser.

Czy selectolax jest szybszy niż lxml? Nie w ujęciu ogólnym. W pełnym zadaniu parsowania i ekstrakcji remisuje przy 100 KB i 1 MB, a wygrywa dopiero przy stronie 10 MB. W samym parsowaniu, bez zapytania, lxml był u mnie faktycznie o około 33-34% szybszy — to wynik niezgodny z oczekiwaniem, więc warto samemu go zweryfikować na własnym sprzęcie.

Czy powinienem wybrać backend Lexbor czy Modest? W prawie każdym przypadku Lexbor — to utrzymywany, pełniejszy silnik, który README rekomenduje, z lepszym pokryciem CSS. Jedyny wyjątek to strona ukrywająca treść w elementach <template>, gdzie poprawne względem specyfikacji zachowanie Lexbor usuwa tę treść, a Modest ją przypadkiem zachowuje. Modest ma też ostre krawędzie, w tym twardy crash interpretera na :dir().

Czy selectolax wspiera XPath? Nie. Żaden backend nie udostępnia metody xpath() — selectolax działa wyłącznie z CSS. Jeśli Twoje scrapery zależą od XPath, migracja oznacza przepisanie selektorów, a to największy koszt przejścia z lxml- lub parsel-based stacku.

Dlaczego wynik z selectolax jest zniekształcony albo brakuje elementów? Najczęściej są dwa powody. Jeśli tekst wraca ze znakami zastępczymi albo bez polskich/obcych znaków, prawdopodobnie podałeś surowe bajty spoza UTF-8 — najpierw zdekoduj je do str (resp.content.decode("latin-1")), a dopiero potem parsuj. Jeśli na nowoczesnej stronie brakuje linków albo elementów, mogą one znajdować się wewnątrz tagów <template>, do których backend Lexbor nie schodzi; dla takiej strony przełącz się na Modest albo inny parser.

Ke
Ke
CTO w Thunderbit | Starszy data scientist i ekspert ML Dzięki prawie dziesięciu latom doświadczenia w uczeniu maszynowym i data science, Ke Shen jest absolwentem Columbia University i byłym starszym data scientistą w Walmart Labs. Dysponując dogłębną, uznaną przez branżowych ekspertów wiedzą w zakresie Python, R, Java i statystyki, dzieli się sprawdzonymi w boju spostrzeżeniami na temat wdrażania złożonych algorytmów AI — od teorii po architekturę gotową do produkcji.

Wypróbuj Thunderbit

Zbieraj leady i inne dane w zaledwie 2 kliknięcia. Wspierane przez AI.

Pobierz Thunderbit To darmowe
Wyciągaj dane z użyciem AI
Łatwo przenoś dane do Google Sheets, Airtable lub Notion
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week