python - pdffilemerger - pypdf2 merge pdfs
pypdf fusionar varios archivos pdf en un pdf (4)
Si tengo más de 1000 archivos pdf, debo combinarlos en un solo 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()
Ejecute el código anterior, cuando input = PdfFileReader(file(filename500+, "rb"))
,
Un mensaje de error: IOError: [Errno 24] Too many open files:
Creo que esto es un error, si no, ¿qué debo hacer?
El paquete pdfrw lee cada archivo de una vez, por lo que no sufrirá el problema de tener demasiados archivos abiertos. Here hay un ejemplo de script de concatenación.
La parte relevante - asume inputs
es una lista de nombres de archivos de entrada, y outfn
es un nombre de archivo de salida:
from pdfrw import PdfReader, PdfWriter
writer = PdfWriter()
for inpfn in inputs:
writer.addpages(PdfReader(inpfn).pages)
writer.write(outfn)
Descargo de responsabilidad: Soy el principal autor pdfrw.
El problema es que solo se le permite tener un cierto número de archivos abiertos en un momento dado. Hay formas de cambiar esto ( http://docs.python.org/3/library/resource.html#resource.getrlimit ), pero no creo que lo necesite.
Lo que podrías intentar es cerrar los archivos en el bucle for:
input = PdfFileReader()
output = PdfFileWriter()
for file in filenames:
f = open(file, ''rb'')
input = PdfFileReader(f)
# Some code
f.close()
Hace poco me encontré con el mismo problema, así que busqué en PyPDF2 para ver qué sucede y cómo resolverlo.
Nota: Supongo que el filename
es una cadena de ruta de archivo bien formada. Asume lo mismo para todo mi código
La respuesta corta
Use la clase PdfFileMerger()
lugar de la clase PdfFileWriter()
. He intentado proporcionar lo siguiente para asemejarme lo más posible a tu contenido:
from PyPDF2 import PdfFileMerger, PdfFileReader
[...]
merger = PdfFileMerger()
for filename in filenames:
merger.append(PdfFileReader(file(filename, ''rb'')))
merger.write("document-output.pdf")
La respuesta larga
La forma en que está utilizando PdfFileReader
y PdfFileWriter
es mantener cada archivo abierto, y finalmente hace que Python genere IOError 24. Para ser más específico, cuando agrega una página a PdfFileWriter
, está agregando referencias a la página en el PdfFileReader
abierto ( por lo tanto, el error de E / S observado si cierra el archivo). Python detecta que el archivo sigue siendo referenciado y no realiza ninguna recolección de basura / cierre automático de archivos a pesar de reutilizar el identificador de archivo. Permanecen abiertos hasta que PdfFileWriter
ya no necesita acceder a ellos, que se encuentra en output.write(outputStream)
en su código.
Para resolver esto, cree copias en la memoria del contenido y permita que se cierre el archivo. Noté en mis aventuras a través del código PyPDF2 que la clase PdfFileMerger()
ya tiene esta funcionalidad, así que en lugar de reinventar la rueda, opté por usarla en su lugar. Sin embargo, supe que mi primer vistazo a PdfFileMerger
no era lo suficientemente cercano y que solo creaba copias en ciertas condiciones .
Mis intentos iniciales parecían ser los siguientes, y resultaron en los mismos problemas de IO:
merger = PdfFileMerger()
for filename in filenames:
merger.append(filename)
merger.write(output_file_path)
Mirando el código fuente de PyPDF2, vemos que append()
requiere que se pase fileobj
, y luego usa la función merge()
, pasando la última página como la nueva posición de los archivos. merge()
hace lo siguiente con fileobj
(antes de abrirlo con 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
Podemos ver que la opción append()
acepta una cadena, y al hacerlo, asume que es una ruta de archivo y crea un objeto de archivo en esa ubicación. El resultado final es exactamente lo mismo que intentamos evitar. ¡Un objeto PdfFileReader()
que mantiene abierto un archivo hasta que finalmente se escribe!
Sin embargo, si creamos un objeto de archivo de la cadena de ruta de archivo o un objeto PdfFileReader
(ver Edición 2) de la cadena de ruta antes de que se pase a append()
, automáticamente creará una copia para nosotros como un objeto StringIO
, permitiendo Python para cerrar el archivo.
Recomendaría el merger.append(file(filename, ''rb''))
más merger.append(file(filename, ''rb''))
, ya que otros informaron que un objeto PdfFileReader
puede permanecer abierto en la memoria, incluso después de llamar a writer.close()
.
Espero que esto haya ayudado!
EDITAR: asumí que estaba usando PyPDF2
, no PyPDF
. Si no es así, recomiendo encarecidamente el cambio, ya que PyPDF ya no se mantiene con el autor dando sus bendiciones oficiales a Phaseit en el desarrollo de PyPDF2.
Si, por algún motivo, no puede cambiar a PyPDF2 (licencias, restricciones del sistema, etc.), PdfFileMerger
no estará disponible para usted. En esa situación, puede reutilizar el código de la función de merge
de PyPDF2 (provista arriba) para crear una copia del archivo como un objeto StringIO
, y usarlo en su código en lugar del objeto del archivo.
EDIT 2: recomendación previa de usar merger.append(PdfFileReader(file(filename, ''rb'')))
modificada según los comentarios (Gracias @Agostino) .
Tal vez justo lo que dice, estás abriendo muchos archivos. Puede usar explícitamente f=file(filename) ... f.close()
en el bucle, o usar la instrucción with
. Para que cada archivo abierto se cierre correctamente.