PyQuery dodaje składnię jQuery bez istotnego dla decyzji narzutu selektora w tym benchmarku

Ostatnia aktualizacja: August 18, 2026
PyQuery dodaje składnię jQuery bez istotnego dla decyzji narzutu selektora w tym benchmarku
Podsumowanie AI
PyQuery nakłada na lxml API w stylu jQuery. W pięciu rozmiarach stron od 1 KB do 10 MB wszystkie pięć pokazanych median było niższych niż w surowym lxml, w tym o 1,5% różnicy przy największym rozmiarze. Benchmark nie dowodzi, że wrapper jest szybszy; nie wykazał różnicy na tyle dużej, by zmienić tę decyzję dotyczącą selekcji i odczytu. Od 10 KB wzwyż w pokazanych medianach wynik był też o kilka procent zbliżony do selectolax. Bez z góry ustalonego marginesu równoważności to po prostu bliski rezultat, a nie statystyczny remis.

PyQuery nakłada na lxml API w stylu jQuery. W pięciu rozmiarach stron od 1 KB do 10 MB wszystkie pięć pokazanych median było niższych niż w surowym lxml, w tym o 1,5% przy największym rozmiarze. Benchmark nie dowodzi, że wrapper jest szybszy; nie wykazał różnicy na tyle dużej, by zmienić tę decyzję dotyczącą selekcji i odczytu.

Od 10 KB wzwyż w pokazanych medianach wynik był też o kilka procent zbliżony do selectolax. Bez z góry ustalonego marginesu równoważności to po prostu bliski rezultat, a nie statystyczny remis.

Czym jest PyQuery

PyQuery to biblioteka Pythona, która udostępnia selektory i łańcuchowe wywołania znane z jQuery na drzewie dokumentu lxml. Testowana wersja: 2.1.0, licencja BSD, 2 380 gwiazdek na GitHubie, 59 otwartych zgłoszeń, ostatni push 2026-07-27.

Oficjalna dokumentacja: PyQuery documentation.

System diagram: jQuery Syntax Over lxml

from pyquery import PyQuery as pq
d = pq(html)
titles = [e.text_content() for e in d("h3.title")]
hrefs = [e.get("href") for e in d("a")]

Zwracane elementy są elementami lxml, więc wszystko, co potrafisz robić z lxml, nadal działa. W tym właśnie tkwi założenie: PyQuery to warstwa wygody, a nie parser. pip install pyquery pobiera 3 pakiety — lxml, cssselect i samo PyQuery — oraz 20,1 MiB, z czego prawie wszystko stanowią skompilowane rozszerzenia lxml.

Jeśli używałeś cheerio w Node, to jest ten sam ogólny pomysł API, tylko w Pythonie. Dwa testowane tu selektory działały w obu przypadkach; test nie potwierdza pełnej zgodności języka selektorów między cssselect i cheerio.

Pomiar

Ta baza badawcza już wcześniej miała benchmark parserów z cechą, której brakuje większości testów: bramką zgodności, która hashuje wyekstrahowaną treść — posortowane tytuły i posortowane hrefy — względem parsera referencyjnego, tak aby biblioteka, która „omija” pracę, nie mogła uzyskać sztucznie szybkiego czasu. Pięć rozmiarów stron, 50 iteracji, trzy niezależne przebiegi.

Dodanie do tego PyQuery wymagało dwóch rzeczy.

Ponownego uruchomienia referencji. selectolax został uruchomiony jeszcze raz w tym samym procesie. Hash treści zgodził się w 5 z 5 rozmiarów, a p50 wylądował między 0,989× a 1,079× opublikowanej wartości — czyli użyto tego samego komputera i tego samego środowiska testowego.

Uruchomienia lxml także w tym samym procesie. Opublikowany benchmark zapisuje maszynę i wersję Pythona, ale nie wersje bibliotek, więc wiersz dla lxml mógł pochodzić z innego wydania lxml niż to, które PyQuery opakowuje tutaj. Porównywanie przez taką lukę oznaczałoby de facto zestawienie dwóch wersji lxml i nazwanie tego „narzutem wrappera”. Uruchomienie lxml obok PyQuery usuwa tę wątpliwość — w tym venv oba używają lxml 6.1.1.

Rozmiar stronyselectolaxPyQuerylxmlPyQuery vs lxml
1 KB0,0286 ms0,0456 ms0,0508 ms0,90×
10 KB0,1725 ms0,1728 ms0,1802 ms0,96×
100 KB1,4855 ms1,4093 ms1,4145 ms1,00×
1 MB14,97 ms14,96 ms15,03 ms1,00×
10 MB158,10 ms162,86 ms165,25 ms0,99×

p50 w milisekundach, mediana z trzech przebiegów, wszystko w jednym procesie. parser-bench.json. Wszystkie trzy hashe treści zgadzały się z referencją dla każdego rozmiaru.

Narzut wrappera, którego tu nie ma

Measured results chart: PyQuery and lxml on the same fixture

PyQuery wypadł na poziomie lub poniżej surowego lxml w każdej pokazanej medianie. To nie jest dowód, że wrapper przyspiesza parsowanie. Trzy mediany z przebiegów i brak wcześniej ustalonego marginesu równoważności pozwalają na węższy wniosek: na tym zestawie danych nie było istotnego dla decyzji narzutu selektora.

Przy 10 MB trzy przebiegi PyQuery wyniosły 162,86, 163,17 i 161,13 ms; lxml: 169,46, 165,25 i 164,18. Zakresy są bliskie, ale się nie nakładają. Przy 1 MB mediany różnią się o 0,5%. Takie małe serie wspierają praktyczną ocenę, a nie twierdzenie o statystycznej równoważności.

System diagram: Wrapper and Parser Boundaries

Mechanizm jest prosty: pq(html) buduje drzewo lxml tylko raz, d("h3.title") kompiluje selektor CSS przez cssselect tak jak robi to tree.cssselect(), a zwracane elementy są elementami lxml. W tym mierzonym gorącym fragmencie PyQuery wykonuje niewiele własnej pracy. Traversal, modyfikacje, powtarzane zapytania, import i pamięć pozostają poza tezą o czasie samego selektora.

Bliskie wyniki od 10 KB wzwyż

Bardziej użytecznym wnioskiem jest pierwsza kolumna.

Od 10 KB wzwyż rozrzut median od najszybszego do najwolniejszego między selectolax, lxml i PyQuery wyniósł 4,5% przy 10 KB, 5,4% przy 100 KB, 0,5% przy 1 MB i 4,5% przy 10 MB. Ten run nie był testem równoważności; praktyczny wniosek jest taki, że te różnice nie zmieniłyby większości wyborów parsera dla tego zadania.

selectolax rzeczywiście jest szybszy przy 1 KB — 0,0286 ms wobec 0,0456 i 0,0508 — ale ten wiersz jest bezużyteczny. Między trzema parserami rozrzut w tym rozmiarze wynosi 77,6%, a same trzy przebiegi selectolax mieściły się w zakresie 0,0267 do 0,0404 ms. Przy 28 mikrosekundach timer i scheduler dominują. Nie chciałbym niczego tam porównywać.

W tym zadaniu polegającym na selekcji i odczycie wybieraj spośród tych trzech na podstawie API i faktycznych zależności, a nie domniemanej hierarchii szybkości. PyQuery nie wykazał istotnego dla decyzji kary względem lxml. selectolax używa innego stosu parsera, ale ten artykuł nie mierzył jego zajętości instalacyjnej, pokrycia wheelami ani wymagań budowania na tej samej podstawie.

Dla porównania opublikowany benchmark umieścił na tym samym fixture 10 MB jeszcze dwie inne opcje w Pythonie, i to one faktycznie się różnią:

Parser (10 MB)p50
selectolax (lexbor)159,93 ms
lxml172,93 ms
parsel231,85 ms
selectolax (modest)247,95 ms
BeautifulSoup + lxml2 261,56 ms
BeautifulSoup + html.parser2 788,75 ms

Opublikowane wartości z bench_parse.json.

Historyczne wiersze benchmarku pokazują, że BeautifulSoup jest ponad rząd wielkości wolniejszy od szybszych median parserów na tym fixture. Te wiersze nie były ponownie uruchamiane z aktualną parą PyQuery/lxml w tym samym procesie, więc są raczej kontekstem niż kontrolowanym mnożnikiem dla głównego werdyktu.

Zapisany wiersz dla cheerio wynosił 2 927,89 ms (2927.8857 w parser-bench.json) i odpowiadał mu taki sam hash wyekstrahowanej treści. Ten wynik między środowiskami zależy też od Node, wersji pakietów i historycznych warunków uruchomienia; nie należy go czytać jako izolowanego mnożnika szybkości samej biblioteki.

Rzeczywista konfiguracja

BibliotekaPakietyMiejsce na dyskuLicencjaGwiazdkiOstatni push
PyQuery320,1 MiBBSD2 3802026-07-27
cheerio (Node)22 (npm)9,0 MiBMIT30 4492026-08-11

Oficjalne źródło: PyQuery on PyPI.

metadata-snapshot.json.

Trzy pakiety to schludny ślad zależności, a dwa z nich — lxml i cssselect — to rzeczy, które już i tak ma wiele projektów scrapingowych w Pythonie. W takim przypadku marginalny koszt PyQuery to tylko kilkadziesiąt kilobajtów.

Te 20,1 MiB to skompilowane rozszerzenia lxml, a nie PyQuery. To dokładnie te same 20 MiB, które płacisz, używając bezpośrednio lxml.

Pakiet ma licencję BSD. W momencie robienia zrzutu miał 59 otwartych zgłoszeń i push wykonany trzy tygodnie przed testem; same te obserwacje nie dowodzą jakości utrzymania ani przyszłej kompatybilności.

Pamięć i to, co robi z nią uszkodzony HTML

Dwie rzeczy, które w każdym teście z tej serii były wcześniej oznaczone jako nieprzetestowane, zostały teraz zmierzone.

Szerszy kontekst testów obciążeniowych znajdziesz w porównaniu pamięci i niepoprawnego HTML dla dziesięciu bibliotek.

Szczytowy resident memory, mierzony przez /usr/bin/time -l, z jednym świeżym procesem na komórkę — dolny próg importu to koszt załadowanej i bezczynnej biblioteki, a szczyty obejmują dokument.

BibliotekaRuntimePróg importuPeak 226 KBPeak 10 MB
html2textpython3.1418,719,971,2
pyquerypython3.1430,333,9172,5
resiliparsepython3.1420,525,1225,1
markdownifypython3.1423,928,9278,5
goose3python3.1444,152,4398,5
cheerionode2266,876,5398,5
justextpython3.1430,336,6431,2
newspaper4kpython3.1452,661,8668,5
trafilaturapython3.1452,564,8927,1
turndownnode2247,868,42947,1

memory-results.json. Baseline Pythona i Node nie są ze sobą porównywalne; interpreter jest obecny w obu.

PyQuery jest lżejszy niż resiliparse na dużym dokumencie — 172,5 MiB wobec 225,1 — mimo wyższego progu importu. Drzewo lxml jest zwarte, a większość progu 30,3 MiB w PyQuery wynika z samego załadowania lxml, a nie z działań PyQuery.

Uszkodzony HTML. Dwanaście dokumentów, z których każdy psuje dokładnie jedną rzecz — niezamknięte tagi, źle zagnieżdżone elementy inline, atrybuty bez cudzysłowu zawierające spacje, przypadkowe zamknięcia, brak <html> w ogóle, duplikaty atrybutów, dokument ucięty w połowie tagu, błędne encje, niezamknięty <script>, kłamliwa deklaracja kodowania, komentarz zawierający znaczniki i 600 poziomów zagnieżdżenia — plus dwie poprawne próbki kontrolne o dopasowanych rozmiarach, bo „zwrócił nic” mówi coś o uszkodzeniu tylko wtedy, gdy biblioteka milczy również na czystym dokumencie o tym samym rozmiarze.

pyquery zgłosił wyjątek w 0 z 14 przypadków i zwrócił nic w 1, odzyskując 10/22 znaczników kontrolnych w uszkodzonych fixture'ach (malformed-results.json). Jedna próbka jest wyłączona z tego zliczenia: zgodnie z HTML5 wszystko po niezamkniętym <script> jest treścią skryptu, więc utrata tych danych jest tam poprawna, a ich odzyskanie byłoby odchyleniem. Bez bezpośrednich wyników alternatyw obok tego wyniku 10/22 jest raczej obserwacją odporności niż rankingiem wyboru parsera.

Plusy i minusy

Na plus. Składnia jQuery, znana każdemu, kto pisał frontendowy JavaScript albo używał cheerio. W tym teście nie pojawił się istotny dla decyzji narzut selektora względem surowego lxml. Tylko 3 pakiety, z czego 2 prawdopodobnie już masz w swoim drzewie zależności. Zwraca elementy lxml, więc techniki lxml nadal są dostępne. BSD. Hashe treści zgadzały się z referencją we wszystkich pięciu rozmiarach.

Na minus. 20,1 MiB, bo lxml. 2 380 gwiazdek oznacza znacznie mniejszą społeczność niż 30 449 w cheerio — czyli mniej gotowych przykładów, gdy coś jest nietypowe. To warstwa wygody, więc jeśli lxml czegoś nie potrafi, PyQuery też tego nie zrobi. A jeśli liczyłeś, że API jQuery da wydajność, to nie: daje ergonomię, a pracę wykonuje parser pod spodem.

Kto powinien używać, a kto nie

Użyj PyQuery, jeśli ty lub twój zespół wolicie selektory w stylu jQuery w Pythonie. Zmierzony etap budowy, dwa selekcje i odczyty nie wykazały istotnej dla decyzji kary względem lxml; innych operacji PyQuery nie mierzono.

Użyj bezpośrednio lxml, jeśli wolisz XPath albo chcesz mieć jeden pakiet mniej. Ten przebieg nie pokazał powodu związanego z szybkością selektorów, który rozstrzygałby między nimi.

Rozważ selectolax, jeśli jego API parsera i stos zależności pasują do twojego projektu. Wiersz dla 1 KB jest wyraźnie nieklasyfikowany, a ten artykuł nie uzasadnia twierdzenia o „najmniejszych zależnościach”.

W Node, analogicznym kształtem API jest cheerio. Zapisane wyniki między środowiskami były tu wolniejsze, ale różnice między runtime i historycznymi przebiegami nie pozwalają na czysty wniosek tylko o bibliotece.

Gdzie pasuje zarządzane API

PyQuery parsuje HTML, który już masz. Nie pobiera strony, nie renderuje JavaScriptu ani nie obsługuje warstwy anty-bot — żaden parser w tym porównaniu tego nie robi, a na wielu prawdziwych celach to właśnie ta trudniejsza połowa.

Nota autora: Thunderbit to nasza zarządzana opcja do pobierania, renderowania i ekstrakcji z URL-i. Nie była tu benchmarkowana przeciwko PyQuery. Granica jest prosta: albo masz już HTML i chcesz lokalnych selektorów, albo chcesz, by pobieranie strony i ekstrakcja działały jako usługa.

Uczciwe ujęcie: jeśli masz HTML i znasz selektory, PyQuery jest darmowy i wygodny. Jeśli selektory ciągle się psują albo pobierasz dane na dużą skalę, to już zupełnie inny zakup.

Szerszy przegląd rynku znajdziesz w naszym zestawieniu API do web scrapingu, a także w przewodniku po open-source scraperach dla rozwiązań self-hosted. Jeśli wynik parsowania trafia do modelu, konwersja HTML do Markdown w Pythonie pokazuje, gdzie zaczyna się utrata jakości.

Wypróbuj Thunderbit do ekstrakcji danych z sieci

Czy warto używać PyQuery?

Tak, jeśli chcesz składni w stylu jQuery w Pythonie, a zmierzona ścieżka selekcji i odczytu odpowiada twojemu obciążeniu.

Benchmark nie wykazał istotnego dla decyzji narzutu selektora względem lxml, zachowując jednocześnie zgodność hashy treści. Nie dowiódł jednak zerowego kosztu całej biblioteki.

Przy pięciu rozmiarach mediany trzech parserów Pythona były na tyle blisko, że w tym zadaniu bardziej prawdopodobne jest, iż zadecyduje dopasowanie API. Zdefiniuj margines równoważności i uruchom ponownie dokładne alternatywy, zanim zamienisz tę ocenę w szerszy ranking parserów.

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

FAQ

Czy PyQuery spowalnia lxml? W tym przebiegu nie pojawił się istotny dla decyzji narzut selektora. W pięciu rozmiarach stron mediany były na poziomie lub poniżej surowego lxml, przy czym oba działały na lxml 6.1.1 w tym samym procesie. Przy 10 MB bliskie zakresy się nie nakładały: PyQuery 161,13–163,17 ms i lxml 164,18–169,46 ms. pq(html) buduje drzewo lxml, a testowane selektory kompilują się przez cssselect.

Czy selectolax jest szybszy niż PyQuery? Mediana przy 1 KB była niższa, ale ten wiersz nie został sklasyfikowany, bo przy skali mikrosekundowej dominuje zmienność. Od 10 KB wzwyż rozrzut median wynosił 0,5%–5,4%. To jest wynik bliski dla tego zadania, a nie dowód równoważności ani nakładających się zakresów w każdym przypadku.

Dlaczego uruchomić lxml ponownie zamiast cytować opublikowaną wartość? Bo opublikowany benchmark zapisuje maszynę i wersję Pythona, ale nie wersje bibliotek. Jego wiersz dla lxml mógł pochodzić z innego wydania niż to, które PyQuery opakowuje dziś, a luka wersji wyglądałaby jak nieistniejący narzut wrappera. Uruchomienie obu w jednym procesie na lxml 6.1.1 usuwa niejasność.

Jak to wypada na tle cheerio? To ten sam ogólny pomysł API, ale inny ekosystem. Dwa testowane tu selektory działały w obu, a hashe treści zgadzały się we wszystkich pięciu rozmiarach; nie dowodzi to pełnej zgodności selektorów. Zapisane czasy cheerio były wolniejsze, ale różnice między środowiskami i historycznymi przebiegami uniemożliwiają twierdzenie o mnożniku tylko dla biblioteki.

Czego tu nie testowano? Pamięć mierzono jako szczytowy RSS dla samego importu, dokumentu 226 KB i dokumentu 10 MB. Niepoprawny HTML testowano na 12 uszkodzonych dokumentach plus dwóch dopasowanych kontrolach. Nadal nieprzetestowane są wydajność modyfikacji i traversalu w PyQuery, cache powtarzanych zapytań, pobieranie URL-i, współbieżność oraz reprezentatywne obciążenia z prawdziwych stron. Czas dla 1 KB pozostaje nieklasyfikowany.

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.
Spis treści
Thunderbit · Agent AI do danych z internetu

Wyciągaj dane z dowolnej strony w 1 klik

Zaufało nam ponad 250 000 użytkowników
dostępny darmowy plan
Od strony do arkusza kalkulacyjnego
Opisz, czego potrzebujesz — Agent AI Thunderbit zbierze to i wyeksportuje do Excela, Google Sheets, Airtable lub Notion. Start jest darmowy.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week