pypdf将多个pdf文件合并为一个pdf

day*_*say 29 python pypdf

如果我有1000多个pdf文件需要合并成一个pdf,

input = PdfFileReader()
output = PdfFileWriter()
filename0000 ----- filename 1000
    input = PdfFileReader(file(filename, "rb"))
    pageCount = input.getNumPages()
    for iPage in range(0, pageCount):
        output.addPage(input.getPage(iPage))
outputStream = file("document-output.pdf", "wb")
output.write(outputStream)
outputStream.close()
Run Code Online (Sandbox Code Playgroud)

执行上面的代码时input = PdfFileReader(file(filename500+, "rb")),

错误消息: IOError: [Errno 24] Too many open files:

我认为这是一个错误,如果没有,我该怎么办?

Rej*_*ted 62

我最近遇到了同样的问题,所以我挖到PyPDF2看看发生了什么,以及如何解决它.

注意:我假设这filename是一个格式正确的文件路径字符串.假设我的所有代码都是一样的

简答

使用PdfFileMerger()类而不是PdfFileWriter()类.我试图提供以下内容,尽可能地与您的内容非常相似:

from PyPDF2 import PdfFileMerger, PdfFileReader

[...]

merger = PdfFileMerger()
for filename in filenames:
    merger.append(PdfFileReader(file(filename, 'rb')))

merger.write("document-output.pdf")
Run Code Online (Sandbox Code Playgroud)

答案很长

您正在使用的方式PdfFileReader,并PdfFileWriter在保持每个文件打开,并最终导致Python来产生的IOError 24.具体而言,当你添加一个页面到PdfFileWriter,要添加引用在打开的页面PdfFileReader(因此指出IO关闭文件时出错).Python检测到仍然被引用的文件,尽管重新使用文件句柄,但不会执行任何垃圾收集/自动文件关闭.它们保持打开状态,直到PdfFileWriter不再需要访问它们为止output.write(outputStream).

要解决此问题,请在内容的内存中创建副本,并允许关闭文件.我在冒险中通过PyPDF2代码注意到PdfFileMerger()该类已经具有此功能,因此我选择使用它而不是重新发明轮子.不过,我了解到,我最初的看法并PdfFileMerger不够,而且它只在某些条件下创建了副本.

我最初的尝试看起来如下,并导致相同的IO问题:

merger = PdfFileMerger()
for filename in filenames:
    merger.append(filename)

merger.write(output_file_path)
Run Code Online (Sandbox Code Playgroud)

查看PyPDF2源代码,我们看到append()需要fileobj传递,然后使用该merge()函数,将其作为新文件位置传入其最后一页.merge()执行以下操作fileobj(在打开之前PdfFileReader(fileobj):

    if type(fileobj) in (str, unicode):
        fileobj = file(fileobj, 'rb')
        my_file = True
    elif type(fileobj) == file:
        fileobj.seek(0)
        filecontent = fileobj.read()
        fileobj = StringIO(filecontent)
        my_file = True
    elif type(fileobj) == PdfFileReader:
        orig_tell = fileobj.stream.tell()   
        fileobj.stream.seek(0)
        filecontent = StringIO(fileobj.stream.read())
        fileobj.stream.seek(orig_tell)
        fileobj = filecontent
        my_file = True
Run Code Online (Sandbox Code Playgroud)

我们可以看到该append()选项确实接受了一个字符串,当这样做时,假定它是一个文件路径并在该位置创建一个文件对象.最终结果与我们试图避免的完全相同.一个PdfFileReader()对象保持打开文件,直到最终写入文件!

但是,如果我们在传入之前创建文件路径字符串的文件对象路径字符串的PdfFileReader(参见编辑2)对象,它将自动为我们创建一个副本作为对象,允许Python关闭文件.append()StringIO

我建议更简单merger.append(file(filename, 'rb')),因为其他人报告说PdfFileReader,即使在调用之后,对象也可能在内存中保持打开状态writer.close().

希望这有帮助!

编辑:我以为你在使用PyPDF2,而不是PyPDF.如果你不是,我强烈推荐切换,因为PyPDF不再与作者在开发PyPDF2时给予Phaseit官方祝福.

如果由于某种原因你不能交换到PyPDF2(许可,系统限制等)PdfFileMerger,那么你将无法使用.在这种情况下,您可以重复使用PyPDF2 merge函数(上面提供的)中的代码来创建文件的副本作为StringIO对象,并在代码中使用它来代替文件对象.

编辑2:以前merger.append(PdfFileReader(file(filename, 'rb')))根据评论使用更改的建议(感谢@Agostino).

  • 我注意到我无法删除通过调用“writer.append(PdfFileReader(file(filename, 'rb')))”创建中间“PdfFileReader”对象时附加的一些文件。即使在调用“writer.close()”之后,它们仍然保持锁定状态。更简单的调用 `merger.append(file(filename, 'rb'))` 似乎没有同样的问题。 (2认同)
  • @Rejected我相信对于Python 3,您需要使用“open”,而不是“merger.append(PdfFileReader(file(filename, 'rb')))”中的“file”。就像 `merger.append(PdfFileReader(open(filename, 'rb')))` 一样。 (2认同)