PyQuery ajoute la syntaxe jQuery sans surcoût de sélection déterminant dans ce benchmark

Dernière mise à jour le August 18, 2026
PyQuery ajoute la syntaxe jQuery sans surcoût de sélection déterminant dans ce benchmark
Résumé IA
PyQuery apporte une API de style jQuery par-dessus lxml. Sur cinq tailles de page de 1 KB à 10 MB, les cinq médianes affichées étaient toutes inférieures à celles de lxml brut, y compris un écart de 1,5 % à la plus grande taille. Le benchmark ne montre pas que le wrapper soit plus rapide ; il n’a trouvé aucune différence assez importante pour modifier cette décision autour du couple sélection/lecture. À partir de 10 KB, il restait aussi dans un écart de quelques pourcents de selectolax dans les médianes affichées. Sans marge d’équivalence prédéfinie, il s’agit d’un résultat proche, pas d’une égalité statistique.

PyQuery apporte une API au style jQuery par-dessus lxml. Sur cinq tailles de page allant de 1 KB à 10 MB, les cinq médianes affichées étaient toutes inférieures à celles de lxml brut, y compris un écart de 1,5 % à la plus grande taille. Ce benchmark ne montre pas que le wrapper est plus rapide ; il n’a relevé aucune différence assez nette pour changer cette décision autour du duo sélection/lecture.

À partir de 10 KB, les médianes affichées restaient aussi à quelques pourcents de selectolax. Sans marge d’équivalence définie à l’avance, il s’agit d’un résultat serré, pas d’une égalité statistique.

Qu’est-ce que PyQuery ?

PyQuery est une bibliothèque Python qui vous donne, sur un arbre de documents lxml, la syntaxe de sélection et de chaînage de jQuery. Version testée : 2.1.0, licence BSD, 2 380 étoiles GitHub, 59 tickets ouverts, dernier push le 2026-07-27.

Référence officielle : documentation PyQuery.

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")]

Les éléments renvoyés sont des éléments lxml ; tout ce que vous savez déjà faire avec lxml continue donc de fonctionner. C’est exactement le principe : PyQuery est une couche de confort, pas un parseur. pip install pyquery installe 3 packages — lxml, cssselect et PyQuery lui-même — pour 20,1 MiB, presque entièrement composés des extensions compilées de lxml.

Si vous avez déjà utilisé cheerio dans Node, l’idée d’API est la même en Python. Les deux sélecteurs testés ici fonctionnaient dans les deux cas ; ce test ne prouve pas une parité complète du langage de sélection entre cssselect et cheerio.

La mesure

Cette base de recherche disposait déjà d’un banc de test de parseurs avec une propriété que la plupart des benchmarks n’ont pas : une porte de parité qui hache le contenu extrait — titres triés plus hrefs triés — par rapport à un parseur de référence, de sorte qu’une bibliothèque qui « triche » en faisant moins de travail ne peut pas afficher un faux temps rapide. Cinq tailles de page, 50 itérations, trois exécutions indépendantes.

L’ajout de PyQuery a demandé deux choses.

Relancer la référence. selectolax a été relancé dans le même processus. Son hachage de contenu a été reproduit sur 5 tailles sur 5, et sa médiane p50 s’est située entre 0,989× et 1,079× de la valeur publiée — il s’agit donc bien de la même machine et du même environnement de test.

Exécuter aussi lxml dans le même processus. Le benchmark publié indique la machine et la version de Python, mais pas les versions des bibliothèques ; sa ligne lxml pourrait donc provenir d’une autre version d’lxml que celle encapsulée ici par PyQuery. Comparer à travers cet écart reviendrait à comparer deux versions d’lxml et à parler d’une surcharge du wrapper. L’exécution côte à côte lève le doute — ici, les deux sont en lxml 6.1.1 dans cet environnement virtuel.

Taille de pageselectolaxPyQuerylxmlPyQuery 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 en millisecondes, médiane de trois exécutions, le tout dans un seul processus. parser-bench.json. Les trois hachages de contenu correspondaient à la référence à toutes les tailles.

La surcharge du wrapper qui n’existe pas

Measured results chart: PyQuery and lxml on the same fixture

PyQuery s’est placé au niveau ou en dessous de lxml brut sur toutes les médianes affichées. Cela ne prouve pas qu’un wrapper accélère le parsing. Trois médianes d’exécution et aucune marge d’équivalence définie à l’avance suffisent seulement à conclure, plus prudemment, que sur ce fixture il n’y a pas eu de surcoût de sélection déterminant.

À 10 MB, les trois exécutions de PyQuery étaient de 162.86, 163.17 et 161.13 ms ; celles de lxml de 169.46, 165.25 et 164.18. Les intervalles sont proches, mais ne se recouvrent pas. À 1 MB, les deux médianes ne diffèrent que de 0,5 %. Ces petits écarts permettent un jugement pratique, pas une affirmation d’équivalence statistique.

System diagram: Wrapper and Parser Boundaries

Le mécanisme est assez simple : pq(html) construit une fois l’arbre lxml, d("h3.title") compile le sélecteur CSS via cssselect comme le fait tree.cssselect(), et les éléments renvoyés sont des éléments lxml. Il y a donc très peu de travail PyQuery dans ce chemin chaud mesuré. Parcours, manipulation, requêtes répétées, import et mémoire restent hors du champ de cette affirmation sur le temps de sélection.

Des résultats serrés à partir de 10 KB

Le résultat le plus utile est la première colonne.

À partir de 10 KB, l’écart entre le plus rapide et le plus lent parmi selectolax, lxml et PyQuery était de 4,5 % à 10 KB, 5,4 % à 100 KB, 0,5 % à 1 MB, et 4,5 % à 10 MB. Ce test n’était pas un test d’équivalence ; le jugement pratique est que ces écarts ne changeraient probablement pas la plupart des choix de parseur pour cette charge de travail.

selectolax est vraiment plus rapide à 1 KB — 0.0286 ms contre 0.0456 et 0.0508 — mais cette ligne n’est pas exploitable. Entre les trois parseurs, l’écart à cette taille est de 77,6 %, et les trois exécutions de selectolax allaient de 0.0267 à 0.0404 ms. À 28 microsecondes, le timer et l’ordonnanceur dominent. Je ne classerais rien à ce niveau.

Pour cette charge de travail centrée sur la sélection et la lecture, choisissez entre ces trois options selon l’API et les faits mesurés sur les dépendances, pas selon une hiérarchie supposée des performances. PyQuery n’a montré aucun surcoût déterminant face à lxml. selectolax utilise une autre pile de parsing, mais cet article n’a pas mesuré son empreinte installée, sa couverture de wheels ni ses contraintes de compilation sur la même base.

À titre de comparaison, le benchmark publié plaçait deux autres options Python sur le même fixture de 10 MB, et ce sont elles qui se distinguent vraiment :

Parseur (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

Chiffres publiés issus de bench_parse.json.

Les lignes historiques du benchmark placent BeautifulSoup à plus d’un ordre de grandeur au-dessus des médianes des parseurs les plus rapides sur ce fixture. Ces lignes n’ont pas été recalculées avec la paire PyQuery/lxml du processus courant ; elles servent donc de contexte, et non de multiplicateur contrôlé pour le verdict principal.

La ligne enregistrée pour cheerio était de 2 927,89 ms (2927.8857 dans parser-bench.json) avec des hachages de contenu extraits concordants. Ce résultat inter-runtime dépend aussi de Node, des versions de packages et des paramètres historiques d’exécution ; il ne doit pas être lu comme un multiplicateur isolé de vitesse de bibliothèque.

Réalité de l’installation

BibliothèquePackagesDisqueLicenceÉtoilesDernier push
PyQuery320.1 MiBBSD2,3802026-07-27
cheerio (Node)22 (npm)9.0 MiBMIT30,4492026-08-11

Référence officielle : PyQuery sur PyPI.

metadata-snapshot.json.

Trois packages, c’est une dépendance légère, et deux d’entre eux — lxml et cssselect — font déjà partie de beaucoup de projets Python de scraping. Dans ce cas, le coût marginal de PyQuery se limite à quelques dizaines de kilobytes.

Les 20,1 MiB correspondent aux extensions compilées d’lxml, pas à PyQuery. C’est le même coût d’environ 20 MiB que vous payez déjà si vous utilisez lxml directement.

La bibliothèque est sous licence BSD. À la date de l’instantané, elle comptait 59 tickets ouverts et un push trois semaines avant le test ; ces seules observations ne prouvent ni la qualité de la maintenance ni la compatibilité future.

Mémoire, et effet du HTML cassé

Deux éléments signalés comme non testés dans chaque revue de cette série sont désormais mesurés.

Le contexte plus large du stress test se trouve dans la comparaison mémoire et HTML invalide sur dix bibliothèques.

Mémoire résidente maximale, via /usr/bin/time -l, un processus neuf par cellule — le plancher d’import correspond au coût de la bibliothèque chargée et au repos, les pics incluent le document.

BibliothèqueRuntimePlancher d’importPic 226 KBPic 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. Les bases Python et Node ne sont pas comparables entre elles ; l’interpréteur fait partie des deux.

PyQuery est plus léger que resiliparse sur le gros document — 172.5 MiB contre 225.1 — malgré un plancher d’import plus élevé. L’arbre d’lxml est compact, et la majeure partie du plancher de 30,3 MiB de PyQuery vient du chargement d’lxml plutôt que d’un coût propre à PyQuery.

HTML cassé. Douze documents cassant chacun exactement une chose — balises non fermées, éléments inline mal imbriqués, attributs sans guillemets avec des espaces, fermetures parasites, absence totale de <html>, attributs dupliqués, document tronqué au milieu d’une balise, entités invalides, <script> non fermé, déclaration de charset mensongère, commentaire contenant du balisage, et 600 niveaux d’imbrication — plus deux contrôles bien formés à tailles correspondantes, parce que « il n’a rien renvoyé » ne dit quelque chose sur le caractère mal formé que si la bibliothèque reste muette aussi sur un document propre de même taille.

pyquery a levé une erreur dans 0 cas sur 14 et n’a rien renvoyé dans 1 cas, en récupérant 10/22 balises sentinelles sur les fixtures cassées (malformed-results.json). Une fixture est exclue de ce comptage : selon HTML5, tout ce qui suit un <script> non fermé est du contenu script, donc le perdre est correct et le récupérer serait l’écart. Sans les résultats sentinelles des alternatives directes à côté, 10/22 relève d’une observation de robustesse, pas d’un classement de parseur.

Avantages et inconvénients

En sa faveur. Syntaxe jQuery, familière à toute personne ayant écrit du JavaScript front-end ou utilisé cheerio. Aucun surcoût de sélection déterminant n’a été observé face à lxml brut sur ce fixture. Seulement 3 packages, dont 2 sont probablement déjà présents dans votre arbre de dépendances. Il renvoie des éléments lxml, donc les techniques lxml restent disponibles. BSD. Les hachages de contenu ont correspondu à la référence sur les cinq tailles.

Contre lui. 20,1 MiB, à cause d’lxml. 2 380 étoiles, c’est une communauté bien plus petite que celle de cheerio, qui en compte 30 449 — donc moins d’exemples concrets quand quelque chose d’étrange se produit. C’est une couche de confort, donc tout ce que lxml ne sait pas faire, elle ne le fera pas non plus. Et si vous espériez que l’API jQuery apporte de la performance, ce n’est pas le cas : elle apporte de l’ergonomie, et le moteur de parsing en dessous fait le travail.

Qui devrait l’utiliser, et qui ne devrait pas

Utilisez PyQuery si vous ou votre équipe préférez des sélecteurs de style jQuery en Python. La construction mesurée, les deux sélections et les lectures n’ont montré aucun surcoût déterminant face à lxml ; les autres opérations PyQuery n’ont pas été chronométrées.

Utilisez lxml directement si vous préférez XPath ou souhaitez une dépendance de moins. Cette série de mesures n’a pas montré de raison liée à la vitesse de sélection pour choisir entre les deux.

Évaluez selectolax si son API de parseur et sa pile de dépendances conviennent à votre projet. La ligne 1 KB est explicitement non classée, et cet article ne soutient pas une affirmation de « plus petite dépendance ».

Dans Node, cheerio propose une forme d’API analogue. Les lignes inter-runtime stockées étaient plus lentes ici, mais les différences de runtime et de protocole historique empêchent une conclusion propre limitée à la bibliothèque.

Où s’intègre une API managée

PyQuery analyse du HTML que vous possédez déjà. Il ne récupère pas les pages, n’exécute pas JavaScript et ne gère pas de couche anti-bot — aucun parseur de cette comparaison ne le fait, et sur beaucoup de cibles réelles, c’est pourtant la moitié la plus difficile.

Note de l’auteur : Thunderbit est notre option managée pour la récupération à partir d’une URL, le rendu et l’extraction. Elle n’a pas été comparée à PyQuery ici. La vraie frontière est de savoir si vous avez déjà le HTML et voulez des sélecteurs locaux, ou si vous voulez une acquisition de page et une extraction opérées comme un service.

Le cadrage honnête : si vous avez le HTML et connaissez vos sélecteurs, PyQuery est gratuit et agréable. Si les sélecteurs cessent de fonctionner, ou si vous récupérez à grande échelle, il s’agit d’un autre achat.

Pour aller plus loin, notre tour d’horizon des API de web scraping couvre les options hébergées, et le pilier des scrapers open source les solutions auto-hébergées. Si la sortie parsée alimente un modèle, convertir du HTML en Markdown en Python est l’endroit où la fidélité se perd.

Essayez Thunderbit pour l’extraction de données Web

Faut-il utiliser PyQuery ?

Oui, si vous voulez une syntaxe de style jQuery en Python et que le chemin mesuré sélection/lecture représente bien votre charge de travail.

Le benchmark n’a montré aucun surcoût de sélection déterminant face à lxml, tout en conservant la parité des hachages de contenu. Il n’a pas démontré un coût nul pour la bibliothèque dans son ensemble.

Sur cinq tailles, les médianes des trois parseurs Python sont restées suffisamment proches pour que l’adéquation à l’API domine probablement pour cette tâche. Définissez une marge d’équivalence et relancez les alternatives exactes avant de transformer ce constat en classement global des parseurs.

Essayez Thunderbit pour l’extraction de données Web Get Started Free

FAQ

PyQuery ralentit-il lxml ? Aucun surcoût de sélection déterminant n’a été observé dans cette série. Sur cinq tailles de page, ses médianes se sont situées au niveau ou en dessous de celles de lxml brut, les deux utilisant lxml 6.1.1 dans le même processus. À 10 MB, les plages proches ne se recouvraient pas : PyQuery 161.13–163.17 ms et lxml 164.18–169.46 ms. pq(html) construit un arbre lxml, et les sélecteurs testés passent par cssselect.

selectolax est-il plus rapide que PyQuery ? Sa médiane à 1 KB était plus basse, mais cette ligne n’est pas classée car la variation domine à l’échelle de la microseconde. À partir de 10 KB, les écarts de médiane allaient de 0,5 % à 5,4 %. C’est serré pour cette charge de travail, mais pas une preuve d’équivalence ni de chevauchement systématique des plages.

Pourquoi relancer lxml au lieu de citer le chiffre publié ? Parce que le benchmark publié indique la machine et la version de Python, mais pas les versions des bibliothèques. Sa ligne lxml a pu provenir d’une autre version d’lxml que celle qu’encapsule PyQuery aujourd’hui, et un écart de version aurait pu se lire comme une surcharge du wrapper qui n’existe pas. Exécuter les deux dans un seul processus avec lxml 6.1.1 enlève l’ambiguïté.

Comment cela se compare-t-il à cheerio ? Même idée d’API, écosystème différent. Les deux sélecteurs testés ici fonctionnaient dans les deux cas et les hachages de contenu concordaient sur les cinq tailles ; cela ne prouve pas une compatibilité complète des sélecteurs. Les temps stockés de cheerio étaient plus lents, mais les contrôles inter-runtime et historiques empêchent d’en faire un multiplicateur propre, limité à la bibliothèque.

Qu’est-ce qui n’a pas été testé ici ? La mémoire a été mesurée comme RSS maximal pour un simple import, un document de 226 KB et un document de 10 MB. Le HTML mal formé a été testé avec 12 documents cassés plus deux contrôles correspondants. Restent non testées la performance de manipulation et de parcours de PyQuery, le cache des requêtes répétées, la récupération d’URL, la concurrence et des charges représentatives de sites réels. Le temps à 1 KB reste non classé.

Ke
Ke
CTO chez Thunderbit | Data Scientist senior et expert en ML Forte d’une expérience de près de dix ans en machine learning et en data science, Ke Shen est diplômé de Columbia University et ancien Data Scientist senior chez Walmart Labs. Grâce à une expertise approfondie, reconnue par ses pairs, en Python, R, Java et statistiques, il partage des analyses éprouvées sur le passage d’algorithmes d’IA complexes de la théorie à une architecture prête pour la production.
Table des matières
Thunderbit · Agent IA de données web

Extrayez des données de n'importe quelle page en 1 clic

Approuvé par plus de 250 000 utilisateurs
formule gratuite disponible
De la page web au tableur
Décrivez ce dont vous avez besoin — l'agent IA de Thunderbit l'extrait et l'exporte vers Excel, Google Sheets, Airtable ou Notion. Gratuit pour commencer.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week