في مستند لا تتجاوز أطول فقرة فيه 151 حرفًا، يُرجع jusText بالإعدادات الافتراضية صفر حرف. ليس استخراجًا جزئيًا — بل نصًا فارغًا تمامًا. خفّض عتبة واحدة فقط بحيث تتجاوز فقرة واحدة هذا الحد، وستحصل على 832 حرفًا من المستند نفسه. ولو خفّضتها أكثر، ستبقى النتيجة 832.
في هذا المثال، السلوك أشبه بحافة حادة أكثر من كونه هبوطًا تدريجيًا. وما إذا كانت القيمة الافتراضية في المكان الخطأ أم لا يعتمد على طول الفقرات وتوزيع المحتوى الحشوّي في مجموعة النصوص المستهدفة.
ما هو jusText، ولماذا تهم الكثافة المعجمية
مقارنةً بأدوات الاستخراج الأخرى في هذه المجموعة، يعتمد jusText بشكل غير معتاد على كثافة كلمات الوقف الخاصة باللغة. فالكتلة التي تحتوي نسبة عالية من الكلمات الوظيفية — مثل the وand وof وwas — تكون أكثر ميلًا لأن تُعتبر نصًا إنشائيًا. لكن هذه الإشارة المعجمية ليست كل شيء في المصنّف: طول الفقرة، وكثافة الروابط، وحدود الكتل المشتقة من HTML، والمسافة عن العناوين، وأصناف الفقرات المجاورة، ومرحلة المعالجة الحساسة للسياق كلها تؤثر في النتيجة أيضًا.
المرجع الرسمي: المستودع الرسمي لـ jusText.

وبما أن المصنّف يعتمد على قائمة توقفات لغوية، يأتي jusText مرفقًا بـ 100 قائمة. وهذه الواجهة المعجمية الخاصة بكل لغة هي أبرز ما يميّزه في هذه المقارنة، رغم أن الجودة متعددة اللغات لم تُختبر هنا.
الإصدار المختبَر: 3.0.2، رخصة BSD 2-Clause، 822 نجمة على GitHub. Python 3.14.2.
الحافة الحادة، مقاسة بالأرقام

يعمل مصنّف jusText على مرحلتين. في المرحلة الأولى، ومن دون سياق، تُوسَم كل فقرة على أنها good أو bad أو short أو neargood. ثم تأتي المرحلة الثانية الحساسة للسياق لترقية neargood إلى good — ولكن فقط إذا كانت بجوار كتلة مصنّفة أصلًا على أنها good. ولا تُمنح الفقرة تصنيف good بمفردها إلا إذا تجاوزت length_high، والقيمة الافتراضية هنا 200 حرف.
في مستند لا تتجاوز فيه أي فقرة 200 حرف، لا يوجد ما يطلق عملية الترقية أصلًا، فتتحول كل كتل neargood إلى محتوى حشوّي. وعندها تعود الصفحة فارغة بالكامل.
اختبرتُ العتبة على مثال أطول فقرة فيه 151 حرفًا:
length_high | الفقرات المصنفة good | الأحرف المُعادة |
|---|---|---|
| 200 (افتراضي) | 0 | 0 |
| 150 | 8 | 832 |
| 120 | 8 | 832 |
| 100 | 8 | 832 |
| 80 | 8 | 832 |
justext-length-threshold.json.
في هذا المثال الحدي، فقرة واحدة فقط تجاوزت الخط فحوّلت الفقرات الثماني المستهدفة كلها إلى مخرجات، بينما لم تؤدِّ زيادة التخفيف بعد ذلك إلى أي شيء إضافي. وهذا يتوافق مع مرور الترقية عبر المرحلة السياقية حول كتلة good موجودة مسبقًا، ولا يعني ترقية كل جار تلقائيًا في أي صفحة عشوائية.
وقبل أن أنسب هذا التحول إلى length_high, جرّبت length_low عبر أربع قيم مع max_link_density عبر قيمتين: ثماني توليفات، وكانت النتيجة كلها صفرًا. تعديل أيٍّ من هذين الإعدادين لم يُعد أي مخرجات في هذا المثال.
وعبر مجموعة الأمثلة الكاملة المكوّنة من 22 عنصرًا، يظهر النمط نفسه: 2 من 22 أعطت مخرجات عند length_high=200، و9 من 22 عند 150، و15 من 22 عند 120.
وهذان أمران لا يقولهما هذا الاختبار. لا يعني أن jusText سيئ الاستخراج — ففي صفحة واقعية طبيعية باللغة الإنجليزية ومع الإعدادات الافتراضية، أعاد 1,190 حرفًا من نص مقال نظيف، لأن فقرة الأخبار الحقيقية تتجاوز 200 حرف من المرة الأولى. ولا يعني أيضًا أن الإعداد الافتراضي خاطئ؛ بل يعني أنه يفترض وجود فقرات طويلة، وعليك أن تحسم ما إذا كانت مجموعتك النصية تحتوي هذا النمط قبل تشغيل الأداة.
الوجه الآخر من هذه المجموعة: تسرب أعلى عند الإعدادات الافتراضية
في هذه المجموعة الموسومة من الأمثلة، ومع الإعدادات الافتراضية، سجّل jusText أعلى تسرب للمحتوى الحشوّي.
| المكتبة | استرجاع المقال (كل 22) | تسرب المحتوى الحشوّي | دقة وحدات المحتوى | الوحدات الملوِّثة |
|---|---|---|---|---|
| Readability | 1.0000 | 0.2353 | 0.9109 | 35 |
| trafilatura | 0.9865 | 0.0588 | 0.9411 | 4 |
| newspaper4k | 0.9865 | 0.0000 | 0.9452 | 0 |
| resiliparse | 0.9054 | 0.0588 | 0.9381 | 7 |
| jusText | 0.8378 | 0.4706 | 0.8760 | 74 |
| goose3 | 0.8243 | 0.0000 | 1.0000 | 0 |
sixway-scores.json. مجموعة أمثلة واحدة، ومقيّم واحد، وكل وحدة موسومة بعلامة فريدة حتى يكون الاسترجاع مطابقًا تمامًا لعضوية السلسلة الفرعية.

تسرب 47% من المحتوى الحشوّي و74 وحدة ملوِّثة — أي ضعف معدل التسرب لدى Readability وأكثر من ضعفي التلوث في هذه المجموعة الاصطناعية الموسومة. هذه نتيجة تشخيصية عند الإعدادات الافتراضية، وليست ترتيبًا ثابتًا على مستوى المنتج كله.
التسرب المرصود يتسق مع مرحلة السياق نفسها المرتبطة بالحافة الحادة. فكتل neargood المؤهلة قد تُرقّى عندما تسمح قواعد الفئة والمسافة والسياق بذلك؛ وبالتالي قد تتجاوز كتلة تبدو كمقدمة تحريرية أو تعليقًا مجاورًا للنص الحدّ الفاصل. وتُظهر نتيجة المثال أي الكتل الموسومة سرّبت محتوى، بينما تظل الآلية المبسطة مشروطة بقواعد السياق الدقيقة لدى jusText.
أما الاسترجاع لديه فهو 0.8378، أي ثالث أسوأ نتيجة من الأسفل، وكل الخسارة تعود إلى العتبة: لم يسترجع شيئًا في مقال واحد مكوّن من 129 حرفًا، ولا في صفحة تضم عشر فقرات قصيرة، ولا في المستند شبه الفارغ.
قائمة كلمات الوقف اللغوية
تسعٌ وتسعون قائمة توقفات أخرى.
justext.get_stoplists() تُرجع 100 لغة. المصنّف هنا معرّف لغويًا بطبيعته، لا عبر ترجمة قاعدة إنجليزية عامة، وتبديل اللغة لا يتطلب سوى وسيط واحد:
import justext
paragraphs = justext.justext(html, justext.get_stoplist("Czech"))
text = "\n".join(p.text for p in paragraphs if not p.is_boilerplate)
كما أن trafilatura وgoose3 تعرضان سلوكًا مرتبطًا باللغة، لكن هذا الاستعراض لم يقيّم أي أداة على بيانات حقيقية غير إنجليزية. إن وجود 100 قائمة توقفات مرفقة مع jusText يجعله مرشحًا واضحًا للتقييم متعدد اللغات؛ لكن هذا المخزون وحده لا يثبت جودة الاستخراج عبر تلك اللغات، ولا يثبت أن المنافسين أضعف فيها.
الواجهة البرمجية بسيطة: دالتان وتسع ثوابت قابلة للضبط — length_low 70، وlength_high 200، وstopwords_low 0.30، وstopwords_high 0.32، وmax_link_density 0.20، وmax_heading_distance 200، إضافة إلى معالجة الترميز. كل شيء صغير وواضح ومذكور في التوقيع البرمجي.
التثبيت والسرعة
الأمر pip install justext يثبت 3 حزم فقط — وهو أقل عدد في المقارنة — وبحجم 22.4 MiB، وفي أقل من ثانيتين.
المرجع الرسمي: jusText على PyPI.
| المكتبة | الحزم | site-packages | زمن الاستيراد البارد | p50 للاستخراج |
|---|---|---|---|---|
| resiliparse | 5 | 21.0 MiB | 0.015 s | 0.06 ms |
| jusText | 3 | 22.4 MiB | 0.777 s | 0.56 ms |
| goose3 | 16 | 44.3 MiB | 2.181 s | 1.85 ms |
| newspaper4k | 22 | 47.5 MiB | 2.812 s | 2.69 ms |
| trafilatura | 17 | 69.9 MiB | 1.584 s | 0.51 ms |
install-and-import.json. كل مكتبة داخل بيئة افتراضية فارغة مستقلة.
ثلاث حزم و22.4 MiB تُعد تبعية خفيفة هنا، ووسيط استخراج قدره 0.56 ms قريب من 0.51 لدى trafilatura في هذا الاختبار. لكن قياس البيئة لا يفكك حجم الحزمة بحسب نوع الملفات، لذلك لا يمكنه تحديد مقدار ما يشكله مخزون قوائم التوقف من مساحة القرص.
سؤال الصيانة، بإجابة دقيقة
مقاييس الصيانة الزمنية المستخدمة هنا هي آخر دفع للمستودع وآخر إصدار على PyPI، وكلاهما 2025-02-25. وكان ذلك قبل الاختبار بسبعة عشر شهرًا. إجمالي الإصدارات ثمانية، و91 تفريعًا، و9 مشكلات مفتوحة، وغير مؤرشف.
تُظهر مصنفات PyPI دعمًا يصل حتى 3.9. لكنني شغّلته على 3.14.2، فتم التثبيت والاستيراد في 0.777 ثانية، واستخرج النص من 19 من أصل 22 مثالًا من دون استثناء واحد.
إذًا فالميتاداتا متأخرة خمس نسخ بايثون عن الواقع، بينما الواقع أنه يعمل. وهذه هي النقطة المفيدة: المستودع الهادئ إشارة إلى الدعم لا إلى الوظيفة تلقائيًا. وبالنسبة لمكتبة خوارزميتها كلها طريقة منشورة من عام 2011 مع مجموعة قوائم كلمات، فحالة «انتهى العمل» تبدو معقولة — إذ لم يبق كثير لتغييره، وقوائم التوقف لا تتآكل كما تتآكل حلول التحايل على أنظمة الحماية.
لكن ماذا يعني المستودع الهادئ؟ يعني أنك إذا واجهت خطأ، فستصلحه بنفسك أو ستنشئ نسخة متفرعة. ضع هذا في ميزانك مع وجود 9 مشكلات مفتوحة، وهو ليس نمط مكتبة غارقة في مشاكل غير محلولة.
الذاكرة، وما الذي تفعله HTML المعطوبة بها
هنا تُقاس مسألتان تشغيليتان بشكل منفصل.
السياق الأوسع لاختبار الضغط موجود في مقارنة الذاكرة وHTML المعطوبة عبر عشر مكتبات.
أقصى ذاكرة مقيمة، عبر /usr/bin/time -l، مع عملية جديدة لكل خلية — حد الاستيراد هو ما تستهلكه المكتبة وهي محمّلة وخاملة، أما الذروات فتشمل المستند نفسه.
| المكتبة | وقت التشغيل | حد الاستيراد | ذروة 226 KB | ذروة 10 MB |
|---|---|---|---|---|
| html2text | python3.14 | 18.7 | 19.9 | 71.2 |
| pyquery | python3.14 | 30.3 | 33.9 | 172.5 |
| resiliparse | python3.14 | 20.5 | 25.1 | 225.1 |
| markdownify | python3.14 | 23.9 | 28.9 | 278.5 |
| goose3 | python3.14 | 44.1 | 52.4 | 398.5 |
| cheerio | node22 | 66.8 | 76.5 | 398.5 |
| justext | python3.14 | 30.3 | 36.6 | 431.2 |
| newspaper4k | python3.14 | 52.6 | 61.8 | 668.5 |
| trafilatura | python3.14 | 52.5 | 64.8 | 927.1 |
| turndown | node22 | 47.8 | 68.4 | 2947.1 |
memory-results.json. لا يمكن مقارنة خطوط الأساس في Python وNode مباشرة؛ فالمفسّر موجود في كليهما.
حدّ jusText الأدنى هو 30.3 MiB، وذروته في هذا المثال البالغ 10 MB هي 431.2 MiB، أي نحو 14.2 مرة من حد الاستيراد و43.1 مرة من حجم الإدخال بالاعتماد على RSS الخام. لكن مثالًا واحدًا وعملية واحدة لا تكفيان لرسم منحنى قياس عام؛ كما أن خطوط أساس Python وNode تبقى غير قابلة للمقارنة مباشرة.
HTML المعطوبة. اثنا عشر مستندًا، كل واحد يكسر شيئًا واحدًا فقط — وسوم غير مغلقة، عناصر داخلية موضوعة في غير محلها، سمات غير مقتبسة مع مسافات، وسوم إغلاق زائدة، وعدم وجود <html> أصلًا، وسمات مكررة، ومستند مقطوع منتصف الوسم، وكيانات خاطئة، و<script> غير مغلق، وإعلان ترميز كاذب، وتعليق يحتوي على HTML، و600 مستوى من التعشيق — إضافة إلى عينتين سليمتين بمقاسات مماثلة، لأن «أعاد لا شيء» لا يقول شيئًا عن فساد البنية إذا كانت الأداة صامتة أيضًا على مستند سليم بنفس الحجم.
أطلق justext استثناءً في 0 من أصل 14، وأعاد لا شيء في 13، واسترجع 0/33 من العلامات المميزة المقاسة عبر العينات المعطوبة (malformed-results.json). كما أن العينة السليمة القصيرة أعادت أيضًا 0 حرف، بينما كانت العينة السليمة الطويلة هي النتيجة الوحيدة غير الفارغة بـ1,333 حرفًا. جميع العينات المعطوبة الاثنتي عشرة بقيت فارغة، وحالة <script> غير المغلق مستثناة من تقييم بقاء العلامات. وبالتالي يثبت هذا التجميع تحمّل المحلل — بلا استثناء — لكنه لا يستطيع أن ينسب الصمت إلى فساد HTML بدلًا من العتبة المرتبطة بالحجم التي قِيسَت مسبقًا.
الإيجابيات والسلبيات
من ناحيته. 100 قائمة توقفات لغوية، ومصنّف بُني فعلًا حولها لا مجرد ترجمتها إليها. أقل عدد تبعيات في المقارنة: 3 حزم. استخراج وسيط بزمن 0.56 ms. تسع ثوابت ضبط موثقة وواضحة. BSD 2-Clause. يعمل بسلاسة على Python 3.14 رغم أن مصنفات الدعم تتوقف عند 3.9.
ضده. أعلى تسرب للمحتوى الحشوّي في هذا الاختبار الاصطناعي عند الإعدادات الافتراضية: 47%، مع 74 وحدة ملوِّثة. المثال القصير عاد بسلسلة فارغة عندما لم تتجاوز أي فقرة length_high. الاسترجاع كان 0.8378 في هذه المجموعة. كما أن آخر دفع للمستودع وآخر إصدار مسجل كانا قبل الاختبار بسبعة عشر شهرًا، لذا يجب أخذ عبء الصيانة بعين الاعتبار.
من المناسب له ومن لا يناسبه
اختبر jusText إذا كانت لديك مجموعات نصية متعددة اللغات، لأن سطح قائمة التوقفات الخاص بكل لغة واضح وواسع. هذا الاختبار أحصى 100 قائمة توقفات لكنه لم يقس الجودة متعددة اللغات. كما أنه مرشح جيد عندما تكون تبعية من ثلاث حزم فقط، مع تحكم صريح في العتبات، مناسبة لبيئتك.
تجاوزه إذا كنت تمرّر البيانات إلى نموذج على مستوى كل رمز، لأن 74 وحدة ملوِّثة مقابل صفر لدى goose3 وnewspaper4k تمثل كلفة مباشرة. وتجاوزه أيضًا في الصفحات القصيرة الفقرات — مثل ملخصات المنتجات، والقوائم، وسجلات التغييرات، وإدخالات الأسئلة الشائعة — ما لم تكن قد ضبطت length_high عمدًا. وتجاوزه إن كنت تحتاج تبعية نشطة الصيانة لأسباب امتثال أو شراء، وهو قيد حقيقي حتى لو كان الكود يعمل.
وإذا استخدمته فعلًا، فاضبط length_high على عينة تحقق موسومة بدلًا من نسخ القيمة الافتراضية أو قاعدة المئينات. جرّب نطاقًا من العتبات المعقولة وقِس استرجاع المقال ودقة المحتوى الحشوّي معًا؛ فخفض العتبة قد يعيد النصوص القصيرة، لكنه قد يرفع أيضًا كتلًا مجاورة غير مرغوبة.
أين تناسب واجهة API المُدارة
يتعامل jusText مع HTML لديك أصلًا، مثل بقية الأدوات في هذه المقارنة. ولا تقوم أي من هذه المكتبات بجلب الصفحة أو تنفيذ JavaScript أو التعامل مع طبقة حماية ضد الروبوتات.
للاطلاع على نفس الأمثلة عبر جميع أدوات الاستخراج الست، راجع مقارنة الاستخراج بين ست مكتبات.
أما خدمة الاستحواذ/العرض/الاستخراج المستضافة، بما في ذلك Thunderbit نفسها، فهي تغطي حدود مسؤولية مختلفة. ولم يخضع Thunderbit للاختبار هنا. والفرق ببساطة هو: تصنيف نص HTML مُزوَّد مسبقًا مقابل خدمة تجلب عنوان URL وتعالجه؛ وهذه المقالة لا تقدم مقارنة جودة على المقياس نفسه.
والصياغة المنصفة هي: قوائم التوقفات متعددة اللغات في jusText قدرة حقيقية ومجانية. فإذا كانت مشكلتك هي جلب الصفحات بلغات كثيرة أكثر من تصنيف النصوص بداخلها، فهذه عملية شراء مختلفة.
وللمشهد الأوسع، راجع جولة على واجهات Web Scraping API للخدمات المستضافة، ودليل open-source scraper للحلول المستضافة ذاتيًا. وإذا كانت المخرجات ستتجه إلى نموذج، فـتحويل HTML إلى Markdown في Python هو المكان الذي تضيع فيه أغلب الدقة.
جرّب Thunderbit لاستخراج بيانات الويب
هل يجب أن تستخدم jusText؟
هو خيار مناسب إذا كانت مجموعة النصوص متعددة اللغات أو إذا كانت توزيعات طول الفقرات فيها تستفيد من ضبط العتبات صراحةً. تحقّق من هاتين الخاصيتين قبل النشر.
قوائم التوقفات المرفقة وعددها 100 هي ميزة تصميم حقيقية، لكن الدقة متعددة اللغات لم تُختبر. الحافة الحادة في العتبة قابلة للضبط؛ أما ما إذا كانت القيمة الأقل مقبولة فيعتمد على الاسترجاع ودقة المحتوى الحشوّي المقاسين على عينة موسومة.
في هذه المجموعة الاصطناعية الإنجليزية ومع الإعدادات الافتراضية، سرّبت newspaper4k صفر وحدات حشو موسومة، واستعادت 0.9865 من وحدات المقال. وهذا يجعلها مرشحًا للمقارنة في هذا الحمل، لا توصية استبدال شاملة.
جرّب Thunderbit لاستخراج بيانات الويب Get Started Free
الأسئلة الشائعة
لماذا يُرجع jusText نصًا فارغًا بدل استخراج جزئي؟
لأن مصنّفه يعمل على مرحلتين. الفقرة لا تنال تصنيف good بمفردها إلا إذا تجاوزت length_high (القيمة الافتراضية 200 حرف)، ثم تأتي المرحلة الثانية لترقية الكتل المجاورة neargood. فإذا لم تتجاوز أي فقرة العتبة، فلا توجد نقطة انطلاق أصلًا، فتتحول كل المرشحات إلى محتوى حشوّي وتكون النتيجة فارغة. هذا سلوك قائم على مبدأ الكل أو لا شيء، وليس فشلًا في إيجاد جواب جزئي.
هل jusText مهجور؟ آخر دفع للمستودع وآخر إصدار على PyPI كانا كلاهما في 2025-02-25 — أي قبل الاختبار بسبعة عشر شهرًا — كما أن مصنفات PyPI تتوقف عند Python 3.9. لكنه ثُبّت وعمل على Python 3.14.2 من دون استثناء، و9 مشكلات مفتوحة لا تعني تراكمًا ضخمًا. لذا اقرأ التواريخ كمؤشر خطر على الصيانة لا كدليل على عطل في السلوك؛ والخطر العملي هو أنك قد تحتاج إلى إصلاح أخطائك بنفسك.
لماذا يتسرب محتوى حشوّي أكثر من Readability؟ في هذه المجموعة، عبرت كتل مجاورة شبيهة بالنصوص التحريرية حدود المخرجات وفق قواعد السياق الافتراضية. الترقية هنا مشروطة بنوع الكتلة والمسافة والسياق، وليست تلقائية لكل جار. وكانت النتيجة المقاسة تسرب 47% من وحدات المحتوى الحشوّي و74 وحدة ملوِّثة عند الإعدادات الافتراضية.
كيف أستخدمه للغة أخرى؟
justext.justext(html, justext.get_stoplist("German")). تُرجع get_stoplists() جميع القوائم المئة المتاحة. قائمة التوقفات هي المدخل المعجمي الخاص باللغة إلى مصنّف يستخدم أيضًا طول الفقرة، وكثافة الروابط، والتقسيم المستمد من HTML، والمسافة عن العناوين، وسياق الكتل المجاورة. هذا يبدّل إدخال اللغة؛ لكنه لا يثبت وحده جودة الاستخراج بالألمانية.
ما الذي لم يُختبر هنا؟
لم تُختبر صفحات حقيقية إطلاقًا — فهذه عينات مضبوطة ذات وحدات موسومة. كما أن القدرة متعددة اللغات، وهي نقطة البيع الأساسية للمكتبة، جرى حصرها في 100 قائمة توقفات دون قياس النص غير الإنجليزي. ولم تُختبر حالات حافة الترميز، رغم أن jusText يوفّر معاملات encoding وdefault_encoding وenc_errors. كما أبقيت max_heading_distance ونسبتي كلمات الوقف عند القيم الافتراضية طوال الوقت.


