مراجعة Apache Nutch: أربعة حدود تحدد ما إذا كان يعمل وما الذي يعثر عليه

آخر تحديث في August 14, 2026
مراجعة Apache Nutch: أربعة حدود تحدد ما إذا كان يعمل وما الذي يعثر عليه
ملخص AI
Apache Nutch هو زاحف تابع لمؤسسة Apache Software Foundation بدأ تطويره عام 2004. وهو نظام يعمل على JVM ومبني على Hadoop، ويُدار على هيئة دورة متكررة بدلًا من أمر تدفقي واحد: حيث تُحقن عناوين URL البذور في قاعدة بيانات دائمة، ثم تتكرر جولات generate → fetch → parse → updatedb، مع أماكن إضافات للبروتوكول والمحلل وفلترة الروابط والتقييم. أما المخرجات المعتادة فتتجه إلى فهرس بحث مثل Solr أو Elasticsearch بدلًا من CSV. اختبرت Nutch 1.22 على موقع محلي مضبوط ومتحكم فيه — موقع يسجل كل طلب من جهة الخادم، بحيث تُقاس النتائج بما رآه الخادم فعليًا لا بما ادّعاه الزاحف.

Apache Nutch هو زاحف تابع لمؤسسة Apache Software Foundation بدأ تطويره عام 2004. وهو نظام يعمل على JVM ومبني على Hadoop، ويعمل على هيئة دورة متكررة لا كأمر تدفقي واحد: تُحقن عناوين URL البذور في قاعدة بيانات دائمة، ثم تتكرر جولات generate → fetch → parse → updatedb، مع أماكن إضافات مخصصة للبروتوكول والمحلل وفلترة الروابط وتقييمها. أما المخرجات المعتادة فتتجه إلى فهرس بحث مثل Solr أو Elasticsearch، وليس إلى ملف CSV.

اختبرت Nutch 1.22 على موقع محلي مضبوط ومتحكم فيه — موقع يسجل كل طلب من جهة الخادم، بحيث تُقاس النتائج بما رآه الخادم فعليًا لا بما ادّعاه الزاحف. وقد حدّدت أربعة عوامل مسار التشغيل: إصدار JDK، وhttp.agent.name، ونطاق الزحف، ووجود parse-js داخل plugin.includes. تكررت الدورة الكاملة عدة مرات بالإعدادات المختبرة؛ وعند تغيير JDK أو ترك هوية الوكيل غير مضبوطة، يتوقف التشغيل قبل الوصول إلى صفحات مفيدة.

يظهر حد 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 بالمعنى التقليدي. استخراج الحقول المنظمة ليس مهمتها: فهي تكتشف عناوين URL وتزحف إليها على نطاق واسع، وتحتفظ بقاعدة بيانات دائمة لهذه العناوين وحالاتها (crawldb)، ثم تسلّمك segments ليحوّلها شيء آخر إلى فهرس. إذا وجّهتها إلى كتالوج وتتوقع جدولًا بالأسماء والأسعار، فستحصل بدلًا من ذلك على crawldb.

هذه البنية تفسر معظم ما يلي. Nutch سبقت عصر الزواحف أحادية الملف بنحو عقدين، وقد بُنيت لحل المشكلة التي صُمم Hadoop من أجلها: الزحف إلى صفحات أكثر من أن تتسع على جهاز واحد. تشغيلها على حاسوب محمول ضد موقع تجريبي من 12 صفحة يشبه استئجار قطار شحن لنقل رف كتب — تجربة مفيدة لفهم القطار، لكنها ليست عادلة إذا توقعت منه مرونة دراجة.

الإصدار الحالي هو 1.22، وأُعلن عنه في 17 فبراير 2026. وهو مرخص Apache-2.0، وكان المستودع يملك 3,272 نجمة و8 مشكلات مفتوحة عندما تحققت في 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 لم يصل حتى إلى الشبكة — إذ يهيئ Hadoop LocalJobRunner، الذي يسأل عن المستخدم الحالي، ثم يستدعي 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 أو عنقود Hadoop. هناك افتراض شائع أن Nutch تحتاج إلى عنقود Hadoop وSolr يعملان حتى تبدأ. هذا غير صحيح. وضع التشغيل المحلي يستخدم LocalJobRunner المدمج في Hadoop — بلا HDFS daemon، ولا YARN، ولا عنقود. دورة inject → generate → fetch → parse → updatedb كلها تعمل على جهاز واحد من دون أي شيء آخر مُثبت. جدار JDK هنا مجرد مشكلة توافق بين المكتبات المضمنة، وهو يمنعك قبل أن تطرح حتى سؤال البنية التحتية.

مصفوفة الإصدارات العملية، وقد تم قياس الصفوف الثلاثة كلها:

JDK المستخدمالأمرالنتيجة
OpenJDK 26.0.1bin/nutch injectيفشل، rc=255 — UnsupportedOperationException: getSubject is not supported
OpenJDK 26.0.1bin/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 طويل الدعم ووجّه Nutch إليه:

brew install openjdk@17
export NUTCH_JAVA_HOME=/opt/homebrew/opt/openjdk@17

وهو تثبيت keg-only، لذا لا يغيّر JDK الافتراضي في النظام. واختبارات Nutch الذاتية تستهدف Java 17، كما أعلن المشروع علنًا أن 1.22 هي آخر نسخة تعمل على Java 11 وأن 1.23 ستتطلب Java 17. لذلك JDK طويل الدعم ليس التفافًا على المشكلة، بل هو الإعداد المدعوم. الاختلاف هنا بين ما تدعمه Nutch وبين ما يقدمه brew install openjdk في 2026، وهما سؤالان مختلفان يصطدمان في أول أمر.

من هذه النقطة فصاعدًا، جرى كل شيء على OpenJDK 17.0.20، حيث كانت الدورة كاملة ونظيفة.

الإعداد، بالأرقام: 396 ميغابايت وخاصية واحدة توقف كل شيء

كلمة "ثقيل" تُستخدم كثيرًا، لكنها بلا وزن فعلي لا تعني شيئًا. هذه هي المكونات الفعلية داخل توزيع Nutch 1.22 الثنائي بعد فك الضغط:

العنصرتوزيع Nutch 1.22 الثنائي
الحجم بعد فك الضغط≈396 MB
ملفات jars داخل lib/188 (≈113 MB)
— منها حزمة Hadoop المرفقة13
أدلة الإضافات78
ملفات jars داخل أدلة الإضافات533
ملفات الإعداد35
السكربتات داخل bin/2 — crawl وnutch

وللمقارنة، زاحف Go حديث مثل katana يأتي كملف تنفيذي واحد بحجم يقارب 50 ميغابايت، من دون JVM ومن دون jars خارجية.

ثم هناك البوابة التي لا يحذرك منها أحد. ملف nutch-site.xml المرفق فارغ، وhttp.agent.name يظل افتراضيًا سلسلة فارغة. عندما بقي هذا الحقل غير مضبوط، استخرج الزحف الأول صفر مسارات وسجّل:

ERROR Fetcher: No agents listed in 'http.agent.name' property.

مجرد ضبط هذه الخاصية وحدها — ولا شيء غيرها — قلب السلوك إلى جلب فعلي. ومع بقاء الخاصية فارغة، انتهى الأمر بدون جلب صفحات، وسجّل السجل الخطأ أعلاه؛ أي أنه لم يصمت.

التكوين الأدنى القابل للعمل اتضح أنه ثلاثة عناصر: conf/nutch-site.xml (اسم الوكيل، مجموعة الإضافات، النطاق)، وconf/regex-urlfilter.txt (تقييد المضيف)، وملف عناوين seed URLs. وهذا ليس عددًا سيئًا، لكنه ثلاثة ملفات أكثر من crawler run <url>.

ماذا وجد: مفتاح الإضافة الذي يفرق

System diagram: What it found: the plugin toggle that matters

كان الموقع التجريبي يحتوي على ثلاثة أنواع مختلفة عمدًا من النهايات، وانقسم سلوك Nutch بينها بوضوح:

  • الفئة A — روابط HTML عادية (4 صفحات، إضافة إلى سلسلة بعمق 3 روابط)
  • الفئة B — نهايات لا تظهر إلا كثوابت نصية داخل ملف JavaScript مرتبط: واحدة كوسيط دالة fetch('/api/js-endpoint-7')، وأخرى كإسناد const other = "/api/js-endpoint-8"
  • الفئة C — نهاية لا تظهر إلا بعد تنفيذ JavaScript وإدراجها داخل DOM

النتائج، من سجلات الزيارات على جهة الخادم، تكررت ثلاث مرات:

إعداد الإضافاتالفئة A (روابط HTML)الفئة B (ثوابت داخل ملف JS)الفئة C (DOM وقت التشغيل)
الافتراضي المرفق — parse-(html|tika)4/4 (recall 1.0)0/2 (recall 0.0)لم يتم الوصول إليها
مع parse-jsparse-(html|tika|js)4/4 (recall 1.0)2/2 (recall 1.0)لم يتم الوصول إليها

وكانت النتائج متطابقة في كل تكرار من التكرارات الثلاثة. حتمية كاملة.

وأكثر ما يُستهان به هنا هو قفزة الفئة B. وجدت Nutch نقطتي النهاية المضمّنتين في JavaScript من دون تشغيل متصفح، باستخدام الفحص القائم على regex داخل parse-js لمحتوى JavaScript. وكان ملف app.js نفسه يتم جلبه في الحالتين، لأن Nutch تتعامل مع <script src> كرابط خارجي مهما كانت الإضافات؛ لذا فالفرق كله يتمثل في ما إذا كان هناك شيء يقرأ محتوى الملف بحثًا عن سلاسل تبدو مثل URLs. عند تفعيل الإضافة، تلتقط كلا الشكلين النصيين.

على هذا الموقع التجريبي، وصلت Nutch الافتراضية وkatana في وضعها القياسي إلى مجموعة الفئة A نفسها، بينما وصلت Nutch مع parse-js وkatana مع -jc إلى الفئتين A وB من دون متصفح. لم يُسجَّل إصدار Katana ولا الأمر الكامل هنا، لذا فهذه النتيجة سياقية وليست معيار مقارنة صارمًا بين المنتجين.

أما الفئة C فهي السقف الصادق. لم تصل إليها أي تهيئة ثابتة، وهذا متوقع: استرجاع نقطة نهاية لا تظهر إلا بعد تنفيذ السكربت يتطلب بالفعل تنفيذ السكربت. وقد جرّبت استبدال protocol-http بـ protocol-htmlunit, وهو بروتوكول Nutch الذي ينفذ JavaScript عبر Java. وقد حمّل الموقع وعمل من دون انهيار، لكنه في نفس الإطار المكون من أربع جولات أنهى جولة واحدة فقط، وجلب صفحة seed وapp.js، ولم يصل إلى A أو B أو C، وأبلغت الجولة الثانية بـ 0 records selected for fetching. هذا ليس حكمًا على قدرات HtmlUnit، بل اختبارًا ناقص التهيئة. وما يثبته هنا أضيق: استبدال البروتوكول المنفذ لـ JavaScript ليس تغييرًا مباشرًا بسيطًا، والفئة C بقيت غير مُسترجعة في كل تهيئة اختبرتها.

التحكم في الزحف وسلوك الفشل

العمق ليس خيارًا مستقلًا. لا يوجد --depth 3 في Nutch؛ فالعمق هو عدد جولات generate → fetch → parse → updatedb التي تشغّلها، لأن الجولة R تجلب الحدود المكتشفة في الجولة R-1. وقد أكد اختبار سلسلة العمق ذلك بدقة:

عدد الجولاتأعمق مسار تم الوصول إليه
2/depth/1
3/depth/2
4/depth/3

الأمر بسيط وميكانيكي، لكنه يعني أن العمق هو عدد حلقات في سكربتك، لا باراميتر.

والآن الفخ. الافتراضي المرفق في Nutch هو db.ignore.external.links=false، مقترنًا بفلتر URL متساهل +. — وهذا يعني أن الزحف الافتراضي في Nutch سيتابع الروابط إلى خارج المضيف البذري. وضعت صفحة أولية تربط إلى مسار داخل النطاق ورابط آخر إلى hostname مختلف، فقام الزحف بجلب المضيف الخارجي. وتوافقت إشارتان مستقلتان: crawldb الخاصة بـ Nutch علّمت ذلك db_fetched، وعدّاد الخادم على المضيف الآخر سجل الزيارة.

البقاء داخل النطاق يتطلب تفعيلًا صريحًا، وكلتا الطريقتين تعملان بشكل موثوق:

الإعدادالمضيف الخارجي في crawldbزيارة الخادم للمضيف الخارجيهل تم الاحتواء؟
db.ignore.external.links=false (الافتراضي المرفق)db_fetched+1لا
db.ignore.external.links=trueغير موجود0نعم
قاعدة مضيف في regex-urlfilter.txt (+^http://127.0.0.1: ثم -.)غير موجود0نعم

إذا كنت تزحف إلى موقع واحد، فاضبط أحد الخيارين قبل أول تشغيل حقيقي. ملاحظة منهجية واحدة: هذا الاختبار تحديدًا حساس للحِمل على الخادم المحلي، لذلك جاءت الصفوف الثلاثة من تشغيل لم يلمس فيه أي شيء آخر الموقع التجريبي. السلوك نفسه واضح ميكانيكيًا ومدعوم بإشارتين مستقلتين؛ أما القيم نفسها فهي من تشغيل نظيف واحد، لا متوسط عدة تشغيلات.

ملفات Sitemap خطوة منفصلة. السلوك المهذب مفعل — فالزحف العادي جلب /robots.txt — لكن ملف sitemap نفسه يحتاج إلى أمر مستقل:

الأسلوبهل طُلب /sitemap.xml؟النهايات التي لم توجد إلا داخل sitemap
زحف عاديلم يُطلب أبدًا0/2
bin/nutch sitemap، مع تشغيله صراحة على crawldbتم جلبه2/2 من الإدخالات أُدرجت، واسترجاع كامل
وضع katana المدمج -kf للملفات المعروفة، على نفس الموقع التجريبي، واستضافة IPغير مسجل0/2

هذا نموذج مختلف عن الزواحف التي تسحب الملفات المعروفة ضمن التدفق نفسه، وهو يضيف أمرًا آخر، لكنه يؤدي المهمة بالكامل.

كما أثبت سلوكان أصغر أنهما يعملان جيدًا.

التعامل مع الأخطاء: زحف على صفحة تربط إلى استجابة 500 وأخرى 404 أنهى كل الجولات بسلاسة، وما زال جلب الصفحات الأربع من الفئة A، وسجل كل فشل على حدة:

الاستجابة الفاشلة المرتبطة من الصفحةحالة crawldb المسجلة
500db_unfetched (قابلة لإعادة المحاولة)
404db_gone

لم ينهَر شيء.

الالتزام بالمهلة والأدب: مع خيط واحد لكل queue، جاء الفارق بين عمليات الجلب من نفس المضيف مطابقًا للإعداد:

fetcher.server.delayالوسيط بين عمليات الجلب من نفس المضيف
1.0 ثانية1.009 s (الحد الأدنى 1.006 s)
0.00.002 s

المفتاح يفعل بالضبط ما يقول. والقيمة الافتراضية المرفقة هي 5.0 ثوانٍ، وهي محافظة، وربما صحيحة، مرة أخرى، لأداة صُممت لتزحف إلى خوادم لا تملكها.

كلفة الدفعات، بالثواني

كل أمر في Nutch هو JVM جديدة. وهذه الحقيقة وحدها تهيمن على ملف الزمن أكثر من أي شيء متعلق بعملية الجلب.

المرحلة (لكل جولة)الوسيط بالثواني
inject (مرة واحدة)1.81
generate3.93
fetch2.82
parse1.78
updatedb1.81
جولة كاملة واحدة12.14

الحد الأدنى الفعلي لكل مهمة — أي بدء JVM مع تهيئة Hadoop، مقاسًا كأرخص مرحلة عمل تافهة — يبلغ نحو 1.77 ثانية. اضرب ذلك في أربع أوامر لكل جولة، ثم أضف inject الأولي، وستبدو صورة الزحف الكامل كالتالي:

الأداةزحف عمقه 4 على موقع تجريبي من 12 صفحةالعمليات
Nutchنحو 45 ثانية (قست 45.8 ثانية و45.0 ثانية عبر إعدادين)حوالي 17 تشغيل JVM، لا تكاد أيٌّ منها ينفذ عمل شبكة فعلي
katana في وضع standard، على نفس الموقع التجريبينحو 13 ثانيةعملية واحدة

الفجوة هنا ليست في معدل الجلب؛ فكلتا الأداتين تطلبان العدد نفسه تقريبًا من الصفحات. الفارق معماري. Nutch تدفع كلفة عملية ثابتة لكل مرحلة لأن هذه المراحل مصممة كمهام MapReduce. في زحف محلي صغير، تطغى كلفة الإعداد. ويفترض أن تصبح الكلفة الثابتة نسبة أصغر مع ازدياد طول المهمة، لكن هذا الاختبار لم يقس الحجم الذي تتقاطع عنده Nutch وkatana، ولا ما إذا كانت النسبة تنقلب.

الإيجابيات والسلبيات

الإيجابيات

  • اكتشاف ثابت وحتمي: الفئة A ‏4/4، وسلسلة العمق 3/3، والنتائج متطابقة في ثلاث تشغيلات متكررة.
  • parse-js يسترجع نهايات ثوابت ملف JavaScript (2/2) من دون متصفح، ويصطاد شكلي وسيط الدالة والإسناد معًا.
  • وسيلتان موثقتان لضبط النطاق بالكامل (db.ignore.external.links وفلتر المضيف في regex-urlfilter).
  • استيعاب sitemap عبر bin/nutch sitemap حقق استرجاعًا كاملًا 2/2 لنهايات غفل عنها الزحف العادي تمامًا.
  • متين عند الفشل: تم التعامل مع 500 و404 بحالتين مختلفتين في crawldb، واستمر الزحف.
  • في هذا التشغيل المحلي، كان الفاصل المرصود بين الطلبات من نفس المضيف متوافقًا مع التأخير المضبوط 1.0 ثانية؛ والقيمة الافتراضية المرفقة هي 5.0 ثوانٍ.
  • Apache-2.0، مشروع نشط الصيانة، 78 إضافة، وcrawldb دائمة تتبع حالة كل URL عبر الجولات.
  • يعمل في الوضع المحلي من دون عنقود، ومن دون HDFS، ومن دون حاجة إلى Solr.

السلبيات

  • لا يعمل على JDK 24 أو أحدث، حيث يضربه حذف SecurityManager (وقد قيست الفشل هنا على 26.0.1) — فـ Hadoop 3.4.2 المرفقة تسبق الإصلاح upstream، وخيار الهروب أُزيل، لذا فإن تثبيت LTS JDK شرط أساسي لا تفضيل.
  • الحجم بعد فك الضغط ≈396 MB، و188 ملف jar للمكتبة، و78 دليل إضافة، و35 ملف إعداد.
  • JVM جديدة لكل أمر تعني نحو 1.77 ثانية كتكلفة ثابتة لكل مرحلة؛ وحوالي 45 ثانية لزحف عمقه 4 على 12 صفحة مقابل نحو 13 ثانية لزاحف أحادي الملف على الأرضية نفسها.
  • الافتراضي المرفق يتبع الروابط إلى مضيفين خارجيين؛ البقاء في موقع واحد يحتاج تفعيلًا صريحًا.
  • http.agent.name يأتي فارغًا، ويأبى Fetcher العمل حتى تضبطه.
  • لا يوجد مفتاح مباشر للعمق — العمق هو عدد الحلقات الذي تديره بنفسك.
  • نهايات DOM وقت التشغيل لم يمكن الوصول إليها في أي تهيئة اختبرتها، واستبدال البروتوكول المنفذ لـ JavaScript لم يكن تغييرًا مباشرًا جاهزًا للاستخدام.
  • اختبرت الوضع المحلي على مضيف واحد وبموقع تجريبي صغير. أما الوضع الموزع/HDFS، وفهرسة Solr، وhostdb، والاستئناف، وجدولة إعادة الزحف التدريجي فكانت خارج هذه الجولة — فاعتبرها غير مختبرة هنا، لا موصى بها.

من يجب أن يستخدمه، ومن الأفضل أن يبتعد

Nutch تستحق العناء عندما يكون الزحف نفسه هو الجزء الصعب. إذا كنت تبني فهرس بحث، أو تنفذ زحفًا واسعًا متعدد النطاقات، أو تحتاج إلى قاعدة بيانات URLs دائمة بحالة لكل URL ودلالات إعادة المحاولة، أو تتوقع أن توزع العمل لاحقًا على عدة أجهزة، فهذه بنية تحتية تؤدي هذا العمل المحدد منذ ما قبل ظهور معظم البدائل. نظام الإضافات يعني أنه يمكنك تغيير سلوك البروتوكول والمحلل والفلترة والتقييم من دون تفريع المشروع. أما الإعدادات الافتراضية المتعلقة باللباقة فهي محافظة بطريقة توحي بأن القائمين عليه فكروا بجدية في ألا يزعجوا الآخرين.

ابتعد عنه إذا كنت تريد بيانات منظمة من بضع صفحات فقط. Nutch ستجلبها وتفككها، ثم تسلّمك crawldb وsegments وتتوقع منك أن تجلب الفهرسة بنفسك. ابتعد إذا كانت أهدافك تطبيقات صفحة واحدة تُعرض من جهة العميل — فالفئة C ظلت بعيدة في كل ما شغّلته. ابتعد إذا لم يكن فريقك يعمل أصلًا على JVM، لأنك ستضيف سلسلة أدوات Java، وتثبيتًا مقيدًا لـ LTS JDK، و396 ميغابايت من jars إلى مكدس لا يحتوي على أي من ذلك الآن. وإذا كان عبء العمل هو "زحف موقع واحد، أربع مستويات عمق، مرة كل أسبوع" فستقضي وقتًا على حلقة الجولات والملفات أكثر مما يستحقه الزحف نفسه.

وبالنسبة لمعظم من يشترون scraper، فهذا السيناريو الأخير هو السيناريو الحقيقي. وهذه ليست انتقادًا لـ Nutch بقدر ما هي عدم تطابق بين الأداة والمهمة. وإذا أردت صورة أوسع للمجال، فإن استعراضنا لأدوات open-source scraper وأفضل مشاريع GitHub في web scraping يغطيان الطرف الأخف من الطيف بتفصيل أكبر.

البدائل، بما في ذلك موقع مجموعتنا هنا

أولًا التوصيف العادل: Nutch مجانية، بترخيص Apache، وتستضيفها بنفسك، ويمكنك تشغيلها إلى الأبد من دون تكلفة لكل طلب. وهذه ميزة حقيقية، ولا شيء أدناه يلغيها.

مراجعة ذات صلة: مراجعة Browsertrix Crawler.

داخل عالم open-source، تعتمد المقارنة على ما الذي تحاول تحسينه. إذا أردت إطار Python مع تحكم في الزحف وفلسفة تركز على الطلبات أولًا، فإن Scrapy أقرب مثال في كثير من المشاريع؛ ولم تقِس هذه المقالة حجم تثبيته على الأساس نفسه. إذا أردت زاحف Go صغيرًا بلا متصفح، فإن Colly شكل آخر يستحق التقييم. وإذا كانت مشكلتك تحويل الصفحات إلى محتوى جاهز للـ LLM بدلًا من اكتشاف URLs، فإن Crawl4AI يستهدف طبقة مختلفة.

أما الخدمة المُدارة مثل Thunderbit فتنقل الجلب والعرض والاستخراج إلى ما وراء API، بينما تبقي Nutch حالة الزحف والبنية التحتية تحت سيطرتك. ولم تُشغَّل Thunderbit على هذا الموقع التجريبي، لذا فهذه مقارنة في نموذج الملكية لا ادعاء حول تطابق الاسترجاع أو أداء الصفحات الديناميكية.

المفاضلة هنا بين التحكم والتكلفة الإضافية، وهي مفاضلة واضحة جدًا. Nutch تمنحك تحكمًا كاملًا، وcrawldb دائمة، وقابلية توسع عنقودية مصممة من الأساس، وتكلفة هامشية صفرية — مقابل JVM، وتثبيت LTS JDK، و396 ميغابايت من jars، وحلقة جولات، وطبقة الفهرسة الخاصة بك. أما API المُدارة فتعطيك خرجًا منظمًا من أول طلب ومن دون بنية تحتية — مقابل تسعير لكل طلب وتحكم أقل في حدود الزحف. إذا كانت مهمتك "فهرسة 50 مليون صفحة" ففلسفة Nutch هي الصحيحة، وAPI ستكون غير منطقية. وإذا كانت مهمتك "استخراج سجلات منظمة من 200 صفحة منتج قبل الخميس" فالعكس هو الصحيح.

جرّب Thunderbit لاستخراج بيانات الويب

الحكم النهائي

Apache Nutch تستحق التقييم إذا كنت تنفذ زحفًا مستمرًا متعدد النطاقات وتعمل أصلًا ضمن بنية JVM. على هذا الموقع التجريبي كان اكتشافها الثابت حتميًا عبر التكرارات، وparse-js وجد نهايتي 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 هو الجواب الأكثر أمانًا — اختبارات Nutch الذاتية تستهدفه، وقد عمل بسلاسة في اختباري على OpenJDK 17.0.20. كما أن Java 11 ما زال مدعومًا لـ 1.22، رغم أن المشروع أعلن أن 1.23 ستتطلب Java 17. أي إصدار من JDK 24 فما فوق لن يعمل. وتثبيت Homebrew من نوع keg-only (brew install openjdk@17) مع NUTCH_JAVA_HOME يبقي JDK الافتراضي في النظام دون تغيير.

هل تستطيع Nutch الزحف إلى مواقع كثيفة JavaScript؟ جزئيًا، والفارق مهم. مع تفعيل الإضافة parse-js، وجدت Nutch نهايتيْن موجودتيْن فقط كثوابت نصية داخل ملف JavaScript مرتبط — 2/2، ومن دون متصفح. أما بالإعداد الافتراضي فلم تجد أيًا منهما. لكن نهاية لا تظهر إلا بعد تنفيذ JavaScript وتعديل DOM بقيت غير قابلة للوصول في كل تهيئة ثابتة اختبرتها، واستبدال بروتوكول HtmlUnit لم يكن تغييرًا مباشرًا جاهزًا في تجربتي. بالنسبة للتطبيقات المعروضة من جهة العميل، إما أن تخطط لبروتوكول ينفذ JavaScript مع عمل تهيئة حقيقي، أو تستخدم أداة مختلفة.

هل تحتاج Nutch إلى تثبيت Hadoop وSolr؟ لا. الوضع المحلي يشغّل LocalJobRunner الخاص بـ Hadoop داخل العملية — بلا عنقود، ولا HDFS daemon، ولا YARN — وكل دورة inject → generate → fetch → parse → updatedb تعمل على جهاز واحد من دون أي شيء آخر مثبت. Solr هو عادة الوجهة النهائية للفهرسة، لكن الزحف نفسه لا يحتاجه. مع ذلك، فإن jars الخاصة بـ Hadoop موجودة ضمن التوزيع (13 ملفًا منها، بالإصدار 3.4.2)، وهذا بالضبط سبب ظهور مشكلة التوافق مع JDK أصلًا.

كيف أوقف Nutch من الزحف إلى مواقع أخرى؟ اضبط ذلك صراحة، لأن الافتراضي المرفق لا يفعل ذلك. Nutch 1.22 تأتي مع db.ignore.external.links=false وفلتر URL متساهل، وفي اختباري اتبع الزحف الافتراضي رابطًا إلى مضيف مختلف وجلبه. يمكنك إما تعيين db.ignore.external.links=true في nutch-site.xml، أو إضافة قاعدة مضيف إلى conf/regex-urlfilter.txt (مثلًا +^https://example\.com/ ثم -.). كلاهما احتوى الزحف بالكامل في الاختبار، وتم التحقق من ذلك من crawldb الخاصة بـ Nutch ومن سجل طلبات الخادم الآخر.

Ke
Ke
المدير التقني في Thunderbit | عالم بيانات أول وخبير في تعلّم الآلة بخبرة تقارب عقدًا من الزمن في تعلّم الآلة وعلم البيانات، كيه شين خريج جامعة كولومبيا وكان سابقًا عالم بيانات أول في Walmart Labs. وبفضل خبرته العميقة المعترف بها من قبل الأقران في Python وR وJava والإحصاء، يشارك رؤى مجرّبة حول نقل خوارزميات الذكاء الاصطناعي المعقدة من النظرية إلى بنية جاهزة للإنتاج.
فهرس المحتويات
Thunderbit · وكيل بيانات الويب بالذكاء الاصطناعي

استخرج البيانات من أي صفحة في بنقرة واحدة

موثوق لدى أكثر من 250,000 مستخدم
تتوفر خطة مجانية
من صفحة ويب إلى جدول بيانات
صف ما تحتاجه — ويقوم وكيل Thunderbit بالذكاء الاصطناعي باستخراجه وتصديره إلى Excel أو Google Sheets أو Airtable أو Notion. ابدأ مجانًا.
Chrome Store Rating
PRODUCT HUNT#1 Product of the Week