Keywords
סקירת apache nutch, web scraping, קוד פתוח, benchmark
Content
Apache Nutch הוא סורק של Apache Software Foundation שהפיתוח שלו התחיל ב-2004. זהו סט JVM שבנוי על Hadoop, ומאורגן כלולאה ולא כפקודת streaming אחת: כתובות Seed נכנסות למסד נתונים קבוע, ואז רצים סבבים של generate → fetch → parse → updatedb, עם חריצי תוספים לפרוטוקול, למפענח, למסנן URL ולניקוד. הפלט הרגיל נשלח לאינדקס חיפוש כמו Solr או Elasticsearch, ולא לקובץ CSV.
הרצתי את Nutch 1.22 מול אתר בדיקה מקומי ומבוקר — כזה שמתעד כל בקשה בצד השרת, כך שהתוצאות נבחנות לפי מה שהשרת באמת ראה ולא לפי מה שהסורק טען. ארבעה גבולות עיצבו את ההרצה: גרסת ה-JDK, http.agent.name, תחום הסריקה, והאם parse-js מופיע ב-plugin.includes. המחזור המלא רץ שוב ושוב עם התצורה שנבדקה; שינוי ה-JDK או השארת זהות ה-agent לא מוגדרת גורמים לעצירה עוד לפני שליפת עמודים שימושיים.
גבול ה-JDK מופיע לפני שמתחילה בכלל סריקה. Nutch 1.22 לא עלה כאן על JDK 26.0.1: משימת Hadoop הראשונה קרסה בתוך Subject.getSubject() אחרי ש-Java הסירה את נתיב ה-SecurityManager. Nutch מגיע עם Hadoop 3.4.2, בעוד שהתיקון נחת ב-Hadoop 3.4.3 שבעה ימים אחרי יציאת Nutch 1.22. בנפרד, parse-js שינה את שחזורן של שתי ליטרליות בקובצי JavaScript מ-0/2 ל-2/2 בלי להריץ דפדפן.
למה נועד Nutch, ולמה לא
Nutch הוא לא כלי scraping במובן המקובל. חילוץ מובנה של שדות הוא לא התפקיד שלו: הוא מגלה ומושך כתובות בהיקף גדול, מנהל מסד נתונים קבוע של הכתובות והמצבים שלהן (ה-crawldb), ומוסר לכם segments שמערכת אחרת הופכת לאינדקס. אם תכוונו אותו לקטלוג ותצפו לטבלה של שמות ומחירים, תקבלו במקום זאת crawldb.
הארכיטקטורה הזו מסבירה כמעט את כל מה שמגיע אחר כך. Nutch קדם לעידן הסורקים ה״חד-בינריים״ בכשני עשורים, והוא בנוי לבעיה ש-Hadoop נבנה עבורה: סריקת יותר עמודים ממה שמתאים למכונה אחת. להריץ אותו על מחשב נייד מול fixture של 12 עמודים זה כמו לשכור רכבת משא כדי להעביר מדף ספרים — מלמד על הרכבת, אבל לא הוגן לצפות ממנו לארגונומיה של אופניים.
הגרסה הנוכחית היא 1.22, שהוכרזה ב-17 בפברואר 2026. זהו פרויקט Apache-2.0, המאגר עמד על 3,272 כוכבים עם 8 issues פתוחים כשבדקתי ב-27 ביולי 2026, ו-master עודכן ארבעה ימים לפני כן. זהו פרויקט מתוחזק, לא פרויקט נטוש — וזה בדיוק הזווית הנכונה להבין דרכה את בעיית ה-JDK: חלון בנייה שנסגר שבוע מוקדם מדי, לא הזנחה.
מטריצת הגרסאות: JDK 24+, Hadoop 3.4.2, ותיקון בן שתי שורות
החסם הוא אינטראקציה בין שלוש גרסאות, והחלק היחיד שבו יש לכם שליטה הוא באיזה JDK Nutch ירוץ. משימת Hadoop הראשונה, ב-JDK ברירת המחדל של המארח, קרסה כבר באתחול:
java.lang.UnsupportedOperationException: getSubject is not supported
at java.base/javax.security.auth.Subject.getSubject(Subject.java:277)
at org.apache.hadoop.security.UserGroupInformation.getCurrentUser(UserGroupInformation.java:588)
at org.apache.nutch.crawl.Injector.inject(Injector.java:473)
קוד חזרה 255. אפס עמודים נשלפו. bin/nutch inject אפילו לא מגיע לרשת — הוא מאתחל LocalJobRunner של Hadoop, ששואל מי המשתמש הנוכחי, מה שמוביל ל-Subject.getSubject(), ש-JEP 486 הפך לחריגה מחייבת כש-JDK 24 הסיר לצמיתות את ה-SecurityManager. ה-JDK במכונה שלי היה OpenJDK 26.0.1, הרבה מעבר לקו הזה.
גם נתיב המילוט המסורתי לא עובד. הוספת -Djava.security.manager=allow, הדגל שפעם החזיר את ההתנהגות הישנה, נדחית על-ידי ה-VM עוד לפני שטוענים את קוד Nutch:
Error occurred during initialization of VM
java.lang.Error: A command line option has attempted to allow or enable the Security
Manager. Enabling a Security Manager is not supported.
זה קוד חזרה 1, וקיר ללא דלת — הדגל הוסר יחד עם התכונה.
שורש הבעיה נמצא בגרסת Hadoop שמצורפת ל-Nutch 1.22. בעיית getSubject מתועדת כ-HADOOP-19212 ומתוקנת ב-Hadoop 3.4.3 וב-3.5.0; Nutch 1.22 מגיע עם hadoop-common-3.4.2. Nutch 1.22 יצא ב-17 בפברואר 2026, ו-Hadoop 3.4.3 הגיע בערך שבוע אחר כך.
וזה גם לא בעיית תלות ב-Solr או ב-cluster של Hadoop. יש הנחה נפוצה ש-Nutch חייב cluster של Hadoop ו-Solr פעיל כדי לעשות משהו. הוא לא. מצב Local מריץ את LocalJobRunner של Hadoop בתוך התהליך — בלי דמון HDFS, בלי YARN, בלי cluster. כל מחזור ה-inject → generate → fetch → parse → updatedb רץ על מכונה אחת בלי שום דבר נוסף מותקן. חומת ה-JDK היא לגמרי עניין של גרסת ספרייה מצורפת, והיא עוצרת אתכם עוד לפני שהשאלה על התשתית בכלל עולה.
מטריצת הגרסאות הפרקטית, שלוש השורות נמדדו:
| JDK בשימוש | פקודה | תוצאה |
|---|---|---|
| OpenJDK 26.0.1 | bin/nutch inject | נכשל, rc=255 — UnsupportedOperationException: getSubject is not supported |
| OpenJDK 26.0.1 | bin/nutch inject + -Djava.security.manager=allow | נכשל, rc=1 — ה-VM מסרב לעלות |
| OpenJDK 17.0.20 (LTS) | bin/nutch inject | עובד, rc=0 — Total new urls injected: 1 |
התיקון הוא שתי פקודות. מתקינים JDK מסוג LTS ומכוונים את Nutch אליו:
brew install openjdk@17
export NUTCH_JAVA_HOME=/opt/homebrew/opt/openjdk@17
זהו קג-אונלי, כך שזה לא נוגע בברירת המחדל של המערכת. ה-CI של Nutch עצמו מכוון ל-Java 17, והפרויקט הודיע בפומבי ש-1.22 היא הגרסה האחרונה שתרוץ על Java 11 וש-1.23 תדרוש Java 17. לכן JDK מסוג LTS הוא לא workaround — זו התצורה הנתמכת. הפער הוא בין מה ש-Nutch תומך בו לבין מה ש-brew install openjdk נותן לכם ב-2026, ושתי אלו הן שאלות שונות שפשוט מתנגשות כבר בפקודה הראשונה.
מכאן והלאה הכול רץ על OpenJDK 17.0.20, ושם המחזור כולו נקי.
ההתקנה, במדידה: 396 MB ומאפיין אחד שחוסם הכול
"כבד" היא המילה שכולם שולפים, אבל היא חסרת ערך בלי מדידה. הנה מה שמכילה בפועל ההפצה הבינרית של Nutch 1.22 לאחר פריסה:
| פריט | הפצה בינרית של Nutch 1.22 |
|---|---|
| גודל לאחר פריסה | כ-396 MB |
קבצי JAR ב-lib/ | 188 (כ-113 MB) |
| — מתוכם מחסנית Hadoop המצורפת | 13 |
| תיקיות תוספים | 78 |
| קבצי JAR בתוך תיקיות התוספים | 533 |
| קובצי תצורה | 35 |
סקריפטים ב-bin/ | 2 — crawl ו-nutch |
לשם השוואה, סורק Go מודרני כמו katana מגיע כקובץ בינרי אחד של כ-50 MB, בלי JVM ובלי JAR-ים חיצוניים.
ואז יש את השער שאף אחד לא מזהיר אתכם מפניו. קובץ ה-nutch-site.xml שמגיע עם המערכת ריק, ו-http.agent.name ברירת המחדל שלו היא מחרוזת ריקה. כשהשארתי אותו לא מוגדר, הסריקה הראשונה שלפה אפס נתיבים ורשמה:
ERROR Fetcher: No agents listed in 'http.agent.name' property.
הגדרה של אותו מאפיין אחד — בלי שום דבר אחר — הפכה את זה לשליפה תקינה. כשהמאפיין היה ריק, הפקודה הסתיימה בלי למשוך עמודים והלוג דיווח על שגיאת agent-name שלמעלה; זה לא היה שקט.
התצורה המינימלית שעובדת הסתכמה בשלושה פריטים: conf/nutch-site.xml (שם ה-agent, סט התוספים, היקף), conf/regex-urlfilter.txt (הגדרת תחום לפי host), וקובץ כתובות Seed. זה לא הרבה. פשוט שלושה קבצים יותר מ-crawler run <url>.
מה הוא מצא: המתג של התוסף שקובע

באתר הבדיקה היו שלוש מחלקות שונות בכוונה של endpoints, וההתנהגות של Nutch התחלקה ביניהן בצורה חדה:
- מחלקה A — קישורי HTML רגילים (4 עמודים, ועוד שרשרת בעומק 3 קישורים)
- מחלקה B — endpoints שקיימים רק כליטרליות מחרוזת בתוך קובץ JavaScript מקושר: אחת כארגומנט לקריאה,
fetch('/api/js-endpoint-7'), ואחת כהשמה,const other = "/api/js-endpoint-8" - מחלקה C — endpoint שקיים רק אחרי ש-JavaScript רץ ומזריק אותו ל-DOM
התוצאות, לפי לוגים של פניות מצד השרת, חזרו שלוש פעמים:
| תצורת תוספים | מחלקה A (קישורי HTML) | מחלקה B (ליטרליות בקובץ JS) | מחלקה C (DOM בזמן ריצה) |
|---|---|---|---|
ברירת מחדל שמגיעה עם המערכת — parse-(html|tika) | 4/4 (recall 1.0) | 0/2 (recall 0.0) | לא הושג |
עם parse-js — parse-(html|tika|js) | 4/4 (recall 1.0) | 2/2 (recall 1.0) | לא הושג |
זהה בכל שלוש החזרות. דטרמיניסטי.
הקפיצה של מחלקה B היא החלק שממעיטים בחשיבותו. Nutch מצא את שני ה-endpoints המוטמעים ב-JavaScript בלי להריץ דפדפן, באמצעות סריקת regex של תוכן JavaScript שמבצע תוסף parse-js. הקובץ app.js עצמו נשלף בשתי התצורות — Nutch מתייחס ל-<script src> כאל outlink בלי קשר — ולכן כל ההבדל הוא האם מישהו קורא את התוכן של הקובץ ומחפש מחרוזות שנראות כמו URL. מפעילים את התוסף, והוא תופס את שתי הצורות הליטרליות.
בפיילוט הזה, ברירת המחדל של Nutch ו-מצב standard של katana הגיעו לאותה קבוצת מחלקה A, בעוד ש-Nutch עם parse-js ו-katana עם -jc הגיעו למחלקות A ו-B בלי דפדפן. גרסת katana והפקודה המלאה לא מתועדות במאמר הזה, כך שזהו הקשר ולא benchmark קשיח בין מוצרים.
מחלקה C היא התקרה הכנה. אף תצורת תוספים סטטית לא הגיעה אליה, וזה צפוי: שחזור endpoint שקיים רק אחרי הרצת סקריפט דורש באמת להריץ את הסקריפט. ניסיתי גם להחליף את protocol-http ב-protocol-htmlunit, הפרוטוקול של Nutch שמבצע JavaScript ב-Java טהור. הוא נטען ורץ בלי לקרוס, אבל באותו harness של ארבעה סבבים הוא השלים רק סבב אחד, שלף רק את עמוד ה-Seed ואת app.js, לא הגיע לאף אחת מהמחלקות A/B/C, ובסבב השני דווח על 0 records selected for fetching. זו בדיקה לא מכוונת מספיק, לא פסק דין על היכולת של HtmlUnit. מה שזה כן מראה בצורה צרה יותר: החלפת פרוטוקול שמריץ JS אינה שינוי drop-in, ומחלקה C נשארה בלתי מושגת בכל תצורה שבדקתי.
בקרת סריקה והתנהגות כשיש כשל
עומק הוא לא דגל. אין ב-Nutch --depth 3; העומק הוא פשוט כמה סבבים של generate → fetch → parse → updatedb אתם מריצים, כי סבב R מושך את החזית שהתגלתה בסבב R-1. שרשרת העומק שלי אישרה זאת בדיוק:
| מספר הסבבים | הנתיב העמוק ביותר שהושג |
|---|---|
| 2 | /depth/1 |
| 3 | /depth/2 |
| 4 | /depth/3 |
נקי ומכני, אבל זה אומר שעומק הוא ספירת לולאה בסקריפט שלכם, לא פרמטר.
ועכשיו המלכודת. ברירת המחדל שמגיעה עם Nutch היא db.ignore.external.links=false, יחד עם מסנן URL סלחני +. — כלומר סריקה רגילה של Nutch תעקוב גם אחרי קישורים מחוץ ל-host של ה-Seed. הזנתי דף שקישר לנתיב אחד בתוך התחום וקישור נוסף ל-host אחר, והסריקה משכה גם את ה-host הזר. שני אותות בלתי תלויים הסכימו: ה-crawldb של Nutch סימן אותו כ-db_fetched, ומונה הבקשות של השרת האחר רשם את הפגיעה.
שמירה על היקף היא בחירה מפורשת, ושתי הדרכים הבאות עובדות ומאומתות:
| תצורה | host חיצוני ב-crawldb | פגיעה בשרת של host חיצוני | נשמר בתחום? |
|---|---|---|---|
db.ignore.external.links=false (ברירת מחדל) | db_fetched | +1 | לא |
db.ignore.external.links=true | לא מופיע | 0 | כן |
כלל host ב-regex-urlfilter.txt (+^http://127.0.0.1: ואז -.) | לא מופיע | 0 | כן |
אם אתם סורקים אתר אחד, הגדירו אחת מהאפשרויות האלה לפני הריצה האמיתית הראשונה. הסתייגות מתודולוגית אחת: הבדיקה הספציפית הזו רגישה לעומס על השרת המקומי, כך שהשורות האלה מגיעות מהרצה שבה שום דבר אחר לא נגע ב-fixture. ההתנהגות עצמה ברורה מכנית ומגובה בשני אותות בלתי תלויים; הערכים הספציפיים בטבלה הם ריצה נקייה אחת, לא ממוצע של רבות.
Sitemaps הם שלב נפרד. יש התחשבות בעקרון נימוס — סריקה רגילה אכן שלפה את /robots.txt — אבל את ה-sitemap עצמו צריך לבקש בפקודה ייעודית:
| גישה | האם /sitemap.xml התבקש? | endpoints שהיו קיימים רק ב-sitemap |
|---|---|---|
| סריקה רגילה | לא התבקש מעולם | 0/2 |
bin/nutch sitemap, מורץ במפורש מול ה-crawldb | נשלף | 2/2 רשומות הוזרקו, recall מלא |
מצב -kf של katana ל-known files, על אותו fixture, ב-host מבוסס IP | לא נרשם | 0/2 |
זה מודל אחר מסורקים שמושכים known files inline, וזה עולה לכם בפקודה נוספת — אבל הוא עושה את העבודה במלואה.
שתי התנהגויות קטנות יותר עמדו יפה במבחן.
טיפול בשגיאות: סריקה של דף שקישר ל-500 ול-404 הסתיימה בכל הסבבים בצורה נקייה, עדיין שלפה את כל ארבעת עמודי מחלקה A, ורשמה כל כשל בנפרד:
| תגובת כשל שאליה קישר הדף | מצב שנרשם ב-crawldb |
|---|---|
| 500 | db_unfetched (זמין לניסיון חוזר) |
| 404 | db_gone |
שום דבר לא נתקע.
נימוס: עם thread אחד לכל queue, הפער בין שליפות מאותו host הלך לפי ההגדרה:
fetcher.server.delay | פער חציוני בין שליפות מאותו host |
|---|---|
| 1.0 שנייה | 1.009 שנ׳ (מינימום 1.006 שנ׳) |
| 0.0 | 0.002 שנ׳ |
הכפתור עושה בדיוק מה שכתוב עליו. ברירת המחדל שמגיעה עם המערכת היא 5.0 שניות, וזה שמרני, ושוב — כנראה נכון לכלי שנועד לסרוק שרתים של אחרים.
מס ה-batch, בשניות
כל פקודת Nutch היא JVM טרי. העובדה האחת הזו שולטת בפרופיל התזמון הרבה יותר מכל דבר שקשור לשליפה עצמה.
| שלב (לכל סבב) | שניות חציוניות |
|---|---|
inject (פעם אחת) | 1.81 |
generate | 3.93 |
fetch | 2.82 |
parse | 1.78 |
updatedb | 1.81 |
| סבב מלא אחד | 12.14 |
הרצפה האפקטיבית לכל job — עליית JVM ואתחול Hadoop, שנמדדו כשלב הזול ביותר עם עבודה טריוויאלית — היא בערך 1.77 שניות. מכפילים את זה בארבע פקודות לכל סבב, מוסיפים את ה-inject ההתחלתי, והתמונה הכוללת נראית כך:
| כלי | סריקת עומק 4 של ה-fixture בן 12 העמודים | תהליכים |
|---|---|---|
| Nutch | בערך 45 שניות (מדדתי 45.8 שנ׳ ו-45.0 שנ׳ בשתי תצורות) | סביב 17 עליות JVM, שכמעט אף אחת מהן לא עושה עבודה ברשת |
katana במצב standard, אותו fixture | בערך 13 שניות | תהליך אחד |
הפער הזה לא נובע ממהירות שליפה; שני הכלים מבקשים את אותה חופן עמודים. הוא ארכיטקטוני. Nutch משלם עלות קבועה של תהליך לכל שלב כי השלבים הללו מתוכננים כמשימות MapReduce. בסריקה מקומית קטנה, ההתקנה שולטת. העלות הקבועה אמורה להפוך לחלק קטן יותר בריצה ארוכה יותר, אבל הבדיקה הזו לא מדדה את קנה המידה שבו Nutch ו-katana מצטלבים, או אם היחס ביניהם מתהפך.
יתרונות וחסרונות
יתרונות
- גילוי סטטי דטרמיניסטי: מחלקת HTML 4/4, שרשרת עומק 3/3, זהה בכל שלוש הריצות החוזרות.
parse-jsמשחזר endpoints מליטרליות בקובץ JavaScript (2/2) בלי דפדפן, ותופס גם צורת ארגומנט לקריאה וגם צורת השמה.- שתי בקרות תחום מאומתות שמכילות סריקה במלואה (
db.ignore.external.linksו-host rule ב-regex-urlfilter). - קליטת Sitemap באמצעות
bin/nutch sitemapהשיגה recall מלא של 2/2 עבור endpoints שסריקה רגילה פספסה לחלוטין. - עמידות לכשלים: גם 500 וגם 404 טופלו עם מצבי crawldb נפרדים, והסריקה המשיכה.
- בריצה המקומית הזו, המרווח שנצפה בין בקשות לאותו host היה עקבי עם השהייה שהוגדרה של 1.0 שנ׳; ברירת המחדל שמגיעה עם המערכת היא 5.0 שנ׳.
- Apache-2.0, מתוחזק באופן פעיל, 78 תוספים, ו-crawldb קבוע שמנהל מצב לכל URL בין סבבים.
- רץ במצב מקומי בלי cluster, בלי HDFS ובלי Solr נדרשים.
חסרונות
- לא ירוץ על JDK 24 ומעלה, שם הסרת ה-SecurityManager פוגעת בו (בדקתי את הכשל על 26.0.1) — Hadoop 3.4.2 שמובנה במערכת מקדים את התיקון upstream ודגל המילוט כבר הוסר, כך שנעילת JDK LTS היא דרישת סף קשיחה ולא העדפה.
- כ-396 MB לאחר פריסה, 188 JAR-ים של ספריות, 78 תיקיות תוספים, 35 קובצי תצורה.
- JVM חדש לכל פקודה אומר כ-1.77 שנ׳ של תקורה קבועה לכל שלב; כ-45 שנ׳ לסריקת עומק 4 של 12 עמודים מול כ-13 שנ׳ לסורק בקובץ בינרי אחד באותו תנאי בסיס.
- ברירת המחדל שמגיעה עם המערכת עוקבת אחרי קישורים ל-hostים חיצוניים; הישארות באתר אחד היא בחירה מפורשת.
http.agent.nameמגיע ריק וה-fetcher מסרב לפעול עד שמגדירים אותו.- אין דגל עומק — העומק הוא מונה לולאה שאתם מנהלים בעצמכם.
- endpoints שמבוססים על DOM בזמן ריצה לא הושגו באף תצורה שנבדקה, והחלפת הפרוטוקול שמריץ JS לא הייתה שינוי drop-in.
- בדקתי מצב מקומי על host יחיד עם fixture קטן. מצב distributed/HDFS, אינדוקס ב-Solr, hostdb, resume ותזמון re-crawl מצטבר היו מחוץ לבדיקה הזו — ראו בהם כאן לא נבדקים, לא מאושרים.
למי זה מתאים, ולמי עדיף להתרחק
Nutch מצדיק את עצמו כשהסריקה עצמה היא החלק הקשה. אם אתם בונים אינדקס חיפוש, מריצים סריקה רחבה רב-דומיינית, צריכים מסד נתונים קבוע של כתובות עם מצב לכל URL וסמנטיקה של retry, או מצפים בסופו של דבר לפזר את העבודה על פני כמה מכונות — זו תשתית שעושה בדיוק את העבודה הזאת כבר מלפני שרוב החלופות בכלל התקיימו. מערכת התוספים מאפשרת לשנות פרוטוקול, מפענח, מסנן וניקוד בלי לעשות fork לשום דבר. ברירות המחדל של הנימוס הן שמרניות באופן שמרמז שהמתחזקים חשבו ברצינות על התנהגות אחראית.
עדיף להתרחק אם אתם רוצים לצאת עם נתונים מובְנים מכמה עמודים בודדים. Nutch ימשוך ויפענח אותם, ואז ימסור לכם crawldb ו-segments ויצפה שתביאו אינדקסר. עדיף להתרחק אם היעדים שלכם הם single-page apps שמרונדרות בצד הלקוח — מחלקה C נשארה בלתי מושגת בכל מה שהרצתי. עדיף להתרחק אם הצוות שלכם לא מפעיל JVM, כי אז תוסיפו שרשרת כלים של Java, נעילת JDK מסוג LTS, ו-396 MB של JAR-ים ל-stack שאין בו דבר מזה. ואם העומס הוא "לסרוק אתר אחד, ארבע רמות עומק, פעם בשבוע", תבלו יותר זמן בלולאת הסבבים ובקובצי התצורה מאשר הסריקה מצדיקה.
עבור רוב האנשים שמחפשים scraper, זהו בעצם המקרה האמיתי. וזה לא ביקורת על Nutch — זה אי-התאמה בין הכלי למשימה. אם אתם רוצים להתרשם מהשדה הרחב יותר, הסקירה שלנו על סקרפרים בקוד פתוח ו-פרויקטי ה-web scraping הטובים ביותר ב-GitHub מכסות בפירוט רב יותר את הקצה הקל יותר של הספקטרום.
חלופות, כולל איפה הסטאק שלנו נכנס
המסגור ההוגן קודם כול: Nutch הוא חינמי, ברישיון Apache, מתארח אצלכם, ויישאר שלכם להפעיל לנצח בלי עלות לכל בקשה. זה יתרון אמיתי, ושום דבר למטה לא מבטל אותו.
סקירה קשורה: סקירת Browsertrix Crawler.
בעולם ה-קוד פתוח, ההשוואה תלויה במה מנסים למקסם. אם אתם רוצים framework ב-Python עם שליטה על crawl וגישה שמתחילה מבקשות, Scrapy הוא אנלוג קרוב יותר עבור הרבה פרויקטים; המאמר הזה לא מדד את נפח ההתקנה שלו באותו אופן. אם אתם רוצים סורק Go קומפקטי בלי דפדפן, Colly הוא עוד צורה שכדאי לבדוק. אם הבעיה שלכם היא להפוך עמודים לתוכן מוכן ל-LLM ולא לגלות URLs, Crawl4AI מכוון לשכבה אחרת.
שירות מנוהל כמו Thunderbit מעביר את השליפה, הרינדור והחילוץ מאחורי API, בעוד Nutch שומר את מצב הסריקה והתשתית תחת שליטתכם. Thunderbit לא רץ על ה-fixture הזה, כך שזו השוואה של מודל בעלות ולא טענה על recall תואם או ביצועי עמודים דינמיים.
הפשרה היא בין בעלות לתקורה, והיא לא עדינה. Nutch נותן לכם שליטה מלאה, crawldb קבוע, יכולת התרחבות ל-cluster כבר בתכנון, ועלות שולית אפסית — בתמורה ל-JVM, נעילת JDK מסוג LTS, 396 MB של JAR-ים, לולאת סבבים, ושכבת אינדוקס משלכם. API מנוהל נותן פלט מובנה כבר בקריאה הראשונה בלי תשתית — בתמורה ל-תמחור לפי קריאה ופחות שליטה על חזית הסריקה. אם העבודה שלכם היא "לאנדקס 50 מיליון עמודים", המודל של Nutch נכון, ו-API יהיה אבסורדי. אם העבודה היא "להוציא רשומות מובנות מ-200 דפי מוצר עד יום חמישי", ההפך הוא הנכון.
נסו את Thunderbit לחילוץ נתוני רשת
מסקנה
Apache Nutch שווה בחינה אם אתם מפעילים סריקה רציפה ורב-דומיינית וכבר מפעילים תשתית JVM. על ה-fixture הזה, הגילוי הסטטי שלו היה דטרמיניסטי בין ריצות, parse-js מצא את שני ה-endpoints הליטרליים ב-JavaScript, כשלים נשמרו ב-crawldb, והמרווח בין הבקשות שנצפה תאם להשהייה שהוגדרה.
צריך לתמחר את עלות הכניסה ביושר. Nutch 1.22 נכשל כאן על JDK 26.0.1; OpenJDK 17.0.20 הוא תצורת ה-LTS שאכן אומתה בסקירה הזו, בעוד Java 21 לא נבדקה. לאחר מכן הגדירו http.agent.name, קבעו במפורש את תחום הסריקה, וקחו בחשבון את רצפת התקורה הקבועה של כ-1.77 שנ׳ לכל שלב בריצה המקומית הקטנה הזו. האם העסקה הזו הגיונית תלוי במשך הסריקה, ברוחבה, ובצורך במצב קבוע.
נסו את Thunderbit לחילוץ נתוני רשת Get Started Free
שאלות נפוצות
למה Apache Nutch נכשל עם "getSubject is not supported"?
ב-JDK 24 או חדש יותר, JEP 486 גרם ל-Subject.getSubject() להיזרק תמיד, בעוד Hadoop 3.4.2 שמובנה במערכת עדיין קרא לו. לכן משימת Hadoop הראשונה קורסת עוד לפני שליפת עמוד כלשהו, ודגל המילוט הישן -Djava.security.manager=allow כבר לא מפעיל את ה-VM. השתמשו בתצורת Java 17 שאומתה והגדירו NUTCH_JAVA_HOME; ייתכן ש-Java 21 נתמכת, אבל הסקירה הזו לא הריצה עליה את המחזור המלא.
על איזו גרסת Java כדאי להריץ את Nutch 1.22?
Java 17 היא התשובה הבטוחה ביותר — ה-CI של Nutch עצמו מכוון אליה, והיא עבדה נקי בבדיקות שלי על OpenJDK 17.0.20. גם Java 11 עדיין נתמכת עבור 1.22, אף שהפרויקט כבר הודיע ש-1.23 תדרוש Java 17. כל דבר מ-JDK 24 ומעלה לא ירוץ. התקנת Homebrew קג-אונלי (brew install openjdk@17) יחד עם NUTCH_JAVA_HOME משאירה את ברירת המחדל של JDK במערכת ללא שינוי.
האם Nutch יכול לסרוק אתרים כבדי JavaScript?
באופן חלקי, וזה הבדל חשוב. עם התוסף parse-js מופעל, Nutch מצא את שני ה-endpoints שקיימים רק כליטרליות מחרוזת בתוך קובץ JavaScript מקושר — 2/2, בלי דפדפן. בלי התוסף הזה, הוא לא מצא אותם. אבל endpoint שמופיע רק אחרי ש-JavaScript רץ ומשנה את ה-DOM נשאר בלתי נגיש בכל תצורה סטטית שבדקתי, והחלפת הפרוטוקול ל-HtmlUnit לא הייתה שינוי drop-in בריצה שלי. עבור אפליקציות שמרונדרות בצד הלקוח, צריך פרוטוקול שמריץ JS ועבודה קונפיגורציונית אמיתית, או כלי אחר.
האם Nutch צריך ש-Hadoop ו-Solr יהיו מותקנים?
לא. מצב Local מריץ את LocalJobRunner של Hadoop בתוך התהליך — בלי cluster, בלי דמון HDFS, בלי YARN — וכל המחזור inject → generate → fetch → parse → updatedb עובד על מכונה אחת בלי שום דבר נוסף מותקן. Solr הוא בדרך כלל יעד האינדוקס, אבל הסריקה עצמה לא דורשת אותו. עם זאת, JAR-ים של Hadoop כן מובנים בתוך המערכת (13 כאלה, גרסה 3.4.2), וזו בדיוק הסיבה ש-בעיית התאימות ל-JDK קיימת בכלל.
איך מונעים מ-Nutch לסרוק אתרים אחרים?
צריך להגדיר זאת במפורש, כי ברירת המחדל שמגיעה עם המערכת לא עושה את זה. Nutch 1.22 מגיע עם db.ignore.external.links=false ועם מסנן URL סלחני, ובבדיקה שלי סריקה רגילה עקבה אחרי קישור ל-host אחר ומשכה אותו. אפשר להגדיר db.ignore.external.links=true ב-nutch-site.xml, או להוסיף כלל host ל-conf/regex-urlfilter.txt (למשל +^https://example\.com/ ואחריו -.). שתי האפשרויות הכילו את הסריקה במלואה בבדיקות, כפי שאומת מה-crawldb של Nutch עצמו ומלוג הבקשות של השרת האחר.


