0%

66- Working with Excel Files

Read, write, and manipulate Excel spreadsheets. Automate data entry, generate reports, and extract information from .xlsx and .xls files.

Excel is everywhere. Financial reports, sales data, inventory lists, project trackers, customer databases. Many businesses run on Excel. Automating Excel tasks can save hours of manual work.
Python’s openpyxl library is the standard for reading and writing Excel files (.xlsx format). It allows you to read cell values, write data, create charts, apply formatting, and even work with formulas.
This lesson covers everything you need to work with Excel files: loading workbooks, accessing sheets, reading and writing cells, iterating over rows, applying basic formatting, and creating new Excel files from scratch. You will also learn to handle large files efficiently and work with pandas as an alternative.

🕯️ Magic Note

Excel files (.xlsx) are actually ZIP archives containing XML files. The openpyxl library parses these XML files, so you do not need to understand the underlying format. It works with both .xlsx (Excel 2007+) and .xlsm (macro-enabled) files.

Installing openpyxl
Install openpyxl for Excel file handling.

Bash

pip install openpyxl

# For reading old .xls files (Excel 97-2003)

pip install xlrd

# For writing old .xls files

pip install xlwt

Loading and Saving Workbooks
Open existing Excel files or create new ones.

Python

from openpyxl import load_workbook, Workbook

# Load existing workbook

wb = load_workbook(“data.xlsx”)

# Create new workbook

new_wb = Workbook()

# Save workbook

new_wb.save(“output.xlsx”)

# Get sheet names

print(wb.sheetnames)

💡 When loading large Excel files, use read_only=True to save memory: load_workbook(“large.xlsx”, read_only=True).
Working with Sheets
Access sheets by name, index, or create new ones.

Python

from openpyxl import load_workbook, Workbook

wb = load_workbook(“data.xlsx”)

# Get active sheet

sheet = wb.active

# Get sheet by name

sheet = wb[“Sheet1”]

# Get sheet by index

sheet = wb.worksheets[0]

# Create new sheet

new_sheet = wb.create_sheet(“NewSheet”, 0) # Insert at position 0

# Remove a sheet

wb.remove(wb[“Sheet2”])

# Rename a sheet

sheet.title = “RenamedSheet”

Reading Cell Values
Access individual cells by row and column.

Python

from openpyxl import load_workbook

wb = load_workbook(“data.xlsx”)

sheet = wb.active

# By cell coordinates (1-indexed)

cell_a1 = sheet[“A1”]

value_a1 = sheet[“A1”].value

# By row and column numbers

cell = sheet.cell(row=2, column=3)

value = cell.value

# Get cell properties

print(f”Value: {cell.value}”)

print(f”Row: {cell.row}, Column: {cell.column}”)

print(f”Coordinate: {cell.coordinate}”) # e.g., “C2”

print(f”Data type: {cell.data_type}”)

💡 Excel uses 1-indexing (A1 is row 1, column 1). This is different from Python’s 0-indexing. Remember this when looping.
Writing Cell Values
Set values in cells and save the workbook.

Python

from openpyxl import Workbook

wb = Workbook()

sheet = wb.active

# Write by coordinate

sheet[“A1”] = “Hello”

sheet[“B1”] = 42

sheet[“C1”] = 3.14159

# Write by row and column

sheet.cell(row=2, column=1, value=”World”)

sheet.cell(row=2, column=2, value=100)

# Write formulas

sheet[“D1”] = “=A1 & \” \” & A2″ # Concatenation

sheet[“E1”] = “=SUM(B1:B2)”

wb.save(“written.xlsx”)

🕯️ Magic Note

You can write formulas as strings. Excel will evaluate them when the file is opened. But openpyxl will not calculate the formula results; it stores the formula text.

Iterating Over Rows and Columns
Loop through all data in a sheet efficiently.

Python

from openpyxl import load_workbook

wb = load_workbook(“data.xlsx”)

sheet = wb.active

# Iterate over all rows

for row in sheet.iter_rows(values_only=True):

print(row) # tuple of values

# Iterate over rows with rows parameter

for row in sheet.iter_rows(min_row=2, max_row=10, min_col=1, max_col=5, values_only=True):

name, age, city = row[0], row[1], row[2]

print(f”{name} is {age} years old”)

# Iterate over columns

for col in sheet.iter_cols(min_row=1, max_row=5, min_col=1, max_col=3, values_only=True):

print(col)

# Get used range

for row in sheet.iter_rows(values_only=True):

if all(cell is None for cell in row):

break

print(row)

💡 Using values_only=True returns the cell values directly instead of Cell objects. This is faster and more memory-efficient for large sheets.
Adding Data from List of Dictionaries
Write structured data to Excel.

Python

from openpyxl import Workbook

data = [

{“name”: “Ali”, “age”: 25, “city”: “Tehran”},

{“name”: “Sara”, “age”: 30, “city”: “Shiraz”},

{“name”: “Reza”, “age”: 28, “city”: “Isfahan”}

]

def write_to_excel(data, output_path):

wb = Workbook()

sheet = wb.active

# Write headers

if data:

headers = list(data[0].keys())

for col, header in enumerate(headers, 1):

sheet.cell(row=1, column=col, value=header)

# Write data rows

for row_idx, row_data in enumerate(data, 2):

for col_idx, key in enumerate(headers, 1):

sheet.cell(row=row_idx, column=col_idx, value=row_data.get(key))

wb.save(output_path)

print(f”Excel file saved: {output_path}”)

write_to_excel(data, “output.xlsx”)

Basic Formatting (Fonts, Colors, Alignment)
Apply styles to make Excel files more readable.

Python

from openpyxl import Workbook

from openpyxl.styles import Font, PatternFill, Alignment, Border, Side

wb = Workbook()

sheet = wb.active

sheet.title = “Formatted”

# Add sample data

data = [[“Name”, “Score”], [“Ali”, 95], [“Sara”, 87], [“Reza”, 92]]

for row_idx, row in enumerate(data, 1):

for col_idx, value in enumerate(row, 1):

sheet.cell(row=row_idx, column=col_idx, value=value)

# Header formatting

header_font = Font(bold=True, size=12, color=”FFFFFF”)

header_fill = PatternFill(start_color=”3366CC”, end_color=”3366CC”, fill_type=”solid”)

header_alignment = Alignment(horizontal=”center”, vertical=”center”)

for col in range(1, 3):

cell = sheet.cell(row=1, column=col)

cell.font = header_font

cell.fill = header_fill

cell.alignment = header_alignment

# Border for all cells

thin_border = Border(

left=Side(style=”thin”),

right=Side(style=”thin”),

top=Side(style=”thin”),

bottom=Side(style=”thin”)

)

for row in sheet.iter_rows(max_row=4, max_col=2):

for cell in row:

cell.border = thin_border

# Conditional formatting (color based on value)

for row in range(2, 5):

score_cell = sheet.cell(row=row, column=2)

if score_cell.value and score_cell.value >= 90:

score_cell.font = Font(color=”006600″, bold=True)

score_cell.fill = PatternFill(start_color=”CCFFCC”, end_color=”CCFFCC”, fill_type=”solid”)

elif score_cell.value and score_cell.value < 90:

score_cell.font = Font(color=”CC0000″)

# Adjust column width

sheet.column_dimensions[“A”].width = 15

sheet.column_dimensions[“B”].width = 12

wb.save(“formatted.xlsx”)

🕯️ Magic Note

Formatting in openpyxl applies to individual cells, not ranges. To format many cells at once, iterate over them. The PatternFill class uses hex color codes (e.g., “3366CC” is a shade of blue).

Creating Charts
Add charts to Excel files directly from Python.

Python

from openpyxl import Workbook

from openpyxl.chart import BarChart, Reference

wb = Workbook()

sheet = wb.active

sheet.title = “Sales Data”

# Sample data

data = [

[“Month”, “Sales”, “Expenses”],

[“Jan”, 1000, 600],

[“Feb”, 1200, 700],

[“Mar”, 900, 500],

[“Apr”, 1500, 800],

[“May”, 1800, 900]

]

for row in data:

sheet.append(row)

# Create bar chart

chart = BarChart()

chart.title = “Monthly Performance”

chart.x_axis.title = “Month”

chart.y_axis.title = “Amount ($)”

# Define data range

data_ref = Reference(sheet, min_col=2, max_col=3, min_row=1, max_row=6)

categ_ref = Reference(sheet, min_col=1, min_row=2, max_row=6)

chart.add_data(data_ref, titles_from_data=True)

chart.set_categories(categ_ref)

# Add chart to sheet

sheet.add_chart(chart, “E5”)

wb.save(“chart.xlsx”)

Merging Cells
Merge cells for headers or titles.

Python

from openpyxl import Workbook

from openpyxl.styles import Alignment, Font

wb = Workbook()

sheet = wb.active

# Merge cells A1 to E1

sheet.merge_cells(“A1:E1”)

sheet[“A1”] = “Annual Report 2025”

sheet[“A1”].font = Font(size=16, bold=True)

sheet[“A1″].alignment = Alignment(horizontal=”center”)

# Merge by row and column indices

sheet.merge_cells(start_row=2, start_column=1, end_row=2, end_column=5)

sheet[“A2”] = “Subtitle”

# Unmerge if needed

# sheet.unmerge_cells(“A1:E1”)

wb.save(“merged.xlsx”)

Working with Large Excel Files (Read-Only Mode)
For very large files, use read-only or write-only mode to save memory.

Python

from openpyxl import load_workbook

# Read-only mode (minimal memory, cannot write)

wb = load_workbook(“large_file.xlsx”, read_only=True)

sheet = wb.active

for row in sheet.iter_rows(values_only=True):

# Process each row without loading entire file into memory

process_row(row)

wb.close() # Important for read-only mode

# Write-only mode (stream writing, cannot read)

from openpyxl import Workbook

wb = Workbook(write_only=True)

sheet = wb.create_sheet()

for row_data in large_dataset:

sheet.append(row_data) # Write rows one by one without keeping in memory

wb.save(“large_output.xlsx”)

🕯️ Magic Note

Read-only mode is essential for processing huge Excel files (hundreds of thousands of rows). Write-only mode is useful for generating large files without memory overhead. Both have limitations (no formatting, no random access), but they save significant memory.

Reading Old .xls Files with xlrd
For legacy Excel files (.xls), use the xlrd library.

Python

import xlrd

# Open old Excel file

workbook = xlrd.open_workbook(“old_data.xls”)

sheet = workbook.sheet_by_index(0)

# Read cell values

for row in range(sheet.nrows):

for col in range(sheet.ncols):

value = sheet.cell_value(row, col)

print(value, end=” “)

print()

# Note: xlrd only reads, does not write

# For writing .xls files, use xlwt

Using pandas as an Alternative
Pandas provides a simpler interface for Excel files, especially for data analysis.

Python

import pandas as pd

# Read Excel file

df = pd.read_excel(“data.xlsx”, sheet_name=”Sheet1″)

print(df.head()) # First 5 rows

print(df.info()) # Data types and info

# Filter data

filtered = df[df[“age”] > 25]

# Write to Excel

filtered.to_excel(“filtered_output.xlsx”, index=False)

# Read multiple sheets

all_sheets = pd.read_excel(“data.xlsx”, sheet_name=None)

for sheet_name, df_sheet in all_sheets.items():

print(f”Sheet: {sheet_name}, Shape: {df_sheet.shape}”)

💡 Pandas is excellent for data analysis and manipulation. For simple read/write operations without formatting, pandas is often easier. For complex formatting (colors, fonts, charts), use openpyxl directly.
Practical Example: Excel Report Generator
Combine CSV data, formatting, and charts into a professional report.

Python

import csv

from openpyxl import Workbook

from openpyxl.styles import Font, PatternFill, Alignment, Border, Side

from openpyxl.chart import BarChart, Reference

from datetime import datetime

def create_excel_report(csv_path, output_path):

“””Generate formatted Excel report from CSV data.”””

# Read CSV data

with open(csv_path, “r”, encoding=”utf-8″) as f:

reader = csv.DictReader(f)

data = list(reader)

headers = reader.fieldnames

# Create workbook

wb = Workbook()

sheet = wb.active

sheet.title = “Report”

# Title and metadata

sheet.merge_cells(“A1:D1”)

sheet[“A1″] = f”Data Report – {datetime.now().strftime(‘%Y-%m-%d’)}”

sheet[“A1”].font = Font(size=14, bold=True)

sheet[“A1″].alignment = Alignment(horizontal=”center”)

sheet[“A2″] = f”Total Records: {len(data)}”

sheet[“A2”].font = Font(italic=True)

# Write headers

for col, header in enumerate(headers, 1):

cell = sheet.cell(row=4, column=col, value=header)

cell.font = Font(bold=True, color=”FFFFFF”)

cell.fill = PatternFill(start_color=”3366CC”, end_color=”3366CC”, fill_type=”solid”)

cell.alignment = Alignment(horizontal=”center”)

# Write data rows

for row_idx, row in enumerate(data, 5):

for col, header in enumerate(headers, 1):

value = row.get(header, “”)

sheet.cell(row=row_idx, column=col, value=value)

# Auto-adjust column widths

for col in range(1, len(headers) + 1):

max_length = 0

column_letter = sheet.cell(row=4, column=col).column_letter

for row in range(4, len(data) + 5):

cell_value = sheet.cell(row=row, column=col).value

if cell_value:

max_length = max(max_length, len(str(cell_value)))

sheet.column_dimensions[column_letter].width = min(max_length + 2, 30)

# Save report

wb.save(output_path)

print(f”Report saved: {output_path}”)

# create_excel_report(“sales.csv”, “sales_report.xlsx”)

Common Mistakes with Excel Files
  • Forgetting that row and column indices start at 1 (not 0)
  • Loading large files without read_only mode (memory exhaustion)
  • Not closing read_only workbooks (wb.close())
  • Assuming formulas are evaluated (openpyxl stores formulas, not results)
  • Using .xls files with openpyxl (use xlrd for old format)
  • Forgetting to save after making changes
Check Your Understanding
  • How do you load an existing Excel file with openpyxl?
  • Write code to read the value of cell B3 from an Excel sheet.
  • How do you write a list of dictionaries to an Excel file with headers?
  • What is the difference between read_only and write_only modes?
  • How do you add a bar chart to an Excel sheet?
  • What library would you use to read old .xls files?

⚡ Whisper

Excel files are the language of business. Spreadsheets track sales, inventory, budgets, and schedules. With openpyxl, you speak that language. You read cells like sheet[“B3”].value. You write rows with sheet.append(row). You add charts, merge cells, apply colors, and set fonts. The same reports that take hours manually run in seconds automatically. Monthly reports become one script. Data entry becomes a loop. Inventory updates become a function. Excel automation is not just convenience. It is transformation. You save time. You reduce errors. You focus on analysis, not copying. Learn the basics. Then iterate. Then format. Then chart. Your spreadsheets will never be the same.

Related posts