是否有可能,使用Python,合并单独的PDF文件?

假设是这样,我需要进一步扩展它。我希望循环通过目录中的文件夹,并重复此过程。

我可能是得过其实了,但是否可以排除每个pdf文件中包含的一页(我的报告生成总是创建一个额外的空白页)。


当前回答

下面是针对我的特定用例的最常见答案的时间比较:合并5个大单页pdf文件的列表。每个测试我都运行了两次。

(免责声明:我在Flask中运行这个函数,您的里程可能会有所不同)

博士TL;

pdfrw是我测试的3个pdf文件组合库中最快的一个。

PyPDF2

start = time.time()
merger = PdfFileMerger()
for pdf in all_pdf_obj:
    merger.append(
        os.path.join(
            os.getcwd(), pdf.filename # full path
                )
            )
formatted_name = f'Summary_Invoice_{date.today()}.pdf'
merge_file = os.path.join(os.getcwd(), formatted_name)
merger.write(merge_file)
merger.close()
end = time.time()
print(end - start) #1 66.50084733963013 #2 68.2995400428772

PyMuPDF

start = time.time()
result = fitz.open()

for pdf in all_pdf_obj:
    with fitz.open(os.path.join(os.getcwd(), pdf.filename)) as mfile:
        result.insertPDF(mfile)
formatted_name = f'Summary_Invoice_{date.today()}.pdf'

result.save(formatted_name)
end = time.time()
print(end - start) #1 2.7166640758514404 #2 1.694727897644043

PDFrw

start = time.time()
result = fitz.open()

writer = PdfWriter()
for pdf in all_pdf_obj:
    writer.addpages(PdfReader(os.path.join(os.getcwd(), pdf.filename)).pages)

formatted_name = f'Summary_Invoice_{date.today()}.pdf'
writer.write(formatted_name)
end = time.time()
print(end - start) #1 0.6040127277374268 #2 0.9576816558837891

其他回答

使用Pypdf或其后续版本PyPDF2:

作为PDF工具包构建的Pure-Python库。它能够: 逐页拆分文档, 逐页合并文件,

(以及更多)

下面是一个适用于这两个版本的示例程序。

#!/usr/bin/env python
import sys
try:
    from PyPDF2 import PdfFileReader, PdfFileWriter
except ImportError:
    from pyPdf import PdfFileReader, PdfFileWriter

def pdf_cat(input_files, output_stream):
    input_streams = []
    try:
        # First open all the files, then produce the output file, and
        # finally close the input files. This is necessary because
        # the data isn't read from the input files until the write
        # operation. Thanks to
        # https://stackoverflow.com/questions/6773631/problem-with-closing-python-pypdf-writing-getting-a-valueerror-i-o-operation/6773733#6773733
        for input_file in input_files:
            input_streams.append(open(input_file, 'rb'))
        writer = PdfFileWriter()
        for reader in map(PdfFileReader, input_streams):
            for n in range(reader.getNumPages()):
                writer.addPage(reader.getPage(n))
        writer.write(output_stream)
    finally:
        for f in input_streams:
            f.close()
        output_stream.close()

if __name__ == '__main__':
    if sys.platform == "win32":
        import os, msvcrt
        msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
    pdf_cat(sys.argv[1:], sys.stdout)

http://pieceofpy.com/2009/03/05/concatenating-pdf-with-python/提供了一个解决方案。

类似的:

from pyPdf import PdfFileWriter, PdfFileReader

def append_pdf(input,output):
    [output.addPage(input.getPage(page_num)) for page_num in range(input.numPages)]

output = PdfFileWriter()

append_pdf(PdfFileReader(file("C:\\sample.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample1.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample2.pdf","rb")),output)
append_pdf(PdfFileReader(file("c:\\sample3.pdf","rb")),output)

output.write(file("c:\\combined.pdf","wb"))

------ 11月25日更新------

------似乎以上代码不再工作------

------请使用以下:------

from PyPDF2 import PdfFileMerger, PdfFileReader
import os

merger = PdfFileMerger()

file_folder = "C:\\My Ducoments\\"

root, dirs, files = next(os.walk(file_folder))

for path, subdirs, files in os.walk(root):
    for f in files:
        if f.endswith(".pdf"):
            merger.append(file_folder + f)

merger.write(file_folder + "Economists-1.pdf")

使用字典以获得更大的灵活性(例如sort, dedup):

import os
from PyPDF2 import PdfFileMerger
# use dict to sort by filepath or filename
file_dict = {}
for subdir, dirs, files in os.walk("<dir>"):
    for file in files:
        filepath = subdir + os.sep + file
        # you can have multiple endswith
        if filepath.endswith((".pdf", ".PDF")):
            file_dict[file] = filepath
# use strict = False to ignore PdfReadError: Illegal character error
merger = PdfFileMerger(strict=False)

for k, v in file_dict.items():
    print(k, v)
    merger.append(v)

merger.write("combined_result.pdf")

Giovanni G. PY以一种简单易用的方式(至少对我来说)给出了答案:

import os
from PyPDF2 import PdfFileMerger

def merge_pdfs(export_dir, input_dir, folder):
    current_dir = os.path.join(input_dir, folder)
    pdfs = os.listdir(current_dir)
    
    merger = PdfFileMerger()
    for pdf in pdfs:
        merger.append(open(os.path.join(current_dir, pdf), 'rb'))

    with open(os.path.join(export_dir, folder + ".pdf"), "wb") as fout:
        merger.write(fout)

export_dir = r"E:\Output"
input_dir = r"E:\Input"
folders = os.listdir(input_dir)
[merge_pdfs(export_dir, input_dir, folder) for folder in folders];

您也可以使用pikepdf(源代码文档)。

示例代码可以是(摘自文档):

from glob import glob

from pikepdf import Pdf

pdf = Pdf.new()

for file in glob('*.pdf'):  # you can change this to browse directories recursively
    with Pdf.open(file) as src:
        pdf.pages.extend(src.pages)

pdf.save('merged.pdf')
pdf.close()

如果想要排除页面,可以采用另一种方法,例如将页面复制到新的pdf中(然后,您可以选择不复制哪些页面。Pages对象的行为类似于一个列表)。

它仍然被积极维护,截至2022年2月,PyPDF2和pdfrw似乎都不是这种情况。

我还没有对它进行基准测试,所以我不知道它比其他解决方案更快还是更慢。

在我的例子中,与PyMuPDF相比的一个优点是有一个官方的Ubuntu包可用(python3-pikepdf),可以根据它来打包我自己的软件。