כל פוסט על "מנתח ה-HTML המהיר ביותר ב-Python" בסוף מגיע ל-selectolax, וכל אחד מהם מסיים את הסיפור ב"מהיר הרבה יותר מ-BeautifulSoup". וזה נכון. אבל מה שבדרך כלל חסר בדרך הוא מה קורה כשמשווים את selectolax דווקא מול lxml — כי שם, כבר צריך להוסיף כוכבית ליד המילים "המהיר ביותר".
אז בדקתי את זה כמו שצריך: selectolax (שני ה-backends שלו) מול lxml, BeautifulSoup על html.parser ועל lxml, וגם parsel, על פני חמישה גדלי עמודים מ-1 KB עד 10 MB, כשכל מדידה היא חציון של שלוש הרצות נפרדות בתהליכים נפרדים. selectolax ניצח את BeautifulSoup בפער גדול, והשווה ל-lxml הגולמי — ואז הפסיד בשלב הניתוח הטהור מול lxml. כל המספרים כאן זמניים ומגיעים ממכונה אחת בלבד (macOS arm64, Python 3.14.2); הסקריפטים שמורים בריפו, אז תריצו אותם אצלכם לפני שאתם מצטטים אותי.
מה selectolax באמת עושה — ומה לא
selectolax הוא binding של Python לשני מנועי C — Modest ו-Lexbor — שמנתחים HTML5 ומאפשרים שאילתות עם סלקטורים של CSS. הוא לא crawler, לא דפדפן, ולא "scraper" במובן של לחיצה על כפתור. זה כלי שאליו מעבירים גוש HTML אחרי שכבר שלפתם אותו מהרשת. התיאור של המתחזק עצמו הוא: "A fast HTML5 parser with CSS selectors, written in Cython, using Modest and Lexbor engines."
יש שני backends, וההבדל ביניהם חשוב הרבה יותר ממה שנדמה מקריאת התיעוד:
LexborHTMLParser(מנוע Lexbor) — זה המנוע שה-README ממליץ להשתמש בו נכון ל-2024.HTMLParser(מנוע Modest) — המנוע המקורי, שהספרייה הבסיסית שלו "אינה מתוחזקת יותר", לפי אותו README.
כמה עובדות שכדאי לדעת לפני שנכנסים למהירות. נכון ל-snapshot של הריפו שנלקח ב-2026-07-10, ל-selectolax יש 1,653 כוכבים והגרסה האחרונה היא v0.4.10 (מאי 2026), ו-PyPI מגדיר תמיכה ב-Python >=3.9,<3.15. ההתקנה היא החלק הכי פחות דרמטי בכל הביקורת הזו: pip install selectolax הוריד wheel מוכן מראש של 2.3 MB מסוג cp314 ועבד מיד על Python 3.14 — בלי הורדת דפדפן, בלי שלב doctor, בלי קומפילציה. זה היתרון השקט של מנתח טהור מול כלי שמבוסס על דפדפן. פשוט מייבאים ומתחילים לעבוד.
חשוב גם לציין כבר עכשיו נקודה משפטית: ה-binding של Python הוא MIT, אבל ה-wheel כולל בתוכו את המנועים המקומפלים, ולהם יש רישיונות משלהם — Modest הוא LGPL-2.1, ו-Lexbor הוא Apache-2.0. לכן לומר "selectolax הוא MIT" נכון לגבי קוד ה-Python, אבל לא שלם לגבי הבינארי שבאמת מפיצים. אם למחלקה המשפטית שלכם זה חשוב, זה הפרט שכדאי לסמן.
שאלת המהירות, עם מספרים אמיתיים
המשימה שמדדתי הייתה: לנתח מחרוזת HTML, לשלוף את הטקסט של כל <h3 class="title">, ולשלוף את ה-href של כל <a>. זמן ההשהיה נמדד במילישניות, כחציון של שלוש הרצות בתהליכים נפרדים; הפיזור בין ההרצות נשאר מתחת לכ-5% עבור המנתחים מבוססי ה-C ברוב הגדלים. לפני שמדדתי תא כלשהו, כל פלט של כל מנתח הומר ל-hash של התוכן כדי לזהות ולהוציא מכלל חשבון מנתח שהיה עושה פחות עבודה בשקט — ובדפים האלה כל השישה התאימו בכל גודל, כך שמדובר בהשוואה אמיתית של תפוחים מול תפוחים. כל הנתונים המלאים נמצאים ב-bench_parse.json שמור.

| Page | selectolax (Lexbor) | selectolax (Modest) | lxml | parsel | BS (lxml) | BS (html.parser) |
|---|---|---|---|---|---|---|
| 1 KB | 0.027 | 0.029 | 0.036 | 0.044 | 0.281 | 0.323 |
| 10 KB | 0.160 | 0.171 | 0.166 | 0.228 | 1.705 | 2.049 |
| 100 KB | 1.464 | 1.565 | 1.423 | 2.020 | 16.5 | 20.6 |
| 1 MB | 14.901 | 16.9 | 14.177 | 20.9 | 181.9 | 232.6 |
| 10 MB | 159.9 | 247.9 | 172.9 | 231.9 | 2261.6 | 2788.7 |
מול BeautifulSoup: בערך פי 12-17, והמיתוס אפילו ממעיט
אם ממירים את זה ליחסים, selectolax-Lexbor יוצא מהיר בערך פי 12 מ-BeautifulSoup(html.parser) בדף של 1 KB, ומגיע לפי 17 ב-10 MB, וגם פי 10-14 מהר יותר מ-BeautifulSoup(lxml) על פני אותו טווח. המספר שמסתובב ברשת — "selectolax מהיר בערך פי 4-5 מ-BeautifulSoup" — נמוך מדי מול html.parser, ונכון בערך רק מול BeautifulSoup שמבוסס על lxml. הכפולה האמיתית תלויה באיזה BeautifulSoup אתם מתכוונים אליו וכמה אתם מחלצים מכל עמוד.
זה גם מסתדר עם ה-benchmark של ה-README עצמו, שמרמז על יתרון של פי 25.5 מול BeautifulSoup(html.parser). אף אחד מהמספרים לא שגוי. המשימה ב-README (כותרת, קישורים, סקריפטים ומטא מדפים ביתיים קטנים) עושה פחות חילוץ בדפים קטנים, ולכן overhead של BeautifulSoup בכל ניתוח מקבל משקל גדול יותר. אם מגבילים לטווח הזה, המסקנה היא: selectolax מהיר בערך פי 10-15 מ-BeautifulSoup בעבודה ריאלית של ניתוח וחילוץ, ובפער גדול יותר בדפים זעירים או כשמחולצים מעט פרטים.
אם צוואר הבקבוק שלכם היום הוא קוד BeautifulSoup שמטפל בעמודים, זה המיגרציה שמשלמת על עצמה. המקרה הבא פחות חד-משמעי.
מול lxml: תיקו — ו-lxml מנצח בחלק שכולם שוכחים לבודד
תסתכלו שוב על השורות של 100 KB ו-1 MB. Lexbor ו-lxml נמצאים בתוך כ-5% זה מזה, טווחי המדידה שלהם חופפים, ולפי המתודולוגיה שלי זה תיקו — בלי מנצח, בלי "מהיר יותר". המקום היחיד שבו selectolax באמת מקדים הוא עמוד 10 MB (159.9 ms מול 172.9 ms, פער של 8.1% עם טווחים לא חופפים). לכן במשימה המלאה selectolax משתווה ל-lxml, ומנצח אותו רק על המסמכים הגדולים ביותר.

אחר כך בודדתי את בניית העץ מהשאילתות של CSS, והתמונה מתהפכת באופן שרוב הסקירות מפספסות. עבור ניתוח טהור, בלי שום שאילתה, lxml היה באופן עקבי מהיר בכ-33-34% מ-selectolax-Lexbor במכונה הזו — 77.9 ms מול 116.6 ms בעמוד של 10 MB. במשימה המלאה השניים מתקרבים בכל מקרה, וההשערה שלי (לא משהו שהוכחתי בניסוי ייחוס) היא שבדפים האלה שאילתת ה-CSS היא רק חלק קטן מהזמן הכולל, ולכן היתרון של lxml בשלב הניתוח נבלע עד שהתוצאות הכוללות מתכנסות.
זו הטענה הכי מותקפת בכל הביקורת הזו, ואני רוצה להיות גלוי לגבי הסיבה. היא סותרת את החוכמה המקובלת, וה-benchmark היחיד שמצאתי שמבודד ניתוח בלבד — aows.jpt.sh — מציג את ההפך, עם selectolax מהיר בערך פי 4. לכן סגרתי אותה בגבולות ברורים: התוצאה היא חד-פלטפורמית (macOS arm64, Python 3.14, wheels מוכנים מסוג cp314 — לא נבדק Linux x86_64 ולא build מהמקור), היא נבדקה מול ארבעה גדלי עמודים והחזיקה בכל אחד מהם, והיא נבדקה שוב עם שני APIs שונים של lxml כדי לשלול ארטיפקט של API. שני ה-API-ים של lxml ניצחו את selectolax-Lexbor בכל גודל. אני לא מציג את "lxml מנתח מהר יותר" כאמת סופית — אני מציג את זה כתוצאה שה-benchmark שלי הפיק, עם הסקריפט מצורף, מול רוב המספרים שפורסמו. תריצו אצלכם.
עוד חתיכה אחת: כששואלים 100,000 תגי <a> על עמוד שטוח, lxml ו-selectolax-Modest יוצאים בתיקו (33.30 ms מול 34.19 ms, עם טווחים חופפים), בעוד selectolax-Lexbor מפגר אחרי שניהם בכ-15%. מה שמשותף לכל שלושת מנועי ה-C הוא שהם מהירים פי 5-7 מ-parsel או BeautifulSoup בחירה המונית, בגלל המודל של אובייקט Python לכל node שהוא באמת צוואר הבקבוק. לכן הטענה "selectolax הוא המהיר ביותר בבחירת CSS בכמות גדולה" גם היא לא מחזיקה — Modest רק משתווה ל-lxml, ו-Lexbor מפסיד לו.
המסקנה שהייתי מוכן לעמוד מאחוריה: היתרון של selectolax מול lxml הוא לא מהירות רחבה במשימה המלאה. הוא מנצח רק בעמודים הגדולים ביותר. הערך שלו מגיע מדברים אחרים — נוחות API, התנהגות על קלט פגום, ו-CSS מודרני — ושם נמצא שאר ה-review הזה.
זיכרון והפעלה קרה: תדרגו לפי RSS, לא לפי הפרופיילר שלכם
זיכרון הוא המקום שבו הייתי צריך לתקן את המספרים הקודמים שלי, והתיקון הוא העיקר. כשמודדים את תוספת ה-RSS על עמוד של 10 MB עם tracemalloc כבוי, BeautifulSoup צורך בערך פי 1.5-1.8 יותר זיכרון מ-selectolax או lxml — הטווח נע מ-1.51x (BS-lxml ב-218.4 MB מול Lexbor ב-144.6 MB) ועד 1.75x בקצה העליון. selectolax ו-lxml יושבים באותו tier חסכוני; ל-lxml יש את ה-RSS הכי נמוך.

בגרסה קודמת כתבתי "~3x", והמספר הזה היה שגוי מסיבה חינוכית: המדידה נעשתה כש-tracemalloc פועל, וה-bookkeeping של tracemalloc לכל הקצאה מכפיל בערך את ה-RSS הנראה של המנתח שמקצה הכי הרבה. אז הנה קריאת אזהרה לכל מי שבודק זיכרון של מנתחים: תדרגו לפי RSS כשהפרופיילר כבוי. דירוג לפי peak של tracemalloc מסדר לא נכון במיוחד את המנתחים מבוססי ה-C — הוא גרם ל-selectolax-Lexbor להיראות כבד יותר מ-Modest, כשב-RSS אמיתי הם קרובים. BeautifulSoup באמת הכבד ביותר כאן; פשוט לא פי 3 כמו שנראה מהמדידה המזוהמת.
ההפעלה הקרה קטנה אבל אמיתית: selectolax נטען בכ-14 ms, בערך כמו lxml וכ-2.3x מהר יותר מ-bs4 או parsel. אם אתם משחררים כלי CLI או פונקציה serverless שבה זמן הייבוא הוא חלק מכל הרצה, הפער הזה שווה התייחסות.
כיסוי של סלקטורי CSS: חזק, אבל לא מושלם
כיסוי CSS נבדק במטריצה של 41 מקרים, כשכל סלקטור נבדק מול fixture עם סט תשובות נכון ידוע מראש, ובנוסף ריצה יזומה שנועדה לשבור את מנוע Lexbor. כל מקרה רץ בתהליך נפרד, וזה התברר כחיוני — אחד מהם מפיל את כל ה-interpreter. התוצאות:

| Engine | PASS | WRONG | UNSUPPORTED | PROCESS_ABORT |
|---|---|---|---|---|
| soupsieve | 41 | 0 | 0 | 0 |
| selectolax Lexbor | 39 | 0 | 2 | 0 |
| lxml (cssselect) | 37 | 1 | 3 | 0 |
| parsel (cssselect) | 37 | 1 | 3 | 0 |
| selectolax Modest | 35 | 3 | 2 | 1 |
כשמכניסים לתמונה סלקטורים עוינים, Lexbor הוא לא המנצח המוחלט — soupsieve הוא, עם 41/41 נקי מול 39/41 של Lexbor. שתי הנקודות החסרות של Lexbor הן :lang(en) ו-:dir(rtl), שהוא דוחה עם שגיאת parse. מלבד זאת הוא מושלם, כולל :has(), :is(), :where(), ותכונות case-insensitive.
איפה Lexbor כן בולט? מול ה-stack של cssselect. סלקטור הדגל של ה-README — div > :nth-child(2n+1):not(:has(a)) — מחזיר את הסט הנכון בשני מנועי selectolax וגם ב-soupsieve, אבל את הסט השגוי ב-lxml וב-parsel, בלי שום שגיאה. scraper שמעתיק את הסלקטור הזה ל-Scrapy או ל-parsel יקבל תוצאות שגויות בשקט. כדי לדייק במסגרת: cssselect תומך ב-:has() מאז גרסה 1.2.0 (2022), ובדקתי 1.4.0, כך שזה "נתמך אבל מחושב לא נכון בתוך קומבינציה" — לא "לא נתמך". ההתנהגות השקטה-שגויה של הקומבינציה הזו אינה מופיעה ב-tracker של cssselect, שמדווח על מגבלות :has() כשגיאות שמושלכות. Lexbor גם מטפל בדגל התכונה case-insensitive [data-role="LEAD" i] ש-cssselect דוחה לגמרי.
אבל יש שני חסמים שיקבעו מיגרציות. selectolax לא תומך ב-XPath בכלל — אף backend לא חושף xpath() — וגם לא ב-::text / ::attr(), כי אלה הרחבות של parsel/Scrapy ולא CSS אמיתי. אם הסקרייפרים הקיימים שלכם נשענים על XPath, זה הקיר הגדול ביותר שתיתקלו בו; תצטרכו לכתוב מחדש את הסלקטורים, לא רק להחליף ספרייה. מצד שני, Lexbor מגיע עם pseudo-class בשם :lexbor-contains("text" i) להתאמת טקסט בלי תלות באותיות גדולות/קטנות, דבר ש-lxml, parsel או CSS תקני לא מספקים, וזה עובד בדיוק כפי שתועד.
עמידות מול HTML מכוער, ושם selectolax באמת מרוויח את מקומו
סקרייפינג אמיתי אומר להאכיל מנתח בזבל ולקוות שלא יקרוס. הרצתי 18 קלטים עוינים, וזו הקטגוריה שבה הטיעון של selectolax מול lxml הוא החזק ביותר.
אם נותנים ל-lxml.html.fromstring מחרוזת ריקה או רווחים בלבד, הוא זורק ParserError("Document is empty"). שני המנועים של selectolax מחזירים במקום זאת עץ ריק ותקין. עבור scraper שרץ על רשימת URLs שבה חלק מהתגובות חוזרות ריקות, זה עוד try/except אחד שלא צריך לעטוף סביב הכל. selectolax גם עמד ב-100,000 אלמנטים בלי overflow של stack.
קינון עמוק יצר את הפער החד ביותר. ב-1,000 ו-5,000 רמות של <div> מקוננים, lxml שותק ומפיל את התוכן העמוק ביותר, בעוד selectolax שומר אותו. libxml2 מגביל עומק ניתוח בערך ל-256 רמות וחותך את העץ בלי שגיאה, כך שהטקסט העמוק ביותר פשוט נעלם מהישג יד. שני המנועים של selectolax מחזירים את העץ המלא. זה ההיפוך של מלכודת ה-<template> שאגיע אליה מיד: שם Lexbor מפיל תוכן שהאחרים שומרים; כאן lxml מפיל תוכן ש-selectolax שומר.
לא כל תא היה ניצחון. backend של Modest מפיל את כל ה-Python interpreter עם SIGABRT כשהוא נתקל ב-:dir() — לא חריג שאפשר לתפוס, אלא הרג קשוח של התהליך. זו אזהרת עמידות אמיתית לכל מי שעדיין נשען על ה-backend הוותיק, וזה בדיוק מסוג הדברים שנשארים בלתי נראים עד שזה מפיל Job פרודקשן ב-3 לפנות בוקר.
שתי מלכודות של אובדן נתונים בשקט שכדאי להכיר לפני ששולחים לפרודקשן
אלה לא תגליות חדשות — שתיהן מתועדות upstream — אבל שתיהן עולות בנתונים אמיתיים, בשקט, ושום דבר מהן לא צועק ב-README.
Lexbor מפספס <a> בתוך <template>
בדף ה-MDN החי שבדקתי, selectolax-Lexbor מצא 497 קישורים, בעוד lxml, שני ה-backends של BeautifulSoup, ואפילו backend ה-Modest של selectolax עצמו מצאו 508. אחד-עשר הקישורים החסרים היו מחליף שפה וקישור לדיונים, שנמצאו בתוך אלמנטים של <template> (העמוד משתמש ב-Lit web components).

הסיבה אמיתית לגמרי: לפי תקן HTML5, תוכן של <template> מנותח לתוך fragment נפרד ובלתי-אקטיבי, לא לתוך ה-DOM הרגיל, ו-Lexbor מקפיד על זה — tree.css("a") לא נכנס לתוך תוכן template. lxml, שני ה-backends של BeautifulSoup, ו-Modest משטיחים את תוכן ה-template לתוך העץ הראשי, ולכן הם כן מוצאים את הקישורים האלה. זו בעיה פתוחה ומתועדת (selectolax#146, עם שורש הבעיה במנוע ב-lexbor#170), וניתן להגן על שתי הגישות — Lexbor הוא כנראה היותר נכון לפי התקן. אבל מפתח שמשתמש ב-backend המומלץ מפספס את הנתונים האלה בשקט, בלי שום שגיאה. והכיוון ההפוך חשוב לא פחות: המנתחים האחרים חושפים תוכן template אינרטי שדפדפן לא באמת מציג, כך שהם עלולים לתת לכם נתונים פנטומיים שמשתמש לא יכול לראות. פתרון המילוט הבטוח הוא backend של Modest, או ספרייה אחרת, עבור אותו עמוד ספציפי.
בייטים שאינם UTF-8 משבשים את .text() בלי רעש
אם מעבירים ל-selectolax בייטים שאינם UTF-8 תקינים, הניתוח מצליח — אבל השיבוש צץ מאוחר יותר, וזה גרוע יותר מקריסה נקייה. על "<p>café éè</p>".encode("latin-1"), .text() של Lexbor מחזיר תווי החלפה, .text() של Modest פשוט משליך את הבייטים הבעייתיים, ורק כשנוגעים ב-.html שני המנועים זורקים UnicodeDecodeError. ה-binding מפענח כ-UTF-8 strict רק בעת הקריאה חזרה, לא בזמן הניתוח. זה קשור ל-issue מוכר ב-selectolax סביב קשיחות של encode/decode.
התיקון הוא שורה אחת, וכדאי לזכור אותה בעל פה: לפענח את הבייטים בעצמכם קודם — LexborHTMLParser(resp.content.decode("latin-1")) — ואז שני המנועים מחזירים 'café éè' בצורה תקינה. בפועל, תמיד תעבירו ל-selectolax מחרוזת str, לא bytes גולמיים שאינם UTF-8. ה-README לא מבהיר את זה.
ממדי פרודקשן (מדידה אחת בלבד, אז להתייחס ככיוון)
את המדידות הבאות ביצעתי פעם אחת בלבד, לא בשלוש הרצות, אז אני מסמן אותן כאותות ולא כמספרים סופיים.
סקיילינג עם threads הוא המעניין ביותר. כשחילקתי בדיקת ניתוח של עמוד 1 MB ל-48 הרצות על פני ארבעה threads, selectolax הראה האצה של כ-3.5-3.9x בזמן קיר — סימן אמפירי לספרייה שמשחררת את ה-GIL בזמן ניתוח C — בעוד BeautifulSoup(lxml) דווקא נעשה איטי יותר בכמה מונים ב-threaded mode, סימן לעבודה שמסתדרת בסדר טורי בגלל ה-GIL. lxml נחת באמצע והתוצאה לא הייתה חד-משמעית. לעידן ה-free-threading ש-Python מתקדם אליו, ניתוח ב-selectolax שמקביל בין threads כש-BeautifulSoup לא עושה זאת הוא יתרון אמיתי, גם אם זמני. זו ספירת threads אחת על גודל עמוד אחד, והמנגנון הוא השערה, לא משהו שאימתתי באמצעות instrumenting לקוד ה-C.
על leaks: לאורך 2,000 איטרציות של parse-extract-drop על 1 MB, אף אחד משלושת המנתחים לא הציג עלייה ליניארית ב-RSS שמאפיינת leak — כל אחד התייצב בתוך טווח עבודה תחום. אני סומך על התוצאה הזו במיוחד כי הרצתי דרך אותו כלי גם נבדק-דליפה ידוע, והוא עלה ל-+198 MB כמתוכנן, מה שמוכיח שהכלי מסוגל לראות leak ופשוט לא מצא כזה במנתחים. וגם ידית node שנשמרה בחיים אחרי שהעץ שלה יצא מתחום ה-scope נשארה שימושית, בלי segfault. שוב, מדידה אחת בלבד, לא soak של שעות.
איפה selectolax מתאים — ואיפה הוא רק מעביר את השרביט
כל מה שתואר כאן עוסק במשימה אחת: להפוך HTML שכבר נמצא אצלכם לנתונים מובנים, מהר. בזה selectolax מצטיין. מה שהוא בכוונה לא עושה זה להביא את העמוד, לרנדר JavaScript, לסובב פרוקסיז, לפתור CAPTCHAs, או להחליט אילו אלמנטים אתם בכלל רוצים. כל זה עדיין הקוד שלכם. selectolax הוא שכבת הניתוח, והוא לא מנסה להיות יותר מזה.
כאן נכנס לתמונה שירות חילוץ מנוהל שיושב מעל מנתח במקום להחליף אותו. אם אתם מעדיפים לא לבנות ולתחזק בעצמכם את כל ה-stack של fetch-render-anti-bot-extract, Thunderbit מציע את זה כ-API, כ-MCP server וכ-CLI — POST /distill ממיר עמוד ל-Markdown נקי, ו-POST /extract מחזיר JSON מובנה שתואם לסכימה, כש-rendering ל-JS והגנות anti-bot מטופלים בשבילכם. זו שכבה אחרת של הבעיה: תשתמשו ב-selectolax כשכבר יש לכם HTML ואתם רוצים מהירות ניתוח גולמית בשליטה שלכם, ובמשהו כמו ה-API, ה-MCP server או ה-CLI של Thunderbit כשאתם רוצים שהמשיכה מהרשת והחילוץ יטופלו עבורכם ואתם רק רוצים לקבל נתונים מובנים בחזרה. לא החלפה — גובה אחר באותו stack.
נסו את Thunderbit לחילוץ נתוני רשת
יתרונות, חסרונות, ומי באמת צריך להשתמש בזה
איפה selectolax מנצח:
- מהיר בערך פי 12-17 מ-BeautifulSoup בעבודה ריאלית של parse-and-extract, ויציב על פני שלושה סדרי גודל של גודל עמוד.
- צריכת זיכרון נמוכה (אותו tier כמו lxml, בערך פי 1.5-1.8 פחות מ-BeautifulSoup) וייבוא של כ-14 ms.
- סלחני לקלט שמפיל את lxml — ריק, רווחים, וקינון עמוק בצורה פתולוגית.
- CSS מודרני כולל
:has(),:is(),:where(), תכונות case-insensitive, ו-:lexbor-contains()הייחודי ל-Lexbor. - DOM עם קריאות וכתיבה בטוחות ל-None: אלמנטים חסרים מחזירים
Noneאו[]במקום לזרוק, ואפשר גם לשנות ולסריאליז את העץ. - תחזוקה פעילה (v0.4.10, אמצע 2026) והתקנה פשוטה לחלוטין.
איפה הוא לא מנצח:
- לא מהיר באופן גורף מ-lxml — תיקו במשימה המלאה, ובניתוח הטהור במכונה שלי lxml אפילו מוביל.
- אין XPath ואין
::text/::attr()— קיר מיגרציה אמיתי למי שמבוסס על XPath. - שתי מלכודות של אובדן נתונים בשקט: תוכן
<template>ב-Lexbor, ובייטים שאינם UTF-8 דרך.text(). - backend של Modest הוא מורשת ויקרוס ב-SIGABRT על
:dir(). - כל המספרים כאן הם חד-פלטפורמיים (macOS arm64, Python 3.14) וזמניים.
האם כדאי להשתמש ב-selectolax? כן, אם אתם רוצים מהירות ניתוח ברמה של lxml עם API ידידותי יותר ובטוח יותר ל-None, והתנהגות טובה יותר משמעותית על קלט ריק או פגום — ואתם מוכנים לחיות בעולם של CSS בלבד. אם הקוד שלכם בנוי על XPath, עלות הכתיבה מחדש אמיתית, וצריך לשקול אותה בכנות. ואם אתם רודפים אחרי "המנתח היחיד הכי מהיר", התשובה המדויקת מה-benchmark הזה היא ש-selectolax ו-lxml קרובים מספיק, כך שמי שמכריע הוא נוחות ועמידות, לא מהירות גולמית. וזה ממילא טיעון טוב יותר לבחירת כלי.
נסו את Thunderbit לחילוץ נתוני רשת Get Started Free
שאלות נפוצות
האם selectolax מהיר מ-BeautifulSoup?
כן, באופן ברור — בערך פי 12-17 מהר יותר מ-BeautifulSoup(html.parser) ופי 10-14 מהר יותר מ-BeautifulSoup(lxml) במשימת parse-and-extract ריאלית, עם עקביות בין עמודים של 1 KB עד 10 MB (macOS arm64, Python 3.14). ה"פי 4-5" שמצטטים בדרך כלל ממעיט בפער מול html.parser.
האם selectolax מהיר מ-lxml? לא באופן כללי. במשימת parse-and-extract מלאה הם בתיקו ב-100 KB וב-1 MB, ו-selectolax מנצח רק בעמוד של 10 MB. בניתוח טהור בלי שאילתה, lxml היה בפועל מהיר בכ-33-34% במכונה שלי — תוצאה שסותרת את הקונצנזוס, ולכן סימנתי אותה כחד-פלטפורמית. כדאי לבדוק אצלכם.
האם להשתמש ב-Lexbor או ב-Modest?
Lexbor, כמעט בכל מצב — זה המנוע המתוחזק והמלא שה-README ממליץ עליו, עם כיסוי CSS טוב יותר. החריג הוא עמוד שמסתיר תוכן בתוך <template>, שם ההתנהגות התקנית של Lexbor מפספסת את התוכן הזה, בעוד Modest במקרה שומר אותו. ל-Modest יש גם קצוות חדים, כולל קריסה קשוחה של ה-interpreter על :dir().
האם selectolax תומך ב-XPath?
לא. אף backend לא חושף מתודה xpath() — selectolax הוא CSS בלבד. אם הסקרייפרים שלכם תלויים ב-XPath, המעבר כולל כתיבה מחדש של הסלקטורים, וזה המחיר הגדול ביותר במעבר מ-stack מבוסס lxml או parsel.
למה הפלט של selectolax שלי משובש או חסרים בו אלמנטים?
שני חשודים עיקריים. אם הטקסט חוזר עם תווי החלפה או בלי סימנים דיאקריטיים, כנראה העברתם בייטים גולמיים שאינם UTF-8 — פענחו אותם קודם ל-str (resp.content.decode("latin-1")) לפני הניתוח. אם קישורים או אלמנטים חסרים באתר מודרני, ייתכן שהם חיים בתוך תגיות <template> ש-backend של Lexbor לא נכנס אליהן; עברו ל-Modest או למנתח אחר עבור אותו עמוד.


