في أربع عينات HTML المشتركة مع markitdown، أنتج markdownify نفس عدد صفوف جداول Markdown الناتجة وفق عدّادنا — 36 مقابل 36 — واستعاد جميع سلاسل المحتوى الـ 16 التي تم التحقق منها. وكان إخراجه البالغ 21,062 رمزًا هو الأقل اسميًا، رغم أن المحولات الثلاثة الأولى كانت متقاربة ضمن 1.3%. كما أن حجمه عند التثبيت 1.8 ميغابايت فقط، ويأتي برخصة MIT، وكان لديه 2,235 نجمة على GitHub وقت اللقطة.
وهو المكتبة الأقل تداولًا في هذه الفئة، وبناءً على هذه الأرقام، فهي الأداة التي سأختارها أولًا.
ما هو markdownify
markdownify هو محول Python من HTML إلى Markdown مبني على BeautifulSoup. هذه هي البنية كاملة: تحليل عبر BeautifulSoup، ثم المرور على الشجرة، ثم إخراج Markdown. الإصدار الذي جرى اختباره: 1.2.3، برخصة MIT، و2,235 نجمة، و42 مشكلة مفتوحة، وآخر إصدار بتاريخ 2026-06-30 — ما يعني أنه مشروع نشط الصيانة، مع 44 إصدارًا.
المرجع الرسمي: المستودع الرسمي لـ python-markdownify.

واجهة الاستخدام لا تتعدى دالة واحدة:
from markdownify import markdownify
md = markdownify(html)
كما توجد صيغة تعتمد على class (MarkdownConverter) إذا أردت تخصيص طريقة معالجة العناصر، إضافة إلى مجموعة جيدة من الخيارات — مثل نمط العناوين، وأنواع النقاط، واكتشاف لغة الكود، واستبعاد عناصر معينة. لكن الاستخدام الأحادي السطر هو السيناريو الأكثر شيوعًا، وهو يعمل كما ينبغي.
الأمر pip install markdownify يجلب 5 حزم وحجمًا يبلغ 1.8 ميغابايت، ويُحمّل ببطء بدء مقداره 0.046 ثانية — وهو الأسرع بين المحولات الثلاثة التي اختبرتها. شجرة الاعتماد هنا هي BeautifulSoup ومرافقه المعتادة، وهي مكونات موجودة أصلًا في كثير من مشاريع Python، لذلك تكون الكلفة الإضافية في كثير من الحالات شبه صفرية.
طريقة القياس
شغّلته على أربع عينات HTML سبق أن استخدمتها مجموعة أخرى في هذه القاعدة مع markitdown، وباستخدام سلاسل الفحص المسبقة التسجيل الخاصة بتلك المجموعة — 16 سلسلة نصية مطابقة تمامًا داخل المتن جرى التحقق من بقائها، إضافة إلى فحوصات للكلام القياسي غير المهم في رأس الصفحة وتذييلها. أما العينة الخامسة، Nothing but tables، فهي تشخيص منفصل كثيف الجداول، ولذلك لا تدخل في إجمالي الأربع عينات أدناه.
| المحول | فحوصات المتن | عدد المحارف الناتجة | الرموز (o200k) | صفوف جداول Markdown | الروابط |
|---|---|---|---|---|---|
| markdownify | 16/16 | 76,868 | 21,062 | 36 | 599 |
| html2text | 16/16 | 76,452 | 21,176 | 32 | 545 |
| markitdown | 16/16 | 76,995 | 21,336 | 36 | 598 |
| turndown | 16/16 | 95,188 | 26,236 | 0 | 611 |
fourway-scores.json. أربع عينات، وعدّ الرموز باستخدام o200k_base، وعدّ صفوف الجداول وفق قاعدة واحدة عبر العينات الأربع كلها — بما في ذلك إعادة عدّ الناتج المخزن لـ markitdown، والذي طابق رقمَه المنشور تمامًا.
ثلاث نقاط تبرز هنا.
إنه يعادل markitdown في عدد صفوف الجداول الناتجة. 36 صفًا لكل منهما عبر العينات الأربع المشتركة، وفق نفس القاعدة الاستدلالية. هذا لا يثبت تطابقًا خليةً بخلية؛ لكنه يعني أن المخرجات في الحالتين عرضت للعدّاد نفس عدد صفوف Markdown الجداول القابلة للتعرّف.
لديه أقل عدد من الرموز. 21,062، وهو أقل قليلًا من 21,176 لدى html2text و21,336 لدى markitdown، وأقل بنسبة 19.7% من 26,236 لدى turndown. أما المحولات الثلاثة الأولى فالفارق بينها لا يتجاوز 1.3%، لذلك أعتبرها متعادلة تقريبًا لا فائزة واضحة؛ والفارق المهم فعلًا هو مقارنة turndown.
كل فحوصات المحتوى نجت. جميعها الـ 16. وكذلك الحال مع المحولات الثلاثة الأخرى — فسلامة المحتوى ليست موضع الاختلاف هنا.
الجدول الذي يتقنه
عينة إحصاءات الهوكي هي الموضع الذي تتباين فيه المحولات. markdownify:
| Team Name | Year | Wins | Losses | OT Losses | Win % | ... |
| --- | --- | --- | --- | --- | --- | --- |
| Boston Bruins | 1990 | 44 | 24 | | 0.55 | ... |
صيغة GFM القياسية مع خطوط عمودية في البداية والنهاية، وصف فاصل، و— انتبه إلى الخانة الفارغة بين 24 و0.55 — يُخرج الخانة الفارغة بدلًا من تخطيها. قد يبدو ذلك تفصيلًا بسيطًا، لكنه ليس كذلك: فالمحوّل الذي يحذف الخانات الفارغة يُزحزح كل القيم التي تليها إلى العمود الخطأ، بينما يبدو الإخراج في الظاهر وكأنه جدول صحيح.

turndown على نفس الإدخال يخرج كل قيمة كفقرة مستقلة بلا أي بنية أعمدة. أما html2text فينتج جدولًا صحيحًا لكن بأسلوب مختلف، من دون الخطوط العمودية الخارجية.
إذا كان Markdown سيُرسل إلى نموذج ذكاء اصطناعي، فإن الخطوط العمودية والخانة الفارغة المحفوظة هما ما يمكّنانه من الإجابة عن سؤال مثل "كم مباراة خسر Boston؟" بدل التخمين.
شيئان يحذفه markdownify ولا يحذفه turndown
دقة الجداول هي الفرق المرئي. أما هذا الفرق فهو أكبر ولا يكاد أحد يذكره.
markdownify يزيل محتوى <script> و<style>، بينما turndown لا يفعل ذلك. وعند العدّ باستخدام العلامات التي تظهر داخل هذه العناصر فقط، فإن مخرجات markdownify عبر العينات تحمل صفر علامات script وصفر علامات style؛ بينما يحملها turndown بواقع 10 و84. وفي عينة Wikipedia، هذا يعني الفرق بين 59,561 محرفًا و74,939 — وثماني سطور من إعدادات JavaScript وCSS المضمنة في MediaWiki تمثل 14,644 محرفًا، أي 95% من هذا الفارق (script-style-stripping.json).
وأي شيء يغذي نموذجًا لغويًا، فهذه هي أكثر نفقاته عبثًا: كتلة إعدادات JavaScript تستهلك الرموز ولا تضيف أي معلومة. markdownify يزيلها تلقائيًا من دون طلب. وhtml2text يفعل ذلك أيضًا.
لكل عينة، يتطابق markdownify وhtml2text تمامًا عندما يكون الجدول بسيطًا، ويختلفان عندما لا يكون كذلك:
| العينة | markdownify | html2text |
|---|---|---|
| إحصاءات الهوكي | 27 صفًا | 27 صفًا |
| Wikipedia | 9 صفوف | 5 صفوف |
| Nothing but tables (تشخيص منفصل) | 62 صفًا | 59 صفًا |
markdownify يُنتج صفوفًا معترفًا بها أكثر في المقارنتين التشخيصيتين. وفي Wikipedia تحديدًا، يحافظ على تسعة صفوف مقابل خمسة لدى html2text وفق هذا العداد. هذا إنذار مفيد لمراجعة الجداول المتداخلة وغير المنتظمة قبل الاختيار، لكنه ليس دليلًا على أن كل خلية ناتجة صحيحة دلاليًا.
التثبيت والترخيص، مقارنةً بالبدائل
| المكتبة | الحزم | الحجم على القرص | بدء الاستيراد البارد | الرخصة | النجوم | آخر إصدار |
|---|---|---|---|---|---|---|
| markdownify | 5 | 1.8 MiB | 0.046 s | MIT | 2,235 | 2026-06-30 |
| html2text | 1 | 0.2 MiB | 0.077 s | GPL-3.0-or-later | 2,168 | 2025-04-15 |
| turndown | 3 (npm) | 8.8 MiB | 0.056 s | MIT | 11,386 | 2026-04-03 |
المرجع الرسمي: markdownify على PyPI.
install-and-import.json وmetadata-snapshot.json. تم تثبيت كل مكتبة في بيئة فارغة خاصة بها.
html2text أصغر على القرص بتسعة أضعاف، لكنه يأتي برخصة GPL-3.0-or-later. وفي المنتج الموزّع، ينبغي اعتبار ذلك نقطة مراجعة للجهة المسؤولة عن الترخيص؛ وهذا المقال ليس استشارة قانونية. أما markdownify فبرخصة MIT، وهي عادةً أكثر سماحًا، لكنها تظل جزءًا من مراجعة الامتثال المعتادة.
كما أن 1.8 ميغابايت هنا رقم تقريبي إلى حد ما إذا كان مشروعك يستخدم BeautifulSoup أصلًا، وهو ما تفعله بالفعل كثير من مشاريع استخراج البيانات في Python — ففي هذه الحالة يكون markdownify شبه مجاني من حيث الكلفة الإضافية.
وتيرة إصدارات markdownify هي الأفضل صحةً بين الثلاثة: 44 إصدارًا ودفعة تحديث قبل ستة أسابيع من الاختبار، مقابل آخر إصدار لـ html2text في أبريل 2025.
ماذا تحصل عليه فعليًا من سطر بايثون واحد
المكتبة كلها هي markdownify(html)، ومن المهم أن نكون واضحين بشأن ما الذي يقرره هذا الاستدعاء نيابةً عنك، لأن ثلاثة من تلك القرارات هي التي دار حولها هذا الاختبار.
هو يحلل عبر BeautifulSoup، ويزيل <script> و<style> من دون أن يُطلب منه ذلك، ويخرج جداول بنمط GFM مع خطوط عمودية خارجية وخانات فارغة محفوظة. ومع أن أصل المحلل مهم، فإنه لا يضمن وحده التعامل مع HTML المعطوب، لذلك يُقاس هذا السؤال بشكل منفصل أدناه.
ولا شيء من هذه القرارات يحتاج إلى إعدادات منك. هذا هو السلوك الافتراضي، وفي فئة يكون فيها turndown يترك JavaScript افتراضيًا وhtml2text يلف الأسطر عند 78 حرفًا، فوجود مكتبة تعمل كما ينبغي من أول مرة يستحق التقدير بحد ذاته.
وتوجد خيارات عندما تحتاجها — heading_style وbullets وcode_language وstrip وconvert لقوائم السماح والمنع الخاصة بالعناصر، وMarkdownConverter لتجاوز السلوك لكل عنصر على حدة. ولم تُستخدم أي من هذه الخيارات في القياسات هنا.
الذاكرة، وما الذي يفعله 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 مباشرةً؛ فالمفسر موجود داخل كليهما.
markdownify يقع في المنتصف: حد استيراد 23.9 ميغابايت، و278.5 ميغابايت على مستند بحجم 10 ميغابايت. وهذا يزيد 3.9 مرات على ذروة html2text ويبلغ نحو عُشر turndown، وهذه هي كلفة اعتماد BeautifulSoup تحته.
HTML المعطوب. اثنا عشر مستندًا، كل واحد يكسر شيئًا واحدًا فقط — وسوم غير مغلقة، عناصر inline متداخلة بشكل خاطئ، سمات غير مقتبسة تحتوي على مسافات، وسوم إغلاق شاردة، عدم وجود <html> أصلًا، سمات مكررة، مستند مقطوع في منتصف وسم، كيانات تالفة، وسم <script> غير مغلق، تصريح charset كاذب، تعليق يحتوي على ترميز HTML، و600 مستوى من التداخل — إضافة إلى عينتين سليمتين تمامًا بحجوم متطابقة، لأن عبارة "أعطى لا شيء" لا تقول شيئًا عن العطب إذا كانت المكتبة صامتة أيضًا أمام مستند سليم بالحجم نفسه.
markdownify رمى خطأ في 1 من 14 ولم يُرجع شيئًا في 0، واستعاد 30/33 من العلامات المرجعية عبر العينات المعطوبة (malformed-results.json). وتُستثنى عينة واحدة من هذا العدّ: فوفق HTML5، كل ما يأتي بعد <script> غير مغلق يُعد محتوى script فعلًا، لذا فإن فقدانه هناك هو السلوك الصحيح، واستعادته هو الانحراف.
المزايا والعيوب
ما يميّزه. دقة الجداول مساوية لـ markitdown، وفق القاعدة نفسها. أقل عدد رموز بين الأربعة. MIT. حجمه 1.8 ميغابايت، وكلفته الإضافية تكاد تكون صفرًا إذا كان BeautifulSoup موجودًا أصلًا في مشروعك. أسرع بدء استيراد بارد عند 0.046 ثانية. تحديثات نشطة. يحافظ على الخانات الفارغة في الجداول. قابل للتجاوز على مستوى كل عنصر عبر MarkdownConverter. والاستدعاء الأحادي السطر هو الاختيار الصحيح.
ما يُؤخذ عليه. يعتمد على BeautifulSoup، لذلك حجمه على القرص أكبر بتسعة أضعاف من html2text إذا لم تكن تملكه أصلًا. و2,235 نجمة تعني مجتمعًا أصغر من turndown — وأمثلة عملية أقل عندما تواجه حالة غريبة. وهو مخصص لـ Python فقط، فلا يفيدك داخل حزمة Node. وهناك 42 مشكلة مفتوحة.
من ينبغي أن يستخدمه، ومن لا ينبغي
ابدأ بـ markdownify إذا كان عبء العمل لديك في Python ويشبه هذه العينات. فهو يطابق markitdown في عدد الصفوف الناتجة وفق هذا العداد، ويقع في مجموعة أقل عددًا من الرموز، وهو MIT. وإذا كنت تعتمد أصلًا على BeautifulSoup، فقد تكون كلفة التثبيت الإضافية صغيرة؛ تحقق من فرق الاعتماد الفعلي داخل بيئتك.
فكّر في html2text بدلًا منه إذا كانت ميزانية الاعتماد تُقاس بمئات الكيلوبايت، وكان شكل مخرجاته مناسبًا لصفحاتك. راجع GPL-3.0-or-later مع الجهة المسؤولة عن الترخيص قبل النشر.
فكّر في markitdown بدلًا منه إذا كنت تحول أصلًا ملفات PDF أو Office. مخرجات HTML هنا أظهرت نفس عدد صفوف Markdown الناتجة، لكن لم يُثبت أن الدقة الأوسع متكافئة.
تجاوزه في Node، حيث يكون turndown هو الخيار الطبيعي — مع تثبيت turndown-plugin-gfm معه، لأن النواة الأساسية لـ turndown لا تنتج جداول إطلاقًا.
أين تناسب واجهة API المُدارة
markdownify يحول HTML الموجود لديك أصلًا. لكنه لا يجلب الصفحات، ولا ينفذ JavaScript، ولا يتعامل مع طبقة مكافحة الروبوتات — ولا أي من المحولات الأربعة يفعل ذلك، وفي كثير من الأهداف الواقعية يكون هذا هو الجزء الأصعب من العمل.
وللاطلاع على نفس العينات عبر المحولات الخمسة كلها، راجع المقارنة الخماسية لمكتبات تحويل HTML إلى Markdown.
في Thunderbit، يتعامل خط المطورين لدينا عبر Thunderbit مع العمل السابق على التحويل: POST /distill يجلب عنوان URL ويعيد Markdown، بينما POST /extract يعيد JSON وفق مخطط محدد. كلاهما متاح عبر خادم MCP وواجهة CLI؛ أما التسعير ففي صفحة تسعير Thunderbit. هذه الواجهات المستضافة لم تُقارن بهذه المحولات المحلية، لذا فهذا فرق في الفئة لا مقارنة في الأداء.
والصياغة الصادقة هي: إذا كان لديك HTML وتريد Markdown، فإن markdownify مجاني ويؤدي المهمة بكفاءة تضاهي أي خيار هنا. أما إذا كنت تجلب الصفحات، أو أردت جداول بدل النص السردي، فهذه صفقة مختلفة.
ولمشهد أوسع، تغطي جولة أفضل واجهات Web Scraping APIs الخيارات المستضافة، وتغطي المقالة الأساسية عن أدوات الاستخراج مفتوحة المصدر الخيارات المستضافة ذاتيًا. أما تحويل HTML إلى Markdown في Python فهو الشرح العملي.
جرّب Thunderbit لاستخراج بيانات الويب
هل ينبغي استخدام markdownify؟
في Python، هو مرشح قوي عندما تشبه مدخلاتك هذه العينات؛ اختبره على جداولك وصفحات HTML المعطوبة قبل أن تجعله الخيار الافتراضي.
إنه يعادل markitdown في عدد صفوف الجداول الناتجة، ويقع في مجموعة أقل عددًا من الرموز، وهو الأسرع في بدء الاستيراد في هذه الجولة، كما أنه MIT. لكن في المقابل، استهلك ذاكرة أكثر من html2text، وهو مخصص لـ Python فقط، وسبب خطأ في إحدى عينات HTML المعطوبة. حجم القرص والترخيص عوامل اختيار إضافية، لا الحجج الوحيدة.
ما يلفت نظري هو عدد النجوم. turndown يحظى باهتمام أكبر بخمسة أضعاف، ومع ذلك، وبشكل افتراضي، لا يحول أيًا من الجداول التي يتعامل معها markdownify بشكل صحيح. الشعبية في هذه الفئة لا تعكس السلوك المقاس، وهذا في حد ذاته سبب كافٍ لإجراء هذه المقارنة.
جرّب Thunderbit لاستخراج بيانات الويب Get Started Free
الأسئلة الشائعة
هل markdownify جيد فعلًا مثل markitdown في التعامل مع HTML؟ في هذه العينات الأربع، أنتج كل منهما 36 صفًا معترفًا بها من جداول Markdown، واستعادا جميع السلاسل الـ 16 التي جرى التحقق منها، وبلغ الفارق في عدد المحارف 0.2% فقط. هذا ليس اختبارًا لتكافؤ الخلية أو العرض. كما أن markitdown يدعم أيضًا PDF وOffice، لذا فهذه المقارنة تخص فقط مخرجات HTML المقاسة هنا.
هل يحتاج إلى BeautifulSoup؟ نعم — فهو محلله، ومعظمه من الـ 1.8 ميغابايت. إذا كان مشروعك يستخدم BeautifulSoup أصلًا، فالكلفة الإضافية لـ markdownify صغيرة. وإن لم يكن كذلك، وكان الحجم مهمًا فعلًا، فإن html2text حجمه 0.2 ميغابايت مع حزمة واحدة فقط، لكن على حساب رخصة GPL-3.0-or-later.
كيف يتعامل مع الخانات الفارغة في الجداول؟ يُبقيها في مكانها. في العينة التي تحتوي على فجوات في البيانات، يحافظ الإخراج على الخانة الفارغة في موضعها، بحيث تبقى كل قيمة لاحقة في عمودها الصحيح. أما المحول الذي يتخطى الخانات الفارغة فيُنتج جدولًا يبدو صحيحًا بينما تكون كل قيمة مزاحة عمودًا إلى اليسار، وهذا أسوأ لأن لا شيء ينبّهك إليه.
هل أستخدم الدالة أم class؟
استخدم الدالة markdownify() في الحالة العامة. واستخدم MarkdownConverter عندما تحتاج إلى تجاوز طريقة تحويل عنصر محدد — إذ لم تُستخدم في كل القياسات هنا إلا الدالة العادية مع الخيارات الافتراضية.
ما الذي لم يُختبر هنا؟ أربع عينات مشتركة مع تشخيص منفصل يخص الجداول فقط لا تمثل corpus شاملًا. كما أن مجموعة HTML المعطوب المنفصلة غطت 12 نوعًا من الأعطال واسمين للتحكم، لكنها لم تغطِ كل أشكال HTML التالف. ولم تتم مقارنة القوائم المتداخلة، أو قوائم التعريف، أو الحواشي السفلية، أو الرياضيات، أو جولات التحويل ذهابًا وإيابًا بين Markdown وHTML، أو التكافؤ على مستوى الخلية، أو المتغيرات المختلفة للإعدادات. ولم تتم أيضًا مقارنة سرعة التحويل.


