Índice:
- Introducción.
- El libro (este capítulo).
- La hoja de cálculos.
- La celda.
- Los estilos.
- Los gráficos.
Vamos a empezar a explorar lo que es un libro y las cosas que podemos hacer con el mismo. En realidad, el libro actúa principalmente de contenedor de las hojas por lo que lo que podemos hacer con el mismo no es extremadamente apasionante.
1 |
import openpyxl |
Creamos un nuevo libro vacio:
1 |
wb = openpyxl.Workbook() |
Vamos a recordar, ya lo vimos en el anterior capítulo, todo lo que tenía disponible un libro:
1 |
print(dir(wb)) |
Lo anterior mostrará en pantalla:
1 |
['_Workbook__write_only', '__class__', '__contains__', '__delattr__', '__delitem__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_active_sheet_index', '_add_sheet', '_alignments', '_borders', '_cell_styles', '_colors', '_data_only', '_date_formats', '_differential_styles', '_duplicate_name', '_external_links', '_fills', '_fonts', '_named_styles', '_number_formats', '_pivots', '_protections', '_read_only', '_setup_styles', '_sheets', '_table_styles', 'active', 'add_named_range', 'add_named_style', 'calculation', 'chartsheets', 'close', 'code_name', 'copy_worksheet', 'create_chartsheet', 'create_named_range', 'create_sheet', 'data_only', 'defined_names', 'encoding', 'epoch', 'excel_base_date', 'get_index', 'get_named_range', 'get_named_ranges', 'get_sheet_by_name', 'get_sheet_names', 'index', 'is_template', 'iso_dates', 'loaded_theme', 'mime_type', 'move_sheet', 'named_styles', 'path', 'properties', 'read_only', 'rels', 'remove', 'remove_named_range', 'remove_sheet', 'save', 'security', 'shared_strings', 'sheetnames', 'style_names', 'template', 'vba_archive', 'views', 'worksheets', 'write_only'] |
vamos a ver algunas de estas cositas más en detalle, otras ya las iremos comentando cuando las vayamos utilizando:
active
: es una property que nos devuelve la hoja que está activa o mediante la cual podemos indicar qué hoja queremos que sea la activa. Luego veremos qué es eso de la hoja activa.create_sheet
: este método nos permite crear hojas nuevas en nuestro libro en la posición que le indiquemos.remove
: este método nos deja eliminar hojas de un libro.create_chartsheet
: método para añadir hojas con un gráfico en la posición que indiquemos.worksheets
: property que nos devuelve una lista con todas las hojas del libro.chartsheets
: property que nos devuelve una lista todas las hojas de gráfico del libro.sheetnames
: property que nos devuelve todos los nombres de las hojas del libro.add_named_style
: Método que nos permite añadir estilos.named_styles
: property que nos devuelve una lista de los estilos disponibles.style_names
: property que nos devuelve una lista de los estilos contenidos en el libro.copy_worksheet
: Método para copiar hojas.save
: método para guardar el libro.close
: método para cerrar el libro cuando hayamos terminado con el mismo.
Vamos a ver cuantas hojas tenemos:
1 |
wb.worksheets |
Lo anterior muestra:
1 |
[<Worksheet "Sheet">] |
Por otro lado:
1 |
wb.chartsheets |
Muestra en pantalla:
1 |
[] |
Tenemos una hoja de trabajo y ninguna de gráfico. Vamos a añadir una de cada y vamos a cambiar el nombre de la hoja de trabajo que tenemos ahora.
[Nota: para los nombres vamos a usar HT para worksheets y HG para chartsheets. Los nombres de las hojas en Microsoft excel no pueden ser muy largos]
1 |
wb.active.title = "HT original" |
1 |
wb.create_sheet("Nueva HT delante de la original", index=0) |
1 |
wb.create_chartsheet("Nueva HG al final") |
A ver lo que tenemos ahora:
1 2 |
print(wb.worksheets) print(wb.chartsheets) |
Lo anterior muestra:
1 2 |
[<Worksheet "Nueva HT delante de la original">, <Worksheet "HT original">] [<Chartsheet "Nueva HG al final">] |
Vamos a obtener los nombres:
1 |
print(wb.sheetnames) |
Lo anterior debería mostrar:
1 |
['Nueva HT delante de la original', 'HT original', 'Nueva HG al final'] |
Si ahora quiero trabajar con la hoja o pestaña activa, ¿cuál crees que sería?, ¿la original que teníamos al principio?, ¿alguna de las nuevas?
Lee la primera nota en este apartado de la documentación: Vale 0 por defecto. A no ser que modifiques su valor, siempre obtendrás la primera hoja de trabajo usando este mètodo.
1 |
wb.index(wb.active) |
Lo anterior mostrará el valor 0
.
1 |
wb.active.title |
Y lo anterior mostrará 'Nueva HT delante de la original'
.
¡¡Tened cuidado con esto!!
Si queremos tener la hoja original deberíamos de cambiar el index
o pedirla de forma explícita. Para pedir una hoja se puede hacer de las siguientes formas:
1 |
ws_orig1 = wb["HT original"] |
1 |
ws_orig2 = wb.worksheets[1] |
1 |
print(ws_orig1 == ws_orig2) |
La línea anterior de código debería mostrar True
.
Quizás es mejor la primera que es más explícita… Explícito mejor que implícito, ante la ambigüedad rechaza la tentación de tener que adivinar, etc.
Si queremos borrar una hoja podemos usar el método remove
al cual le tenemos que pasar el objeto (hoja de trabajo u hoja de gráfico) que queremos eliminar:
1 |
print(wb.sheetnames) |
Lo anterior nos dará lo mismo que antes, ['Nueva HT delante de la original', 'HT original', 'Nueva HG al final']
.
1 |
wb.remove(wb["HT original"]) |
1 |
print(wb.sheetnames) |
Y el resultado será ahora ['Nueva HT delante de la original', 'Nueva HG al final']
.
Podemos duplicar una hoja usando el método copy_worksheet
:
1 |
ws_copia = wb.copy_worksheet(wb["Nueva HT delante de la original"]) |
Lo anterior os dará un warning sobre la longitud del nombre, como he comentado más arriba.
1 |
print(wb.sheetnames) |
Lo anterior mostrará ['Nueva HT delante de la original', 'Nueva HG al final', 'Nueva HT delante de la original Copy']
.
1 |
ws_copia.title = "Copia HT de la primera" |
1 |
print(wb.sheetnames) |
Y después de cambiarle el nombre el resultado será ['Nueva HT delante de la original', 'Nueva HG al final', 'Copia HT de la primera']
.
Como habrás notado, el nombre del método es copy_worksheet
y no copy_sheet
ni existe un método copy_chartsheet
…
1 |
wg_copia = wb.copy_worksheet(wb["Nueva HG al final"]) |
Lo anterior dará un AttributeError: 'Chartsheet' object has no attribute '_cells'
.
Si no voy desencaminado esto tiene sentido que sea así ya que una hoja de gráficos no tiene datos y depende de una hoja de datos por lo que copiarla quizás tenga menos sentido.
Vamos a guardar nuestro libro:
1 |
wb.save("new.xlsx") |
Y lo cerramos:
1 |
wb.close() |
Si abrimos el fichero xlsx que acabamos de crear veríamos algo así:

Si miráis la hoja/pestaña activa veréis que es la hoja/pestaña de gráficos que hemos intentando crear con wg_copia = wb.copy_worksheet(wb["Nueva HG al final"])
y nos ha dado un AttributeError
. Para corregir eso mandé un PR al proyecto para evitar que eso pase (pero parece que ahí sigue 🙁 ).
Atributos de un Libro
Podemos añadir “Metainformación” al fichero xlsx como el nombre del autor, fecha, título,…
1 |
wb = openpyxl.Workbook() |
1 |
print(wb.properties) |
Lo anterior dará:
1 2 3 |
<openpyxl.packaging.core.DocumentProperties object> Parameters: creator='openpyxl', title=None, description=None, subject=None, identifier=None, language=None, created=datetime.datetime(2021, 2, 23, 21, 46, 10, 94467), modified=datetime.datetime(2021, 2, 23, 21, 46, 10, 94472), lastModifiedBy=None, category=None, contentStatus=None, version=None, revision=None, keywords=None, lastPrinted=None |
1 |
print(wb.properties.creator) |
Que mostrará openpyxl
.
Podemos actualizar lo anterior de la siguiente forma:
1 |
wb.properties.creator = "Kiko con ayuda de openpyxl" |
1 |
print(wb.properties.creator) |
Y mostrará Kiko con ayuda de openpyxl
.
Con las demás propiedades del documento podríais hacer lo mismo. Si guardamos el documento:
1 2 |
wb.save("new.xlsx") wb.close() |
Lo abrimos con un software de hoja de cálculos y nos vamos a las propiedades del documento veréis algo parecido a lo siguiente:

Abriendo un libro previamente creado
Obviamente, openpyxl
nos deja no solo crear ficheros en formato xlsx, también nos deja abrirlos. Al abrirlos o crearlos podemos pasar una serie de parámetros. Nos vamos a centrar en la lectura de fichero xlsx ya creados:
1 |
help(openpyxl.load_workbook) |
La anterior ayuda mostrará:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
Help on function load_workbook in module openpyxl.reader.excel: load_workbook(filename, read_only=False, keep_vba=False, data_only=False, keep_links=True) Open the given filename and return the workbook :param filename: the path to open or a file-like object :type filename: string or a file-like object open in binary mode c.f., :class:`zipfile.ZipFile` :param read_only: optimised for reading, content cannot be edited :type read_only: bool :param keep_vba: preseve vba content (this does NOT mean you can use it) :type keep_vba: bool :param data_only: controls whether cells with formulae have either the formula (default) or the value stored the last time Excel read the sheet :type data_only: bool :param keep_links: whether links to external workbooks should be preserved. The default is True :type keep_links: bool :rtype: :class:`openpyxl.workbook.Workbook` .. note:: When using lazy load, all worksheets will be :class:`openpyxl.worksheet.iter_worksheet.IterableWorksheet` and the returned workbook will be read-only. |
Además del nombre (y ruta) del fichero la función load_workbook
acepta:
read_only
: Si quieres abrir el libro en modo solo lectura. Por defecto esto esFalse
keep_vba
: Si quieres mantener posible código VBA que pudiera tener el libro. Por supuesto, no lo podrás usar desde Pythondata_only
: Si quieres mantener las fórmulas del libro que tenía al cerrarse o solo mantener los valores nuiméricos.keep_links
: Si quieres mantener enlaces a otros libros.
Modos de solo lectura o escritura
Si necesitas solo leer información de un fichero xlsx para luego usarlo en Python existe un modo optimizado. Lo podemos usar de la siguiente forma:
1 2 3 4 5 6 |
wb = openpyxl.load_workbook("new.xlsx", read_only=True) ws = wb.active for row in ws.rows: for cell in row: print(cell.value) |
De esta forma, lo que usamos no es un objeto Worksheet
. Utilizamos uno más optimizado:
1 |
print(type(ws)) |
El resultado que mostrará es:
1 |
<class 'openpyxl.worksheet._read_only.ReadOnlyWorksheet'> |
De la misma forma, podemos usar un modo optimizado de escritura para cuando, por ejemplo, queremos guardar información que hemos procesado con Python en un fichero xlsx.
1 2 3 4 5 6 7 |
wb = openpyxl.Workbook(write_only=True) ws = wb.create_sheet() for row in range(100): ws.append([f"{i}" for i in range(200)]) wb.save("new.xlsx") |
Nuevamente, si miramos el tipo, veremos que tenemos un tipo de hoja optimizado diferente a Worksheet
:
1 |
print(type(ws)) |
El resultado será:
1 |
<class 'openpyxl.worksheet._write_only.WriteOnlyWorksheet'> |
Fijaos que he tenido que crear una hoja nueva ya que si creamos un libro así este no tiene hojas de inicio.
Fijaos también que las filas las añadimos con el método append
. Este tipo de workbook no nos deja escribir celdas en posiciones arbitrarias.
Hacer notar que este tipo de libro solo se puede guardar una vez. Si lo intentamos guardar una segunda vez nos devolverá una excepción.
Resumen
Hemos estado viendo lo que es un libro. Básicamente es un contenedor de hojas o pestañas y de una serie de atributos sobre el libro mismo, si es de solo lectura, el autor, la última cez que se modificó,…
Hola, tengo una duda, estoy aprendiendo a utilizar Openpyxl pero cuando quiero guardar un archivo modificado, este sobre escribe al anterior, borrando toda la data previamente cargada y registrando unicamente la nueva que ingrese.
Utilizo wb.save(‘nombre.xlsx’), para guardar pero siempre me sobre escribe el archivo y pierdo toda la info antigua.
¿Existe alguna forma de solo “guardar” sin sobre escribir con Openpyxl?
Sin ver el código completo es difícil darte una respuesta. Si quieres, haz la pregunta por aquí: https://foro.pybonacci.org y buscamos una solución.
Buenas;
He seguido los artículos pero me surge una duda, que no se si alguien sabrá como resolverla, quiero borrar las columnas que están ocultas en un archivo.
Mi archivo tiene ocultas desde la columna B hasta la P (ambas inclusive, tengo el siguiente código:
wb = load_workbook(file, data_only=True)
ws = wb['Hoja1']
for colLetter,colDimension in ws.column_dimensions.items():
if colDimension.hidden == True:
print(colLetter)
Se supone que con el código de arriba debería imprimirme las columnas ocultas (BCDEFGHUJKLMNOP), sin embargo solo me devuelve B y C.
¿Alguna idea?
Gracias
Sin ver el fichero xlsx es difícil saber qué es lo que te puede estar fallando.