zipfile — Acceso a archivos ZIP

Propósito:Leer y escribir archivos ZIP.

El módulo zipfile se puede usar para manipular archivos comprimidos ZIP, el formato popularizado por el programa de PC PKZIP.

Probar archivos ZIP

La función is_zipfile() devuelve un valor booleano que indica si o no el nombre de archivo pasado como argumento se refiere a un archivo ZIP válido.

zipfile_is_zipfile.py
import zipfile

for filename in ['README.txt', 'example.zip',
                 'bad_example.zip', 'notthere.zip']:
    print('{:>15}  {}'.format(
        filename, zipfile.is_zipfile(filename)))

Si el archivo no existe, is_zipfile() devuelve False.

$ python3 zipfile_is_zipfile.py

     README.txt  False
    example.zip  True
bad_example.zip  False
   notthere.zip  False

Leer metadatos de un archivo

Usa la clase ZipFile para trabajar directamente con un archivo ZIP. Éste admite métodos para leer datos sobre archivos existentes, así como modificar los archivos agregando archivos adicionales.

zipfile_namelist.py
import zipfile

with zipfile.ZipFile('example.zip', 'r') as zf:
    print(zf.namelist())

El método namelist() devuelve los nombres de los archivos en un archivo existente.

$ python3 zipfile_namelist.py

['README.txt']

Sin embargo, la lista de nombres es solo una parte de la información disponible en el archivo. Para acceder a todos los metadatos sobre los contenidos del ZIP usa los métodos infolist() o getinfo().

zipfile_infolist.py
import datetime
import zipfile


def print_info(archive_name):
    with zipfile.ZipFile(archive_name) as zf:
        for info in zf.infolist():
            print(info.filename)
            print('  Comment     :', info.comment)
            mod_date = datetime.datetime(*info.date_time)
            print('  Modified    :', mod_date)
            if info.create_system == 0:
                system = 'Windows'
            elif info.create_system == 3:
                system = 'Unix'
            else:
                system = 'UNKNOWN'
            print('  System      :', system)
            print('  ZIP version :', info.create_version)
            print('  Compressed  :', info.compress_size, 'bytes')
            print('  Uncompressed:', info.file_size, 'bytes')
            print()


if __name__ == '__main__':
    print_info('example.zip')

Hay campos adicionales distintos a los impresos aquí, pero descifrar los valores en algo útil requiere una lectura cuidadosa de la Nota de aplicación PKZIP con la especificación del archivo ZIP.

$ python3 zipfile_infolist.py

README.txt
  Comment     : b''
  Modified    : 2010-11-15 06:48:02
  System      : Unix
  ZIP version : 30
  Compressed  : 65 bytes
  Uncompressed: 76 bytes

Si el nombre del miembro del archivo es conocido de antemano, su objeto ZipInfo se puede recuperar directamente con getinfo().

zipfile_getinfo.py
import zipfile

with zipfile.ZipFile('example.zip') as zf:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            info = zf.getinfo(filename)
        except KeyError:
            print('ERROR: Did not find {} in zip file'.format(
                filename))
        else:
            print('{} is {} bytes'.format(
                info.filename, info.file_size))

Si el miembro del archivo no está presente, getinfo() levanta un KeyError.

$ python3 zipfile_getinfo.py

README.txt is 76 bytes
ERROR: Did not find notthere.txt in zip file

Extraer archivos de un archivo

Para acceder a los datos de un miembro del archivo, usa el método read(), pasando el nombre del miembro.

zipfile_read.py
import zipfile

with zipfile.ZipFile('example.zip') as zf:
    for filename in ['README.txt', 'notthere.txt']:
        try:
            data = zf.read(filename)
        except KeyError:
            print('ERROR: Did not find {} in zip file'.format(
                filename))
        else:
            print(filename, ':')
            print(data)
        print()

Los datos se descomprimen automáticamente, si es necesario.

$ python3 zipfile_read.py

README.txt :
b'The examples for the zipfile module use \nthis file and exampl
e.zip as data.\n'

ERROR: Did not find notthere.txt in zip file

Crear nuevos aRchivos

Para crear un archivo nuevo, crea una instancia de ZipFile con un modo de 'w'. Cualquier archivo existente se trunca y un nuevo archivo se empieza. Para agregar archivos, usa el método write().

zipfile_write.py
from zipfile_infolist import print_info
import zipfile

print('creating archive')
with zipfile.ZipFile('write.zip', mode='w') as zf:
    print('adding README.txt')
    zf.write('README.txt')

print()
print_info('write.zip')

Por defecto, el contenido del archivo no es comprimido.

$ python3 zipfile_write.py

creating archive
adding README.txt

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

Para agregar compresión, se requiere el módulo zlib. Si :mod: zlib está disponible, el modo de compresión para archivos individuales o para el el archivo en su conjunto se puede configurar usando zipfile.ZIP_DEFLATED. El modo de compresión predeterminado es zipfile.ZIP_STORED, que agrega los datos de entrada al archivo sin comprimirlos.

zipfile_write_compression.py
from zipfile_infolist import print_info
import zipfile
try:
    import zlib
    compression = zipfile.ZIP_DEFLATED
except (ImportError, AttributeError):
    compression = zipfile.ZIP_STORED

modes = {
    zipfile.ZIP_DEFLATED: 'deflated',
    zipfile.ZIP_STORED: 'stored',
}

print('creating archive')
with zipfile.ZipFile('write_compression.zip', mode='w') as zf:
    mode_name = modes[compression]
    print('adding README.txt with compression mode', mode_name)
    zf.write('README.txt', compress_type=compression)

print()
print_info('write_compression.zip')

Esta vez, el miembro del archivo es comprimido.

$ python3 zipfile_write_compression.py

creating archive
adding README.txt with compression mode deflated

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 65 bytes
  Uncompressed: 76 bytes

Usar nombres alternativos de miembros del archivo

Pasa un valor arcname a write() para agregar un archivo a un archivo usando un nombre que no sea el nombre original del archivo.

zipfile_write_arcname.py
from zipfile_infolist import print_info
import zipfile

with zipfile.ZipFile('write_arcname.zip', mode='w') as zf:
    zf.write('README.txt', arcname='NOT_README.txt')

print_info('write_arcname.zip')

No hay ninguna señal del nombre de archivo original en el archivo.

$ python3 zipfile_write_arcname.py

NOT_README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

Escribir datos de fuentes que no sean archivos

A veces es necesario escribir en un archivo ZIP usando datos que no vienen de un archivo existente. En lugar de escribir los datos a un archivo, luego agregar ese archivo al archivo ZIP, usa el método writestr() para agregar una cadena de bytes directamente al archivo.

zipfile_writestr.py
from zipfile_infolist import print_info
import zipfile

msg = 'This data did not exist in a file.'
with zipfile.ZipFile('writestr.zip',
                     mode='w',
                     compression=zipfile.ZIP_DEFLATED,
                     ) as zf:
    zf.writestr('from_string.txt', msg)

print_info('writestr.zip')

with zipfile.ZipFile('writestr.zip', 'r') as zf:
    print(zf.read('from_string.txt'))

En este caso, el argumento compress_type para ZipFile es utilizado para comprimir los datos, ya que writestr() no toma un argumento para especificar la compresión.

$ python3 zipfile_writestr.py

from_string.txt
  Comment     : b''
  Modified    : 2016-12-29 12:14:42
  System      : Unix
  ZIP version : 20
  Compressed  : 36 bytes
  Uncompressed: 34 bytes

b'This data did not exist in a file.'

Escribir con una instancia de ZipInfo

Normalmente, la fecha de modificación se calcula cuando un archivo o cadena es añadido al archivo. Una instancia ZipInfo se puede pasar a writestr() para definir la fecha de modificación y otros metadatos.

zipfile_writestr_zipinfo.py
import time
import zipfile
from zipfile_infolist import print_info

msg = b'This data did not exist in a file.'

with zipfile.ZipFile('writestr_zipinfo.zip',
                     mode='w',
                     ) as zf:
    info = zipfile.ZipInfo('from_string.txt',
                           date_time=time.localtime(time.time()),
                           )
    info.compress_type = zipfile.ZIP_DEFLATED
    info.comment = b'Remarks go here'
    info.create_system = 0
    zf.writestr(info, msg)

print_info('writestr_zipinfo.zip')

En este ejemplo, la hora de modificación se establece en la hora actual, los datos se comprimen y se usa el valor falso para create_system. Un simple comentario también se asocia con el nuevo archivo.

$ python3 zipfile_writestr_zipinfo.py

from_string.txt
  Comment     : b'Remarks go here'
  Modified    : 2016-12-29 12:14:42
  System      : Windows
  ZIP version : 20
  Compressed  : 36 bytes
  Uncompressed: 34 bytes

Anexar a archivos

Además de crear nuevos archivos, es posible adjuntar a un archivo existente o agregar un archivo al final de un archivo existente (como un archivo .exe a un archivo autoextraíble). Para abrir un archivo para adjuntarlo, usa el modo 'a'.

zipfile_append.py
from zipfile_infolist import print_info
import zipfile

print('creating archive')
with zipfile.ZipFile('append.zip', mode='w') as zf:
    zf.write('README.txt')

print()
print_info('append.zip')

print('appending to the archive')
with zipfile.ZipFile('append.zip', mode='a') as zf:
    zf.write('README.txt', arcname='README2.txt')

print()
print_info('append.zip')

El archivo resultante contiene dos miembros:

$ python3 zipfile_append.py

creating archive

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

appending to the archive

README.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

README2.txt
  Comment     : b''
  Modified    : 2016-08-07 13:31:24
  System      : Unix
  ZIP version : 20
  Compressed  : 76 bytes
  Uncompressed: 76 bytes

Archivos ZIP de Python

Python puede importar módulos desde dentro de archivos ZIP usando zipimport, si esos archivos aparecen en sys.path. La clase PyZipFile se puede usar para construir un módulo adecuado para su uso de esta manera. El método extra writepy() le dice a PyZipFile que escanee un directorio en busca de archivos .py y agrege el archivo .pyo o .pyc correspondiente al archivo. Si ninguna forma compilada existe, se crea y agrega un archivo .pyc.

zipfile_pyzipfile.py
import sys
import zipfile

if __name__ == '__main__':
    with zipfile.PyZipFile('pyzipfile.zip', mode='w') as zf:
        zf.debug = 3
        print('Adding python files')
        zf.writepy('.')
    for name in zf.namelist():
        print(name)

    print()
    sys.path.insert(0, 'pyzipfile.zip')
    import zipfile_pyzipfile
    print('Imported from:', zipfile_pyzipfile.__file__)

Con el atributo de depuración de PyZipFile establecido en 3, la depuración verbosa está habilitada y la salida se produce a medida que se compila cada archivo .py que se encuentra.

$ python3 zipfile_pyzipfile.py

Adding python files
Adding files from directory .
Compiling ./zipfile_append.py
Adding zipfile_append.pyc
Compiling ./zipfile_getinfo.py
Adding zipfile_getinfo.pyc
Compiling ./zipfile_infolist.py
Adding zipfile_infolist.pyc
Compiling ./zipfile_is_zipfile.py
Adding zipfile_is_zipfile.pyc
Compiling ./zipfile_namelist.py
Adding zipfile_namelist.pyc
Compiling ./zipfile_printdir.py
Adding zipfile_printdir.pyc
Compiling ./zipfile_pyzipfile.py
Adding zipfile_pyzipfile.pyc
Compiling ./zipfile_read.py
Adding zipfile_read.pyc
Compiling ./zipfile_write.py
Adding zipfile_write.pyc
Compiling ./zipfile_write_arcname.py
Adding zipfile_write_arcname.pyc
Compiling ./zipfile_write_compression.py
Adding zipfile_write_compression.pyc
Compiling ./zipfile_writestr.py
Adding zipfile_writestr.pyc
Compiling ./zipfile_writestr_zipinfo.py
Adding zipfile_writestr_zipinfo.pyc
zipfile_append.pyc
zipfile_getinfo.pyc
zipfile_infolist.pyc
zipfile_is_zipfile.pyc
zipfile_namelist.pyc
zipfile_printdir.pyc
zipfile_pyzipfile.pyc
zipfile_read.pyc
zipfile_write.pyc
zipfile_write_arcname.pyc
zipfile_write_compression.pyc
zipfile_writestr.pyc
zipfile_writestr_zipinfo.pyc

Imported from: pyzipfile.zip/zipfile_pyzipfile.pyc

Limitaciones

El módulo zipfile no admite archivos ZIP con comentarios anexados, o archivos multi-disco. Soporta archivos ZIP más grandes de 4 GB que utilizan las extensiones ZIP64.

Ver también