استخراج النصوص من ملفات PDF باستخدام PDFMiner في Python

استخرج نصوص PDF ببراعة باستخدام Python

Page content

PDFMiner.six هو مكتبة قوية لغة Python لاستخراج النص، البيانات، والمعلومات المتعلقة بالهندسة من ملفات PDF.

على عكس قارئي PDF البسيطة، فإنه يقدم تحليلًا عميقًا للهيكل PDF ويتعامل مع التخطيطات المعقدة بشكل فعال.

استخراج النص من ملف PDF إلى markdown - رؤية IPAD

ما هو PDFMiner ولماذا استخدامه؟

PDFMiner هي مكتبة نقيّة لغة Python تم تصميمها لاستخراج وتحليل النص من ملفات PDF. النسخة .six هي الفرع النشط الذي يدعم Python 3.x، بينما المشروع الأصلي لـ PDFMiner لم يعد يتم تحديثه.

الميزات الرئيسية:

  • تنفيذ نقي لغة Python (بدون اعتماديات خارجية)
  • تحليل تخطيط مفصل وتحديد موقع النص
  • اكتشاف ترميز الخطوط والشخصيات
  • دعم الملفات المشفرة
  • أدوات سطر الأوامر مدرجة
  • بنية قابلة للتوسع لمعالجة مخصصة

PDFMiner مفيد بشكل خاص عندما تحتاج إلى التحكم الدقيق في استخراج النص، أو حفظ معلومات التخطيط، أو العمل مع وثائق معقدة متعددة الأعمدة. على الرغم من أن سرعته قد تكون أقل من بعض الخيارات الأخرى، فإن دقتها وقدرتها على التحليل التفصيلي تجعلها الخيار المفضل لخطوط معالجة الوثائق. إذا كنت ترغب في العملية العكسية، فقد تكون مهتمًا أيضًا بـ إنشاء ملفات PDF بشكل برمجي بلغة Python.

التثبيت والتهيئة

تثبيت PDFMiner.six باستخدام pip:

pip install pdfminer.six

لبيئات العمل الافتراضية (المُوصى بها):

python -m venv venv
source venv/bin/activate  # على Windows: venv\Scripts\activate
pip install pdfminer.six

إذا كنت جديدًا في إدارة حزم Python، فراجع دليل Python المختصر لمزيد من التفاصيل حول pip وبيئات العمل الافتراضية.

التحقق من التثبيت:

pdf2txt.py --version

تحتوي المكتبة على عدة أدوات سطر الأوامر:

  • pdf2txt.py - استخراج النص من ملفات PDF
  • dumppdf.py - عرض هيكل PDF الداخلي
  • latin2ascii.py - تحويل الأحرف اللاتينية إلى أحرف ASCII

هذه الأدوات تتكامل مع أدوات معالجة PDF الأخرى مثل Poppler التي توفر وظائف إضافية مثل استخراج الصفحات وتغيير التنسيق.

استخراج النص الأساسي

استخراج النص البسيط

الطريقة الأسهل لاستخراج النص من ملف PDF:

from pdfminer.high_level import extract_text

# استخراج جميع النصوص من ملف PDF
text = extract_text('document.pdf')
print(text)

تتعامل هذه واجهة برمجة التطبيقات العالية المستوى مع معظم الحالات الشائعة وتُرجع الوثيقة بأكملها كسلسلة نصية واحدة.

استخراج النص من صفحات محددة

للاستخراج من صفحات محددة:

from pdfminer.high_level import extract_text

# استخراج النص من الصفحات 2-5 (مُعدة من الصفر)
text = extract_text('document.pdf', page_numbers=[1, 2, 3, 4])
print(text)

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

استخراج النص مع تكرار الصفحات

للتعامل مع الصفحات على حدة:

from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainer

for page_layout in extract_pages('document.pdf'):
    for element in page_layout:
        if isinstance(element, LTTextContainer):
            print(element.get_text())

هذه الطريقة توفر لك أكثر تحكمًا في كيفية معالجة كل صفحة، وهي مفيدة عند العمل مع مستندات تتغير فيها هيكل الصفحة.

تحليل التخطيط المتقدم

فهم معلمات LAParams

تُحدد معلمات LAParams (التحليل المكاني للهندسة) كيفية تفسير PDFMiner لهيكل المستند. من المهم فهم الفرق بين PDFMiner ومكتبات بسيطة هنا - PDFMiner تحليل بالفعل العلاقات المكانية بين عناصر النص.

from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams

# إنشاء معلمات LAParams مخصصة
laparams = LAParams(
    line_overlap=0.5,      # أقل تداخل لخطوط النص
    char_margin=2.0,       # هامش الحرف
    line_margin=0.5,       # هامش الخط
    word_margin=0.1,       # عتبة فصل الكلمات
    boxes_flow=0.5,        # عتبة تدفق المربعات
    detect_vertical=True,  # اكتشاف النص العمودي
    all_texts=False        # استخراج النص فقط في المربعات
)

text = extract_text('document.pdf', laparams=laparams)

شرح المعلمات:

  • line_overlap: كمية التداخل الأفقية المطلوبة لخطوط النص لاعتبارها نفس الخط (0.0-1.0)
  • char_margin: الحد الأقصى لمسافة بين الحروف في نفس الكلمة (كجزء من عرض الحرف)
  • line_margin: الحد الأقصى لمسافة بين الخطوط في نفس الفقرة
  • word_margin: عتبة فصل الكلمات
  • boxes_flow: عتبة تدفق المربعات
  • detect_vertical: تمكين اكتشاف النص العمودي (شائع في اللغات الآسيوية)

استخراج معلومات التخطيط

احصل على معلومات تفصيلية حول الموقع والخطوط:

from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextBox, LTTextLine, LTChar

for page_layout in extract_pages('document.pdf'):
    for element in page_layout:
        if isinstance(element, LTTextBox):
            # احصل على إحداثيات مربع الحدود
            x0, y0, x1, y1 = element.bbox
            print(f"النص عند ({x0}, {y0}): {element.get_text()}")
            
            # تكرار عبر السطور
            for text_line in element:
                if isinstance(text_line, LTTextLine):
                    # احصل على تفاصيل مستوى الحرف
                    for char in text_line:
                        if isinstance(char, LTChar):
                            print(f"الحرف: {char.get_text()}, "
                                  f"الخط: {char.fontname}, "
                                  f"الحجم: {char.height}")

هذه التفاصيل تُعتبر مفيدة للغاية لتحليل المستندات، استخراج النماذج، أو عند الحاجة إلى فهم هيكل المستند بشكل برمجي.

التعامل مع أنواع PDF المختلفة

PDFs المشفرة

يمكن لـ PDFMiner التعامل مع ملفات PDF المحمية بكلمة مرور:

from pdfminer.high_level import extract_text

# استخراج من ملف PDF محمي بكلمة مرور
text = extract_text('encrypted.pdf', password='your_password')

ملاحظة: لا يمكن لـ PDFMiner تجاوز القيود الأمنية التي تمنع استخراج النص على مستوى PDF.

مستندات متعددة الأعمدة

للمستندات ذات الأعمدة المتعددة، قم بتخصيص LAParams:

from pdfminer.high_level import extract_text
from pdfminer.layout import LAParams

# تحسين لمستندات متعددة الأعمدة
laparams = LAParams(
    detect_vertical=False,
    line_margin=0.3,
    word_margin=0.1,
    boxes_flow=0.3  # قيمة منخفضة لتحسين اكتشاف الأعمدة
)

text = extract_text('multi_column.pdf', laparams=laparams)

-parameter boxes_flow مهم بشكل خاص لمستندات متعددة الأعمدة - القيم المنخفضة تساعد PDFMiner على التمييز بين الأعمدة المختلفة.

النص غير الإنجليزي والنصوص المُشفَّرة

PDFMiner تتعامل جيدًا مع Unicode، ولكن تأكد من الترميز الصحيح:

from pdfminer.high_level import extract_text

# استخراج النص مع دعم Unicode
text = extract_text('multilingual.pdf', codec='utf-8')

# حفظ إلى ملف بترميز UTF-8
with open('output.txt', 'w', encoding='utf-8') as f:
    f.write(text)

العمل مع PDFs الممسوحة

لا يمكن لـ PDFMiner استخراج النص من ملفات PDF الممسوحة (الصور) مباشرة. هذه تتطلب OCR (التعرف الضوئي على الحروف). ومع ذلك، يمكنك دمج PDFMiner مع أدوات OCR.

هنا كيفية الكشف عن ما إذا كان PDF ممسوحًا ويحتاج إلى OCR:

from pdfminer.high_level import extract_text
from pdfminer.high_level import extract_pages
from pdfminer.layout import LTFigure, LTImage

def is_scanned_pdf(pdf_path):
    """التحقق من ما إذا كان PDF يظهر كممسوح (يحتوي بشكل رئيسي على صور)"""
    text_count = 0
    image_count = 0
    
    for page_layout in extract_pages(pdf_path):
        for element in page_layout:
            if isinstance(element, (LTFigure, LTImage)):
                image_count += 1
            elif hasattr(element, 'get_text'):
                if element.get_text().strip():
                    text_count += 1
    
    # إذا كانت معظم الصور وعدد قليل من النصوص، فمن المحتمل أنها ممسوحة
    return image_count > text_count * 2

if is_scanned_pdf('document.pdf'):
    print("يبدو أن هذا PDF ممسوح - استخدم OCR")
else:
    text = extract_text('document.pdf')
    print(text)

للمستندات الممسوحة، تأكد من دمجها مع Tesseract OCR أو استخدام أدوات لاستخراج الصور من ملفات PDF أولاً، ثم تطبيق OCR على تلك الصور.

استخدام الأدوات من سطر الأوامر

تحتوي PDFMiner على أدوات قوية من سطر الأوامر:

استخراج النص باستخدام أدوات سطر الأوامر

# استخراج النص إلى stdout
pdf2txt.py document.pdf

# حفظ إلى ملف
pdf2txt.py -o output.txt document.pdf

# استخراج صفحات محددة
pdf2txt.py -p 1,2,3 document.pdf

# استخراج كـ HTML
pdf2txt.py -t html -o output.html document.pdf

خيارات متقدمة

# معلمات تخطيط مخصصة
pdf2txt.py -L 0.3 -W 0.1 document.pdf

# استخراج مع تحليل تخطيط تفصيلي (XML)
pdf2txt.py -t xml -o layout.xml document.pdf

# تعيين كلمة مرور لـ PDF المشفر
pdf2txt.py -P mypassword encrypted.pdf

هذه الأدوات من سطر الأوامر ممتازة للاختبار السريع، وскриبتات shell، ودمجها في مهام تلقائية.

تحسين الأداء

معالجة PDFs الكبيرة

للمستندات الكبيرة، تجرب هذه استراتيجيات التحسين:

from pdfminer.high_level import extract_pages
from pdfminer.layout import LAParams

# معالجة الصفحات المطلوبة فقط
def extract_page_range(pdf_path, start_page, end_page):
    text_content = []
    for i, page_layout in enumerate(extract_pages(pdf_path)):
        if i < start_page:
            continue
        if i >= end_page:
            break
        text_content.append(page_layout)
    return text_content

# تعطيل تحليل التخطيط لزيادة السرعة
from pdfminer.high_level import extract_text
text = extract_text('large.pdf', laparams=None)  # أسرع بكثير

معالجة دفعة

للمعالجة الفعالة لعدد كبير من PDFs:

from multiprocessing import Pool
from pdfminer.high_level import extract_text
import os

def process_pdf(pdf_path):
    """معالجة ملف PDF واحد"""
    try:
        text = extract_text(pdf_path)
        output_path = pdf_path.replace('.pdf', '.txt')
        with open(output_path, 'w', encoding='utf-8') as f:
            f.write(text)
        return f"تم معالجته: {pdf_path}"
    except Exception as e:
        return f"خطأ في معالجة {pdf_path}: {str(e)}"

# معالجة PDFs بالتوازي
def batch_process_pdfs(pdf_directory, num_workers=4):
    pdf_files = [os.path.join(pdf_directory, f) 
                 for f in os.listdir(pdf_directory) 
                 if f.endswith('.pdf')]
    
    with Pool(num_workers) as pool:
        results = pool.map(process_pdf, pdf_files)
    
    for result in results:
        print(result)

# الاستخدام
batch_process_pdfs('/path/to/pdfs', num_workers=4)

المشاكل الشائعة والحلول

المشكلة: ترتيب النص غير صحيح

المشكلة: النص المستخرج يبدو مزجًا أو خارج الترتيب.

الحل: تعديل معلمات LAParams، خاصة boxes_flow:

from pdfminer.layout import LAParams
laparams = LAParams(boxes_flow=0.3)  # جرّب قيمًا مختلفة
text = extract_text('document.pdf', laparams=laparams)

المشكلة: غياب الفراغات بين الكلمات

المشكلة: تلتصق الكلمات معًا دون فراغات.

الحل: زيادة word_margin:

laparams = LAParams(word_margin=0.2)  # زيادة من القيمة الافتراضية 0.1
text = extract_text('document.pdf', laparams=laparams)

المشكلة: أخطاء الترميز

المشكلة: أحرف غريبة أو أخطاء ترميز.

الحل: تحديد الترميز صراحة:

text = extract_text('document.pdf', codec='utf-8')

المشكلة: أخطاء الذاكرة مع PDFs الكبيرة

المشكلة: أخطاء نقص الذاكرة مع الملفات الكبيرة.

الحل: معالجة الصفحة تلو الأخرى:

def extract_text_chunked(pdf_path, chunk_size=10):
    """استخراج النص بالقطع لتقليل استخدام الذاكرة"""
    all_text = []
    page_count = 0
    
    for page_layout in extract_pages(pdf_path):
        page_text = []
        for element in page_layout:
            if hasattr(element, 'get_text'):
                page_text.append(element.get_text())
        
        all_text.append(''.join(page_text))
        page_count += 1
        
        # معالجة بالقطع
        if page_count % chunk_size == 0:
            yield ''.join(all_text)
            all_text = []
    
    # إخراج النص المتبقي
    if all_text:
        yield ''.join(all_text)

مقارنة PDFMiner بالبدائل

فهم متى استخدام PDFMiner مقابل مكتبات أخرى مهم:

PDFMiner مقابل PyPDF2

PyPDF2 أبسط وأسرع ولكن أقل دقة:

  • استخدم PyPDF2 لـ: PDFs البسيطة، استخراج سريع، دمج أو تقسيم PDFs
  • استخدم PDFMiner لـ: تخطيط معقد، تحديد موقع النص بدقة، تحليل تفصيلي

PDFMiner مقابل pdfplumber

pdfplumber يبني على PDFMiner مع واجهة برمجة تطبيقات مستوى أعلى:

  • استخدم pdfplumber لـ: استخراج الجداول، واجهة برمجة تطبيقات بسيطة، مشاريع برمجية سريعة
  • استخدم PDFMiner لـ: التحكم الأقصى، معالجة مخصصة، أنظمة إنتاج

PDFMiner مقابل PyMuPDF (fitz)

PyMuPDF أسرع بكثير ولكن له اعتماديات C:

  • استخدم PyMuPDF لـ: التطبيقات ذات الأولوية في الأداء، معالجة كمية كبيرة
  • استخدم PDFMiner لـ: متطلبات نقيّة لغة Python، تحليل تخطيط تفصيلي

مثال عملي: استخراج وتحليل المستند

هنا مثال كامل يستخرج النص ويقدم تحليل المستند:

from pdfminer.high_level import extract_pages, extract_text
from pdfminer.layout import LTTextBox, LTChar
from collections import Counter
import re

def analyze_pdf(pdf_path):
    """استخراج النص وتقديم تحليل المستند"""
    
    # استخراج النص الكامل
    full_text = extract_text(pdf_path)
    
    # الإحصائيات
    stats = {
        'total_chars': len(full_text),
        'total_words': len(full_text.split()),
        'total_lines': full_text.count('\n'),
        'fonts': Counter(),
        'font_sizes': Counter(),
        'pages': 0
    }
    
    # تحليل تفصيلي
    for page_layout in extract_pages(pdf_path):
        stats['pages'] += 1
        
        for element in page_layout:
            if isinstance(element, LTTextBox):
                for line in element:
                    for char in line:
                        if isinstance(char, LTChar):
                            stats['fonts'][char.fontname] += 1
                            stats['font_sizes'][round(char.height, 1)] += 1
    
    return {
        'text': full_text,
        'stats': stats,
        'most_common_font': stats['fonts'].most_common(1)[0] if stats['fonts'] else None,
        'most_common_size': stats['font_sizes'].most_common(1)[0] if stats['font_sizes'] else None
    }

# الاستخدام
result = analyze_pdf('document.pdf')
print(f"الصفحات: {result['stats']['pages']}")
print(f"الكلمات: {result['stats']['total_words']}")
print(f"الحروف الرئيسية: {result['most_common_font']}")
print(f"الحجم الرئيسي: {result['most_common_size']}")

دمجها مع خطوط معالجة المستندات

تعمل PDFMiner بشكل جيد في خطوط معالجة المستندات الأكبر. على سبيل المثال، عند بناء أنظمة RAG (Retrieval-Augmented Generation) أو حلول إدارة المستندات، قد تدمجها مع أدوات برمجية أخرى لخط أنظمة كاملة.

بعد استخراج النص من ملفات PDF، غالبًا ما تحتاج إلى تحويله إلى تنسيقات أخرى. يمكنك تحويل محتوى HTML إلى markdown باستخدام مكتبات Python أو حتى الاستفادة من تحويل مدعوم بالـ LLM مع Ollama لتحويل المستندات بذكاء. هذه التقنيات مفيدة بشكل خاص عندما ينتج استخراج PDF محتوى HTML مهيكل يحتاج إلى تنظيف و إعادة تشكيل.

لخطوط تحويل المستندات الشاملة، قد تحتاج أيضًا إلى التعامل مع تحويل مستندات Word إلى markdown، مما يخلق خط عمل موحد يعالج تنسيقات المستندات المختلفة إلى تنسيق إخراج مشترك.

أفضل الممارسات

  1. استخدم دائمًا LAParams للمستندات المعقدة - تعمل الإعدادات الافتراضية على المستندات البسيطة، ولكن تعديل LAParams يحسن النتائج بشكل كبير للمستندات المعقدة.

  2. ابدأ بتجربة صفحات نموذجية أولًا - قبل معالجة دفعات كبيرة، اختبر إعدادات الاستخراج الخاصة بك على عينات تمثيلية.

  3. التعامل مع الاستثناءات بسلاسة - يمكن أن تكون الملفات PDF معطوبة أو غير صحيحة. قم دائمًا بحظر كود الاستخراج في كتل try-except.

  4. احفظ النص المستخرج - لتجنب إعادة المعالجة، احفظ النص المستخرج عند المعالجة المتكررة.

  5. تحقق من جودة النص المستخرج - قم بتنفيذ فحوصات لضمان جودة الاستخراج (مثل طول النص الأدنى، الكلمات الرئيسية المتوقعة).

  6. اعتبر البدائل لحالات الاستخدام المحددة - بينما PDFMiner قوي، فإن الأدوات المتخصصة (مثل tabula-py للجداول) قد تكون مناسبة أحيانًا.

  7. حافظ على تحديث PDFMiner - الفرع .six مُدار نشطًا. حافظ على تحديثه للحصول على إصلاحات الأخطاء والتحسينات.

  8. وثّق كودك بشكل صحيح - عند مشاركة نسخ برمجية لاستخراج PDF، استخدم كُتل markdown بشكل صحيح مع تلوين الترميز لتحسين قابلية القراءة.

الخاتمة

PDFMiner.six هي أداة ضرورية للمطورين بلغة Python الذين يعملون مع ملفات PDF. تنفيذها النقي لغة Python، تحليلها التفصيلي للهندسة، وبنية تطويرها القابلة للتوسع يجعلها مناسبة تمامًا لأنظمة معالجة المستندات في الإنتاج. على الرغم من أن لها منحنى تعليمي أصعب من المكتبات البسيطة، فإن الدقة والتحكم الذي توفرهما لا يمكن مقارنتها في مهام استخراج PDF المعقدة.

سواء كنت تبني نظام إدارة مستندات، تحليل ورق بحثية، أو استخراج البيانات لخطوط أنظمة تعلم الآلة، فإن PDFMiner توفر الأساس لاستخراج النص من ملفات PDF بشكل موثوق بلغة Python.

الموارد المرتبطة

المقالات المرتبطة بهذا الموقع

المراجع الخارجية