合并PDF文件

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

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

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

256513 次浏览

使用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)

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

是的。

下面的例子将一个文件夹中的所有文件合并为一个新的PDF文件:

#!/usr/bin/env python
# -*- coding: utf-8 -*-


from argparse import ArgumentParser
from glob import glob
from pyPdf import PdfFileReader, PdfFileWriter
import os


def merge(path, output_filename):
output = PdfFileWriter()


for pdffile in glob(path + os.sep + '*.pdf'):
if pdffile == output_filename:
continue
print("Parse '%s'" % pdffile)
document = PdfFileReader(open(pdffile, 'rb'))
for i in range(document.getNumPages()):
output.addPage(document.getPage(i))


print("Start writing '%s'" % output_filename)
with open(output_filename, "wb") as f:
output.write(f)


if __name__ == "__main__":
parser = ArgumentParser()


# Add more options if you like
parser.add_argument("-o", "--output",
dest="output_filename",
default="merged.pdf",
help="write merged PDF to FILE",
metavar="FILE")
parser.add_argument("-p", "--path",
dest="path",
default=".",
help="path of source PDF files")


args = parser.parse_args()
merge(args.path, args.output_filename)

这里,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")

你可以使用__abc1 PdfMerger类。

文件连接

你可以通过使用append方法简单地连接文件。

from PyPDF2 import PdfMerger


pdfs = ['file1.pdf', 'file2.pdf', 'file3.pdf', 'file4.pdf']


merger = PdfMerger()


for pdf in pdfs:
merger.append(pdf)


merger.write("result.pdf")
merger.close()

如果需要,可以传递文件句柄而不是文件路径。

文件合并

如果你想对合并进行更细粒度的控制,有PdfMergermerge方法,它允许你在输出文件中指定插入点,这意味着你可以在文件的任何位置插入页面。append方法可以被认为是一个merge,其中插入点是文件的末尾。

如。

merger.merge(2, pdf)

在这里,我们将整个pdf插入到输出中,但在第2页。

页面范围

如果你想控制从特定文件中追加哪些页面,你可以使用appendmergepages关键字参数,传递一个形式为(start, stop[, step])的元组(类似于常规的range函数)。

如。

merger.append(pdf, pages=(0, 3))    # first 3 pages
merger.append(pdf, pages=(0, 6, 2)) # pages 1,3, 5

如果你指定了一个无效的范围,你将得到一个IndexError

注意:还指出,为了避免文件处于打开状态,应该在写入合并文件时调用PdfFileMergers close方法。这确保及时关闭所有文件(输入和输出)。遗憾的是,PdfFileMerger没有作为上下文管理器实现,因此我们可以使用with关键字,避免显式的关闭调用,并获得一些简单的异常安全。

你可能还想看看作为pypdf2一部分提供的pdfcat脚本。您可以潜在地避免完全编写代码的需要。

PyPdf2 github还包括一些示例代码演示合并。

PyMuPdf

另一个值得一看的库是PyMuPdf。合并同样简单。

从命令行:

python -m fitz join -o result.pdf file1.pdf file2.pdf file3.pdf

从代码中

import fitz


result = fitz.open()


for pdf in ['file1.pdf', 'file2.pdf', 'file3.pdf']:
with fitz.open(pdf) as mfile:
result.insertPDF(mfile)
    

result.save("result.pdf")

有大量的选项,详细在项目维基

pdfrw图书馆可以很容易地做到这一点,假设你不需要保存书签和注释,并且你的pdf文件没有加密。cat.py是一个示例拼接脚本,subset.py是一个示例页面子集脚本。

串联脚本的相关部分——假设inputs是一个输入文件名列表,而outfn是一个输出文件名:

from pdfrw import PdfReader, PdfWriter


writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)

正如你所看到的,省略最后一页是很容易的,例如:

    writer.addpages(PdfReader(inpfn).pages[:-1])

声明:我是pdfrw的主要作者。

合并目录下的所有pdf文件

把pdf文件放到目录下。启动程序。你会得到一个合并了所有pdf文件的pdf。

import os
from PyPDF2 import PdfFileMerger


x = [a for a in os.listdir() if a.endswith(".pdf")]


merger = PdfFileMerger()


for pdf in x:
merger.append(open(pdf, 'rb'))


with open("result.pdf", "wb") as fout:
merger.write(fout)

今天我该如何编写上面相同的代码呢

from glob import glob
from PyPDF2 import PdfFileMerger






def pdf_merge():
''' Merges all the pdf files in current directory '''
merger = PdfFileMerger()
allpdfs = [a for a in glob("*.pdf")]
[merger.append(pdf) for pdf in allpdfs]
with open("Merged_pdfs.pdf", "wb") as new_file:
merger.write(new_file)




if __name__ == "__main__":
pdf_merge()
from PyPDF2 import PdfFileMerger
import webbrowser
import os
dir_path = os.path.dirname(os.path.realpath(__file__))


def list_files(directory, extension):
return (f for f in os.listdir(directory) if f.endswith('.' + extension))


pdfs = list_files(dir_path, "pdf")


merger = PdfFileMerger()


for pdf in pdfs:
merger.append(open(pdf, 'rb'))


with open('result.pdf', 'wb') as fout:
merger.write(fout)


webbrowser.open_new('file://'+ dir_path + '/result.pdf')

Git Repo: https://github.com/mahaguru24/Python_Merge_PDF.git

使用字典以获得更大的灵活性(例如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")

我在linux终端上通过利用subprocess(假设目录中存在one.pdf和two.pdf)使用pdf unite,目的是将它们合并为three.pdf

 import subprocess
subprocess.call(['pdfunite one.pdf two.pdf three.pdf'],shell=True)

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];

下面是针对我的特定用例的最常见答案的时间比较:合并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

你可以使用PyPDF2模块中的PdfFileMerger

例如,要从路径列表中合并多个PDF文件,可以使用以下函数:

from PyPDF2 import PdfFileMerger


# pass the path of the output final file.pdf and the list of paths
def merge_pdf(out_path: str, extracted_files: list [str]):
merger   = PdfFileMerger()
    

for pdf in extracted_files:
merger.append(pdf)


merger.write(out_path)
merger.close()


merge_pdf('./final.pdf', extracted_files)

这个函数从父文件夹中递归地获取所有文件:

import os


# pass the path of the parent_folder
def fetch_all_files(parent_folder: str):
target_files = []
for path, subdirs, files in os.walk(parent_folder):
for name in files:
target_files.append(os.path.join(path, name))
return target_files


# get a list of all the paths of the pdf
extracted_files = fetch_all_files('./parent_folder')

最后,使用这两个函数进行声明。parent_folder_path可以包含多个文档,output_pdf_path用于合并PDF的目的地:

# get a list of all the paths of the pdf
parent_folder_path = './parent_folder'
outup_pdf_path     = './final.pdf'


extracted_files = fetch_all_files(parent_folder_path)
merge_pdf(outup_pdf_path, extracted_files)

你可以从这里得到完整的代码(来源):如何使用Python合并PDF文档

你也可以使用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中(您可以选择不复制哪些页面,然后pdf.pages对象的行为就像一个列表)。

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

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

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

< p > def pdf_merger(路径): "";合并为一个pdf""

import logging
logging.basicConfig(filename = 'output.log', level = logging.DEBUG, format = '%(asctime)s %(levelname)s %(message)s' )


try:
import glob, os
import PyPDF2
    

os.chdir(path)
    

pdfs = []
    

for file in glob.glob("*.pdf"):
pdfs.append(file)
        

if len(pdfs) == 0:
logging.info("No pdf in the given directory")
        

else:
merger = PyPDF2.PdfFileMerger()
        

for pdf in pdfs:
merger.append(pdf)
            

merger.write('result.pdf')
merger.close()
        

except Exception as e:
logging.error('Error has happened')
logging.exception('Exception occured' + str(e))
conda activate py_envs


pip install PyPDF2


from PyPDF2 import PdfFileMerger


#set path files


os.chdir('directory_files')
cwd = os.path.abspath('')
files = os.listdir(cwd)


def merge_pdf_files():
merger = PdfFileMerger()
pdf_files = [x for x in files if x.endswith(".pdf")]
[merger.append(pdf) for pdf in pdf_files]
with open("merged_pdf_all.pdf", "wb") as new_file:
merger.write(new_file)


if __name__ == "__main__":
pdf_merge()