Índice:
- Introducción (este capítulo).
- El libro.
- La hoja de cálculos.
- La celda.
- Los estilos.
- Los gráficos.
Hay varias librerías para leer y/o escribir archivos en formato xlsx desde Python. El formato xlsx es el formato que usa Microsoft para la hoja de cálculos Microsoft Excel y se basa en el estándar Office Open XML del que ya hemos hablado bastante.
En este tutorial vamos a ver cómo podemos trabajar para leer información de ficheros en formato xlsx ya creados o para crear nuestros propios ficheros en formato xlsx.
Primero unos conceptos básicos:
- libro: En nuestra jerga esto será la representación de nuestro fichero xlsx cuando lo abrimos en un software de hoja de cálculos.
- hoja o pestaña: Un libro está compuesto de varias hojas o pestañas. Una hoja o pestaña es lo que usamos para escribir nuestra información. Cuando está vacia la podemos ver como una tabla vacia o rejilla (si es una hoja de trabajo). A esta tabla le podemos añadir valores numéricos, fechas, texto, fórmulas,…, en celdas. Además, sobre la rejilla podemos colocar gráficos basados en los datos de las celdas. Las hojas también pueden ser de gráficos. Veremos esto más en detalle en próximos capítulos.
- celda: Es dónde colocamos nuestra información con la que haremos cosas.
Hay muchas cosas pero estas son las principales. Vamos a ver cómo se representan estas cosas en openpyxl
:
Como siempre, vamos a empezar importando bibliotecas:
from inspect import (
getmembers, ismethod, isdatadescriptor, ismethoddescriptor
)
import openpyxl
Veamos lo que nos ofrece:
print(dir(openpyxl))
La salida de lo anterior nos dará algo como lo siguiente:
['DEFUSEDXML', 'LXML', 'NUMPY', 'PANDAS', 'Workbook', '__author__', '__author_email__', '__builtins__', '__cached__', '__doc__', '__file__', '__license__', '__loader__', '__maintainer_email__', '__name__', '__package__', '__path__', '__spec__', '__url__', '__version__', '_constants', 'cell', 'chart', 'chartsheet', 'comments', 'compat', 'constants', 'descriptors', 'drawing', 'formatting', 'formula', 'load_workbook', 'open', 'packaging', 'pivot', 'reader', 'styles', 'utils', 'workbook', 'worksheet', 'writer', 'xml']
Puedes ver que hay nombres como Workbook
y worksheet
que es lo que, más arriba, hemos denominado libro y hoja, respectivamente.
Para crear un documento vacio podemos hacer lo siguiente:
wb = openpyxl.Workbook()
Si inspeccionamos un poco esta instancia de Workbook
:
print(dir(wb))
Lo anterior nos mostrará algo como lo siguiente:
['_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']
Tiene un montón de cosas disponibles. Vamos a ver qué podría significar cada método público de los que tenemos ahí. Para ello vamos a usar algunas utilidades del módulo inspect
:
for member in getmembers(wb, predicate=ismethod):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
Lo anterior nos da información sobre los métodos:
add_named_range
Add an existing named_range to the list of named_ranges.
add_named_style
Add a named style
close
Close workbook file if open. Only affects read-only and write-only modes.
copy_worksheet
Copy an existing worksheet in the current workbook
create_chartsheet
create_named_range
Create a new named_range on a worksheet
create_sheet
Create a worksheet (at an optional index).
get_index
Return the index of the worksheet.
get_named_range
Return the range specified by name.
get_named_ranges
Return all named ranges
get_sheet_by_name
Returns a worksheet by its name.
get_sheet_names
index
Return the index of a worksheet.
move_sheet
Move a sheet or sheetname
remove
Remove `worksheet` from this workbook.
remove_named_range
Remove a named_range from this workbook.
remove_sheet
Remove `worksheet` from this workbook.
save
Save the current workbook under the given `filename`.
Veamos si tiene property‘s que puedan ser útiles:
for member in getmembers(openpyxl.workbook.Workbook,
predicate=isdatadescriptor):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
Lo anterior mostrará en pantalla:
active
Get the currently active sheet or None
chartsheets
A list of Chartsheets in this workbook
data_only
excel_base_date
mime_type
The mime type is determined by whether a workbook is a template or
named_styles
List available named styles
read_only
sheetnames
Returns the list of the names of worksheets in this workbook.
style_names
List of named styles
worksheets
A list of sheets in this workbook
write_only
Vemos algunos métodos y property‘s interesantes para obtener la hoja que está activa, para obtener los nombres de las hojas, borrar alguna hoja, guardar el libro,…
Vamos a acceder a una hoja puesto que ahí es donde podemos leer y/o escribir cosas. Lo podemos hacer usando la property active
:
ws = wb.active
print(type(ws))
Lo anterior debería mostrar en pantalla:
<class 'openpyxl.worksheet.worksheet.Worksheet'>
Vemos que es un objeto Worksheet
. Vamos a ver lo que podemos hacer con el mismo:
print(dir(ws))
Lo anterior debería mostrar en pantalla:
['BREAK_COLUMN', 'BREAK_NONE', 'BREAK_ROW', 'HeaderFooter', 'ORIENTATION_LANDSCAPE', 'ORIENTATION_PORTRAIT', 'PAPERSIZE_A3', 'PAPERSIZE_A4', 'PAPERSIZE_A4_SMALL', 'PAPERSIZE_A5', 'PAPERSIZE_EXECUTIVE', 'PAPERSIZE_LEDGER', 'PAPERSIZE_LEGAL', 'PAPERSIZE_LETTER', 'PAPERSIZE_LETTER_SMALL', 'PAPERSIZE_STATEMENT', 'PAPERSIZE_TABLOID', 'SHEETSTATE_HIDDEN', 'SHEETSTATE_VERYHIDDEN', 'SHEETSTATE_VISIBLE', '_WorkbookChild__title', '__class__', '__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__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_add_cell', '_add_column', '_add_row', '_cells', '_cells_by_col', '_cells_by_row', '_charts', '_clean_merge_range', '_comments', '_current_row', '_default_title', '_drawing', '_get_cell', '_hyperlinks', '_id', '_images', '_invalid_row', '_move_cell', '_move_cells', '_parent', '_path', '_pivots', '_print_area', '_print_cols', '_print_rows', '_rel_type', '_rels', '_setup', '_tables', 'active_cell', 'add_chart', 'add_data_validation', 'add_image', 'add_pivot', 'add_table', 'append', 'auto_filter', 'calculate_dimension', 'cell', 'col_breaks', 'column_dimensions', 'columns', 'conditional_formatting', 'data_validations', 'delete_cols', 'delete_rows', 'dimensions', 'encoding', 'evenFooter', 'evenHeader', 'firstFooter', 'firstHeader', 'formula_attributes', 'freeze_panes', 'insert_cols', 'insert_rows', 'iter_cols', 'iter_rows', 'legacy_drawing', 'max_column', 'max_row', 'merge_cells', 'merged_cell_ranges', 'merged_cells', 'mime_type', 'min_column', 'min_row', 'move_range', 'oddFooter', 'oddHeader', 'orientation', 'page_breaks', 'page_margins', 'page_setup', 'paper_size', 'parent', 'path', 'print_area', 'print_options', 'print_title_cols', 'print_title_rows', 'print_titles', 'protection', 'row_breaks', 'row_dimensions', 'rows', 'scenarios', 'selected_cell', 'set_printer_settings', 'sheet_format', 'sheet_properties', 'sheet_state', 'sheet_view', 'show_gridlines', 'show_summary_below', 'show_summary_right', 'tables', 'title', 'unmerge_cells', 'values', 'views']
La hoja ofrece muchas más cosas que el libro. Es lógico puesto que aquí es donde vamos a hacer la mayoría de cosas.
Como antes, vamos a inspeccionar un poco los métodos y property‘s para ver brevemente lo que hacen:
for member in getmembers(ws, predicate=ismethod):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
Y lo anterior debería mostrar en pantalla algo como lo siguiente:
add_chart
Add a chart to the sheet
add_data_validation
Add a data-validation object to the sheet. The data-validation
add_image
Add an image to the sheet.
add_pivot
add_table
Check for duplicate name in definedNames and other worksheet tables
append
Appends a group of values at the bottom of the current sheet.
calculate_dimension
Return the minimum bounding range for all cells containing data (ex. 'A1:M24')
cell
Returns a cell object based on the given coordinates.
delete_cols
Delete column or columns from col==idx
delete_rows
Delete row or rows from row==idx
insert_cols
Insert column or columns before col==idx
insert_rows
Insert row or rows before row==idx
iter_cols
Produces cells from the worksheet, by column. Specify the iteration range
iter_rows
Produces cells from the worksheet, by row. Specify the iteration range
merge_cells
Set merge on a cell range. Range is a cell range (e.g. A1:E1)
move_range
Move a cell range by the number of rows and/or columns:
set_printer_settings
Set printer settings
unmerge_cells
Remove merge on a cell range. Range is a cell range (e.g. A1:E1)
for member in getmembers(openpyxl.worksheet.worksheet.Worksheet,
predicate=isdatadescriptor):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
Y, como antes, nos mostrará nuevas cosas por pantalla:
active_cell
columns
Produces all cells in the worksheet, by column (see :func:`iter_cols`)
dimensions
Returns the result of :func:`calculate_dimension`
encoding
evenFooter
evenHeader
firstFooter
firstHeader
freeze_panes
max_column
The maximum column index containing data (1-based)
max_row
The maximum row index containing data (1-based)
merged_cell_ranges
Return a copy of cell ranges
min_column
The minimum column index containing data (1-based)
min_row
The minimium row index containing data (1-based)
oddFooter
oddHeader
page_breaks
parent
path
print_area
The print area for the worksheet, or None if not set. To set, supply a range
print_title_cols
Columns to be printed at the left side of every page (ex: 'A:C')
print_title_rows
Rows to be printed at the top of every page (ex: '1:3')
print_titles
rows
Produces all cells in the worksheet, by row (see :func:`iter_rows`)
selected_cell
sheet_view
show_gridlines
show_summary_below
show_summary_right
tables
title
values
Produces all cell values in the worksheet, by row
Nos vamos a fijar ahora en el método cell
, el cual nos permite acceder a la celda colocada en la posición que especifiquemos. Accedemos a la celda que está en la segunda fila y en la segunda columna:
[INCISO: Las filas y columnas empiezan en 1 y no en 0 como todo lo demás en Python].
c = ws.cell(2, 2)
print(type(c))
Lo anterior nos muestra en pantalla el tipo del nuevo objeto:
<class 'openpyxl.cell.cell.Cell'>
Tenemos ahora un objeto Cell
. Como anteriormente, vamos a ver lo que tiene disponible:
print(dir(c))
Que nos muestra en pantalla:
['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__', '_bind_value', '_comment', '_hyperlink', '_style', '_value', 'alignment', 'base_date', 'border', 'check_error', 'check_string', 'col_idx', 'column', 'column_letter', 'comment', 'coordinate', 'data_type', 'encoding', 'fill', 'font', 'has_style', 'hyperlink', 'internal_value', 'is_date', 'number_format', 'offset', 'parent', 'pivotButton', 'protection', 'quotePrefix', 'row', 'style', 'style_id', 'value']
Siguiendo la misma tónica que antes vamos a ver los métodos y propertys y una breve descripción:
for member in getmembers(c, predicate=ismethoddescriptor):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
for member in getmembers(openpyxl.cell.Cell, predicate=isdatadescriptor):
if not member[0].startswith("_"):
print(member[0])
try:
if member[1].__doc__.split("\n")[0]:
print(member[1].__doc__.split("\n")[0])
else:
print(member[1].__doc__.split("\n")[1])
except:
continue
print()
Y nos mostrará en pantalla:
alignment
base_date
border
col_idx
The numerical index of the column
column
column_letter
comment
Returns the comment associated with this cell
coordinate
This cell's coordinate (ex. 'A5')
data_type
encoding
fill
font
has_style
hyperlink
Return the hyperlink target or an empty string
internal_value
Always returns the value for excel.
is_date
True if the value is formatted as a date
number_format
parent
pivotButton
protection
quotePrefix
row
style
style_id
value
Get or set the value held in the cell.
Con la property value
pòdemos obtener el valor de la celda o darle un valor. Vamos a darle un valor:
c.value = 9999
Y vamos a guardar el libro:
wb.save("new.xlsx")
El anterior código debería haber creado un fichero llamado ‘new.xlsx’ en la misma carpeta desde donde estés ejecutando el código. Si abrimos el nuevo fichero que acabamos de crear con un programa de hoja de cálculos deberíamos ver algo como lo siguiente:
Resumen
Este primer artículo ha sido un poco un repaso al concepto de libro, hoja y celda, que son algunos de los conceptos más importantes en una hoja de cálculos y hemos visto un poco por encima cómo se han implementado estos conceptos en openpyxl
y qué funcionalidad asociada tienen.
Hemos visto que la información se distribuye principalmente en un libro (objeto openpyxl.workbook.Workbook
) que contiene una o más hojas o pestañas (objeto openpyxl.worksheet.worksheet.Worksheet
) que, a su vez, contienen celdas (objeto openpyxl.cell.Cell
) y pueden contener otras cosas.
Dentro de poco más. Estad atentas.