دمج ملفات PDF باستخدام بايثون في ملف واحد | بايثون بالمثال 28

دمج ملفات PDF باستخدام بايثون في ملف واحد | بايثون بالمثال

هل لديك عدة ملفات PDF وتريد جمعها في ملف واحد دون فتح كل ملف يدويًا؟ في هذا الدرس سنكتب كود دمج ملفات PDF باستخدام بايثون، ثم نحوله إلى سكربت جاهز يدمج أي عدد من الملفات مع الحفاظ على الترتيب الذي تختاره.

في الدرس 28 من سلسلة «بايثون بالمثال» سنتعلم الدمج باستخدام مكتبة pypdf، ودمج جميع ملفات مجلد تلقائيًا، واختيار صفحات محددة من ملفات مختلفة، ثم اختبار ملف PDF الناتج. نرفق أيضًا ملفات PDF تجريبية وملفات المشروع لتجربة جميع الأمثلة.

{alertInfo}المتطلبات: Python 3.9 أو أحدث، وتثبيت مكتبة pypdf. يعمل الدمج محليًا على جهازك، ولا يتطلب رفع الملفات إلى الإنترنت. المكتبة لا تنفذ OCR ولا تعالج PDF التالف تلقائيًا.

تحميل مشروع دمج ملفات PDF ببايثون

حمّل الكود الكامل وملفات PDF التجريبية والاختبارات، وجرّب دمج الملفات خطوة بخطوة.

تحميل ملفات الدرس ZIP

{getToc} $title={محتوى المقال}


ماذا سننجز بهذا الكود؟

لنفترض أن لدينا ثلاثة مستندات: مقدمة من صفحتين، وتقرير من ثلاث صفحات، وملاحظات من صفحة واحدة. سننشئ ملف merged.pdf يحتوي 6 صفحات بالترتيب الذي نحدده، دون تحويل الصفحات إلى صور.

samples/
├── 01-intro.pdf       (صفحتان)
├── 02-report.pdf      (3 صفحات)
└── 03-notes.pdf       (صفحة)

output/
└── merged.pdf         (6 صفحات)

سنستخدم PdfWriter؛ فهو يوفر الدالة append() لإضافة صفحات ملف PDF إلى نهاية الناتج. وتدعم الدالة اختيار نطاقات أو قائمة صفحات محددة وفق التوثيق الرسمي لمكتبة pypdf.

خطوات دمج ملفات PDF بمكتبة pypdf | بايثون بالمثال

تثبيت مكتبة pypdf

افتح الطرفية داخل مجلد جديد للمشروع، ثم نفّذ:

python -m pip install pypdf

للتأكد من التثبيت في بيئة بايثون نفسها التي ستشغّل بها الأكواد:

python -m pip show pypdf

نستخدم اسم المكتبة pypdf في الكود، وليس PyPDF2 الذي قد تجده في شروحات أقدم.

أبسط كود لدمج ملفين PDF باستخدام بايثون

ضع ملفي 01-intro.pdf و02-report.pdf في مجلد samples. أنشئ ملفًا باسم first_merge.py واكتب:

from pypdf import PdfWriter

writer = PdfWriter()

try:
    writer.append("samples/01-intro.pdf")
    writer.append("samples/02-report.pdf")
    writer.write("merged.pdf")
finally:
    writer.close()

print("تم إنشاء merged.pdf")

شغّل الملف:

python first_merge.py

سيظهر merged.pdf داخل مجلد التشغيل. يحتوي الناتج على خمس صفحات: صفحتان من الملف الأول، تليهما ثلاث صفحات من الثاني.

{alertSuccess}هذا هو أقصر مسار عملي: PdfWriter() ثم append() بالترتيب الذي تريده، ثم write() لحفظ الناتج.

كيف يعمل كود الدمج سطرًا بسطر؟

السطروظيفته
from pypdf import PdfWriterاستيراد الأداة التي تنشئ ملف PDF الناتج.
writer = PdfWriter()إنشاء كائن يحتفظ بالصفحات التي سنضيفها.
writer.append(...)إضافة كل صفحات الملف المحدد إلى نهاية الناتج.
writer.write(...)حفظ ملف PDF بعد الدمج.
writer.close()إغلاق موارد الكاتب.

دمج عدة ملفات PDF بالترتيب الذي نختاره

إذا أردت دمج ثلاثة ملفات أو أكثر، ضع المسارات في قائمة، ثم مرّ عليها باستخدام حلقة for؛ الترتيب في القائمة هو ترتيب الصفحات في الناتج.

from pypdf import PdfWriter

files = [
    "samples/01-intro.pdf",
    "samples/02-report.pdf",
    "samples/03-notes.pdf",
]

writer = PdfWriter()
try:
    for file_path in files:
        writer.append(file_path)
    writer.write("all-documents.pdf")
finally:
    writer.close()

print("تم الدمج بالترتيب المحدد")

إذا بدّلت موضع 03-notes.pdf في القائمة، فستنتقل صفحته إلى الموضع الجديد عند الدمج.

دمج جميع ملفات PDF داخل مجلد تلقائيًا

عندما تكون لديك عشرات الملفات، يمكننا العثور عليها باستخدام pathlib بدل إدخال كل اسم يدويًا. هنا نستخدم ترتيب الأسماء الطبيعي حتى يأتي file2.pdf قبل file10.pdf، ولا نضيف ملفات موجودة داخل مجلدات فرعية.

from pathlib import Path
from pypdf import PdfWriter

folder = Path("samples")
output = Path("output/merged-folder.pdf")
output.parent.mkdir(parents=True, exist_ok=True)

pdf_files = sorted(
    (path for path in folder.iterdir()
     if path.is_file() and path.suffix.lower() == ".pdf"
     and path.resolve() != output.resolve()),
    key=lambda path: path.name.lower()
)

if not pdf_files:
    raise SystemExit("لا توجد ملفات PDF للدمج")

writer = PdfWriter()
try:
    for path in pdf_files:
        writer.append(path)
    writer.write(output)
finally:
    writer.close()

print(f"تم دمج {len(pdf_files)} ملفات")

هذا المثال المختصر يستخدم الترتيب الأبجدي بواسطة sorted(). أما مشروع ZIP فيستخدم ترتيبًا طبيعيًا للأرقام؛ فمثلًا file2.pdf يسبق file10.pdf. كذلك ترفض أداة المشروع استبدال ملف موجود إلا بموافقتك.

ترتيب ملفات PDF قبل الدمج باستخدام بايثون | بايثون بالمثال

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

قد تحتاج إلى الصفحتين الأولى والثانية من ملف، ثم الصفحة الثالثة من ملف آخر، بدل دمج جميع الصفحات. تستطيع استخدام الوسيط pages؛ في مكتبة pypdf يبدأ ترقيم الصفحات برمجيًا من 0.

from pypdf import PdfWriter

writer = PdfWriter()
try:
    # أول صفحتين من الملف الأول (الفهارس 0 و1)
    writer.append("samples/01-intro.pdf", pages=(0, 2))

    # الصفحة الثالثة فقط من الملف الثاني (فهرس 2)
    writer.append("samples/02-report.pdf", pages=[2])

    writer.write("selected-pages.pdf")
finally:
    writer.close()

الناتج هنا يحتوي ثلاث صفحات. pages=(0, 2) يعني الفهارس من 0 حتى قبل 2؛ أي الصفحتين الأولى والثانية، بينما pages=[2] تعني الصفحة الثالثة وحدها.

دمج صفحات PDF محددة باستخدام بايثون | بايثون بالمثال

مشروع الدرس: أداة دمج PDF من سطر الأوامر

بدل تعديل ملفات Python لكل عملية، أعددنا سكربت pdf_merge.py يعتمد على argparse، ويقدم ثلاثة أوامر واضحة: files وfolder وpages.

بنية ملفات المشروع

python-merge-pdf-example-28/
├── pdf_merge.py
├── pdf_merge_core.py
├── requirements.txt
├── README.md
├── samples/
│   ├── 01-intro.pdf
│   ├── 02-report.pdf
│   └── 03-notes.pdf
├── output/
│   ├── merged-example.pdf
│   └── selected-pages-example.pdf
└── tests/
    └── test_pdf_merge.py

يمثل pdf_merge_core.py المنطق الفعلي لقراءة الملفات ودمجها والتحقق من الصفحات، بينما ينظّم pdf_merge.py أوامر التشغيل. سنعرض كود واجهة الأوامر كاملًا، وستجد ملف المنطق الكامل داخل الحزمة.

الكود الكامل لملف pdf_merge.py

"""CLI: merge specific PDFs, a whole folder, or selected pages."""
import argparse
import sys
from pathlib import Path

from pdf_merge_core import merge_pdf_files, pdfs_in_folder


def main(argv=None):
    parser = argparse.ArgumentParser(
        description="دمج ملفات PDF محليًا باستخدام بايثون وpypdf"
    )
    commands = parser.add_subparsers(dest="command", required=True)

    files_cmd = commands.add_parser("files", help="دمج ملفات بترتيب كتابتها")
    files_cmd.add_argument("files", nargs="+", help="مسارات ملفات PDF")
    files_cmd.add_argument("--output", required=True)
    files_cmd.add_argument("--overwrite", action="store_true")

    folder_cmd = commands.add_parser("folder", help="دمج كل PDF في مجلد واحد")
    folder_cmd.add_argument("folder", help="مجلد ملفات PDF")
    folder_cmd.add_argument("--output", required=True)
    folder_cmd.add_argument("--overwrite", action="store_true")

    parts_cmd = commands.add_parser("pages", help="دمج صفحات مختارة من ملفات مختلفة")
    parts_cmd.add_argument("--part", action="append", required=True,
                           help='مسار:صفحات مثل samples/01-intro.pdf:1-2')
    parts_cmd.add_argument("--output", required=True)
    parts_cmd.add_argument("--overwrite", action="store_true")

    args = parser.parse_args(argv)
    try:
        if args.command == "files":
            sources = [(Path(file), None) for file in args.files]
        elif args.command == "folder":
            sources = [(path, None) for path in pdfs_in_folder(args.folder, args.output)]
        else:
            sources = []
            for part in args.part:
                filename, sep, pages = part.rpartition(":")
                if not sep or not filename.strip() or not pages.strip():
                    raise ValueError(f"استخدم المسار:الصفحات، مثل file.pdf:1-2: {part}")
                sources.append((Path(filename), pages))

        count = merge_pdf_files(sources, args.output, overwrite=args.overwrite)
        print(f"تم دمج {len(sources)} مصدر/جزء في {args.output} — {count} صفحات")
        return 0
    except (OSError, ValueError) as error:
        print(f"خطأ: {error}", file=sys.stderr)
        return 1


if __name__ == "__main__":
    raise SystemExit(main())

أوامر جاهزة لتشغيل مشروع دمج PDF

بعد تحميل ZIP وفك ضغطه، افتح الطرفية داخل مجلد المشروع، ثم ثبّت المكتبة وشغّل الأمر المناسب:

python -m pip install -r requirements.txt

دمج ملفين بالترتيب المحدد

python pdf_merge.py files samples/01-intro.pdf samples/02-report.pdf --output output/merged.pdf

دمج جميع الملفات داخل samples

python pdf_merge.py folder samples --output output/all-documents.pdf

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

python pdf_merge.py pages --part "samples/01-intro.pdf:2" --part "samples/02-report.pdf:1-2" --output output/selected.pdf

لاحظ أن أرقام الصفحات في أمر pages تبدأ من 1 لتكون أسهل للمستخدم، وأن 1-2 تعني الصفحتين الأولى والثانية. على عكس مثال pages=(0, 2) البرمجي الذي يستخدم فهارس تبدأ من صفر.

يرفض البرنامج استبدال ملف الناتج إن كان موجودًا بالفعل، ويمكن إضافة --overwrite فقط عندما تريد الاستبدال صراحة. كما يحمي ملفات المصدر من الكتابة فوقها ويحفظ الناتج بعد اكتمال الدمج.

التحقق من عدد صفحات ملف PDF الناتج

من الأفضل ألا نعتمد على ظهور رسالة «تم» فقط؛ بل نفتح الناتج ونتأكد من عدد صفحاته باستخدام PdfReader:

from pypdf import PdfReader

reader = PdfReader("output/merged.pdf")
print("عدد الصفحات:", len(reader.pages))

إذا دمجنا ملف المقدمة المكوّن من صفحتين مع التقرير المكوّن من ثلاث صفحات، فالناتج المتوقع هو عدد الصفحات: 5. ويمكنك فتح الملف يدويًا لمراجعة الترتيب ومظهر الصفحات.

الأخطاء الشائعة عند دمج ملفات PDF باستخدام بايثون

المشكلةالسبب المحتمل والحل
No module named pypdfنفّذ python -m pip install pypdf بالمفسر الذي تستخدمه للتشغيل.
ImportError: PdfMergerتعتمد شروحات قديمة على PdfMerger؛ استخدم PdfWriter وappend().
FileNotFoundErrorراجع اسم الملف ومجلد التشغيل، وضع المسار بين علامتي اقتباس إذا احتوى مسافات.
الناتج بترتيب غير صحيححدد ترتيب القائمة يدويًا أو استخدم أسماء مرقمة وترتيبًا طبيعيًا.
الملف مشفرلن تستطيع دمجه بهذه الأداة قبل إزالة التشفير بصورة مشروعة.
ملف PDF تالف أو غير قابل للقراءةتحقق من الملف الأصلي أولًا، فالدمج لا يصلح الملفات التالفة.
الملف الناتج موجود بالفعلاستخدم اسمًا آخر؛ أو --overwrite عند قصد الاستبدال.
النماذج التفاعلية بها حقول متكررةقد تتداخل أسماء حقول PDF، لذا راجع النماذج المدمجة قبل استخدامها.

هل تقل جودة صفحات PDF بعد الدمج؟

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

{alertWarning}تنبيه خصوصية: الدمج في هذا الدرس محلي، ولا يرفع المستندات إلى موقع خارجي. احتفظ بنسخة من ملفاتك الأصلية ولا تُنفّذ سكربتات غير موثوقة على مستندات خاصة.

للتوسع في الأتمتة، يمكنك قراءة تنظيم الملفات حسب الامتداد باستخدام بايثون، أو درس دمج ملفات Excel متعددة باستخدام Pandas. وهما يعالجان مهام مختلفة عن دمج صفحات PDF.

الأسئلة الشائعة عن دمج PDF في بايثون

هل يمكن دمج ملفات PDF في بايثون دون إنترنت؟

نعم، بعد تثبيت مكتبة pypdf يتم الدمج محليًا، دون رفع المستندات إلى خدمة ويب.

ما الفرق بين pypdf وPyPDF2؟

pypdf هو الاسم الحديث للمشروع الذي كان يُعرف أيضًا باسم PyPDF2. استخدم from pypdf import PdfWriter في الأمثلة الحالية.

كيف أدمج عشرات ملفات PDF تلقائيًا؟

ضعها داخل مجلد، ثم استخدم أمر folder. يُرتب مشروعنا الملفات ترتيبًا طبيعيًا بحسب الأسماء.

هل يمكن دمج صفحة واحدة فقط من كل ملف PDF؟

نعم، باستخدام pages يمكنك تحديد صفحات معينة من كل ملف. في مشروعنا استخدم مثلًا --part "file.pdf:2" للصفحة الثانية.

هل يمكن دمج PDF محمي بكلمة مرور؟

تحتاج أولًا إلى إذن الوصول ووسيلة صحيحة لفك تشفير المستند. المثال يرفض المستندات المشفرة بدل التخمين أو تجاوز الحماية.

هل يمكن دمج PDF دون فقدان جودة الصور؟

لا يحول الكود الصفحات إلى صور، لكنه لا يضمن بقاء كل خاصية متقدمة كما هي، لذا تحقق من الناتج بعد الدمج.

إرسال تعليق

أحدث أقدم