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

时间:2013-06-14 09:07:06

标签: 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()

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

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

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

5 个答案:

答案 0 :(得分:57)

我最近遇到了同样的问题,所以我挖到了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")

长答案

您使用PdfFileReaderPdfFileWriter的方式是保持每个文件都处于打开状态,并最终导致Python生成IOError 24.更具体地说,当您向{{1}添加页面时您正在打开PdfFileWriter页面中的页面引用(因此,如果您关闭文件,则会出现标注的IO错误)。 Python检测到仍然被引用的文件,尽管重新使用文件句柄,但不会执行任何垃圾收集/自动文件关闭。它们保持打开状态,直到PdfFileReader不再需要访问它们,代码位于PdfFileWriter

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

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

PdfFileMerger

查看PyPDF2源代码,我们看到merger = PdfFileMerger() for filename in filenames: merger.append(filename) merger.write(output_file_path) 需要传递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 选项确实接受了一个字符串,并且在这样做时,假设它是一个文件路径并在该位置创建一个文件对象。最终结果与我们试图避免的完全相同。一个append()对象保持打开文件,直到最终写入文件!

但是,如果我们要么创建文件路径字符串的文件对象PdfFileReader() (请参阅编辑2) 路径字符串传递到PdfFileReader之前的对象,它将自动为我们创建一个append()对象的副本,允许Python关闭该文件。

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

希望这有帮助!

编辑:我认为您使用的是writer.close(),而不是PyPDF2。如果你不是,我强烈推荐切换,因为PyPDF不再与作者在开发PyPDF2时向Phaseit提供正式祝福。

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

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

答案 1 :(得分:3)

pdfrw包可以一次性读取每个文件,因此不会遇到打开文件太多的问题。 Here是一个示例连接脚本。

相关部分 - 假设inputs是输入文件名列表,outfn是输出文件名:

from pdfrw import PdfReader, PdfWriter

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

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

答案 2 :(得分:1)

问题是您只能在任何给定时间打开一定数量的文件。有办法改变这个(http://docs.python.org/3/library/resource.html#resource.getrlimit),但我认为你不需要这个。

您可以尝试关闭for循环中的文件:

input = PdfFileReader()
output = PdfFileWriter()
for file in filenames:
   f = open(file, 'rb')
   input = PdfFileReader(f)
   # Some code
   f.close()

答案 3 :(得分:1)

我已经编写了这段代码来帮助回答:-

import sys
import os
import PyPDF2

merger = PyPDF2.PdfFileMerger()

#get PDFs files and path

path = sys.argv[1]
pdfs = sys.argv[2:]
os.chdir(path)


#iterate among the documents
for pdf in pdfs:
    try:
        #if doc exist then merge
        if os.path.exists(pdf):
            input = PyPDF2.PdfFileReader(open(pdf,'rb'))
            merger.append((input))
        else:
            print(f"problem with file {pdf}")

    except:
            print("cant merge !! sorry")
    else:
            print(f" {pdf} Merged !!! ")

merger.write("Merged_doc.pdf")

在此,我使用了PyPDF2.PdfFileMerger和PyPDF2.PdfFileReader,而不是将文件名明确转换为文件对象

答案 4 :(得分:0)

它可能正是它所说的,你正在开放许多文件。 您可以在循环中明确使用f=file(filename) ... f.close(),或使用with语句。这样每个打开的文件都可以正常关闭。