Chapter 15 - Google Sheets (Python)
Here's a concise walkthrough of the main ideas in Chapter 15, each with a small example.
Why Google Sheets + EZSheets
- Google Sheets is a free, web-based spreadsheet app with an official API, but that API is complex; EZSheets is a wrapper library that simplifies common tasks.
- You'll use EZSheets to create/upload/download spreadsheets, read/write data, and work with Google Forms responses and a fake blockchain project.
Installing and setting up EZSheets
You install EZSheets with pip, then do a one-time Google Cloud setup to get credentials and tokens.
One-time Google Cloud setup (5 parts)
- Create a Google Cloud project – at
https://console.cloud.google.com, click "Select a project" → "New Project", accept the generated name/ID, click "Create". - Enable APIs – in "APIs & Services → Library", enable both "Google Sheets API" and "Google Drive API".
- Configure OAuth consent screen – choose External, set app name, support email, and developer contact email, add scopes for
.../auth/driveand.../auth/spreadsheets, add test users (the Gmail addresses whose spreadsheets your scripts will access), then review the summary. - Create credentials – "APIs & Services → Credentials → + Create Credentials → OAuth client ID → Desktop App → Create → Download JSON". Rename the downloaded file to
credentials-sheets.jsonand place it next to your scripts. - Log in with credentials – in the folder containing
credentials-sheets.json, start Python and runimport ezsheets; EZSheets opens a browser, you approve access for Sheets and Drive, and it createstoken-drive.pickleandtoken-sheets.picklefiles in that folder.
If you later accidentally leak these files (they act like passwords for Sheets access), revoke them by deleting the OAuth client in the Cloud Console's Credentials page, then recreate credentials and tokens.
Spreadsheet objects
A Google spreadsheet is an ezsheets.Spreadsheet object with attributes like title, id, url, sheetTitles, and sheets.
Creating, uploading, listing spreadsheets
New blank spreadsheet:
import ezsheets
ss = ezsheets.Spreadsheet()
ss.title = 'Title of My New Spreadsheet'
print(ss.url, ss.id)
Load an existing spreadsheet (by redirect URL, full URL, or plain ID):
example_ss = ezsheets.Spreadsheet('https://autbor.com/examplegs')
ss1 = ezsheets.Spreadsheet(
'https://docs.google.com/spreadsheets/d/1TzOJxh.../'
)
ss2 = ezsheets.Spreadsheet('1TzOJxhNKr15tzdZxTqtQ3EmDP6em_elnbtmZIcyu8vI')
Upload a local file (Excel, ODS, CSV, TSV) to Google Sheets:
ss = ezsheets.upload('my_spreadsheet.xlsx')
print(ss.title) # 'my_spreadsheet'
List all spreadsheets in your Google account (including Trash):
ezsheets.listSpreadsheets()
# {'1J-Jx6N...': 'Education Data', ...}
Accessing spreadsheet attributes and copying sheets
You can copy a sheet from a read-only spreadsheet into a new one:
import ezsheets
example_ss = ezsheets.Spreadsheet('https://autbor.com/examplegs')
ss = ezsheets.Spreadsheet()
example_ss.sheets[0].copyTo(ss)
ss.sheets[0].delete() # remove default Sheet1
ss.title = 'Sweigart Books'
print(ss.sheetTitles) # ('Copy of Books',)
Refresh to reflect remote changes:
ss.refresh()
Downloading spreadsheets in various formats
ss = ezsheets.Spreadsheet('https://autbor.com/examplegs')
ss.downloadAsExcel() # 'Sweigart_Books.xlsx'
ss.downloadAsODS() # 'Sweigart_Books.ods'
ss.downloadAsCSV() # first sheet only
ss.downloadAsTSV()
ss.downloadAsPDF()
ss.downloadAsHTML() # ZIP of HTML files
ss.downloadAsExcel('custom.xlsx')
CSV/TSV can only contain one sheet; EZSheets downloads the first sheet unless you reorder sheets.
Deleting spreadsheets
Move to Drive Trash:
ss.delete()
Permanently delete:
ss.delete(permanent=True)
listSpreadsheets() still shows trashed spreadsheets until they're permanently deleted.
Sheet objects
Each Spreadsheet has one or more Sheet objects (worksheets) accessible via ss.sheets[index], ss[sheet_title], or ss.Sheet().
Creating, moving, deleting sheets
ss = ezsheets.Spreadsheet()
ss.title = 'Multiple Sheets'
print(ss.sheetTitles) # ('Sheet1',)
ss.Sheet('Spam')
ss.Sheet('Eggs')
print(ss.sheetTitles) # ('Sheet1', 'Spam', 'Eggs')
ss.Sheet('Bacon', 0) # insert at index 0
print(ss.sheetTitles) # ('Bacon', 'Sheet1', 'Spam', 'Eggs')
# Reorder using index attribute
ss.sheets[0].index = 2
print(ss.sheetTitles) # ('Sheet1', 'Spam', 'Bacon', 'Eggs')
Delete and clear:
ss.sheets[0].delete() # delete first sheet
sheet = ss['Eggs']
sheet.clear() # clear all cell data, sheet remains
Copy sheets between spreadsheets:
ss1 = ezsheets.Spreadsheet()
ss1.title = 'First Spreadsheet'
ss1.sheets[0].title = 'Spam'
ss2 = ezsheets.Spreadsheet()
ss2.title = 'Second Spreadsheet'
ss2.sheets[0].title = 'Eggs'
ss1[0].updateRow(1, ['Some', 'data', 'in', 'the', 'first', 'row'])
ss1[0].copyTo(ss2)
print(ss2.sheetTitles) # ('Eggs', 'Copy of Spam')
Reading and writing cell data
Basic cell access
String-style address: 'A1'. Tuple-style address: (column, row) as integers (1-based).
ss = ezsheets.Spreadsheet()
ss.title = 'My Spreadsheet'
sheet = ss.sheets[0]
sheet['A1'] = 'Name'
sheet['B1'] = 'Age'
sheet['C1'] = 'Favorite Movie'
sheet['A2'] = 'Alice'
sheet['B2'] = 30 # will be stored and returned as '30'
sheet['C2'] = 'RoboCop'
print(sheet['A1']) # 'Name'
print(sheet['A2']) # 'Alice'
print(sheet[2, 1]) # 'Age' (column 2, row 1 == B1)
print(sheet['B2']) # '30'
All values are returned as strings, even numbers.
Converting addresses and columns
Helper functions:
import ezsheets
print(ezsheets.convertAddress('A2')) # (1, 2)
print(ezsheets.convertAddress(1, 2)) # 'A2'
print(ezsheets.getColumnLetterOf(2)) # 'B'
print(ezsheets.getColumnNumberOf('B')) # 2
print(ezsheets.getColumnLetterOf(999)) # 'ALK'
print(ezsheets.getColumnNumberOf('ZZZ')) # 18278
Reading and writing whole rows/columns
Updating many cells one-by-one is slow; EZSheets lets you get/update entire rows or columns at once.
import ezsheets
ss = ezsheets.upload('produceSales3.xlsx')
sheet = ss.sheets[0]
print(sheet.getRow(1)) # ['PRODUCE', 'COST PER POUND', 'POUNDS SOLD', 'TOTAL', '', '']
print(sheet.getRow(2)) # ['Potatoes', '0.86', '21.6', '18.58', '', '']
print(sheet.getColumn(1)) # column A values
print(sheet.getColumn('A')) # same as getColumn(1)
Update a row:
sheet.updateRow(3, ['Pumpkin', '11.50', '20', '230'])
print(sheet.getRow(3)) # ['Pumpkin', '11.50', '20', '230', '', '']
Update a column with transformed data:
column_one = sheet.getColumn(1)
for i, value in enumerate(column_one):
column_one[i] = value.upper()
sheet.updateColumn(1, column_one)
Update all rows in one request:
rows = sheet.getRows()
rows[10][0] = 'PUMPKIN'
rows[10][2] = '400'
rows[10][3] = '904'
sheet.updateRows(rows)
Resize the sheet:
print(sheet.rowCount) # e.g. 23758
print(sheet.columnCount) # 6
sheet.columnCount = 4 # deletes extra columns at the end
Google Forms
- Google Forms can write responses into a Google Sheets spreadsheet.
- With EZSheets, your Python scripts can read that spreadsheet and automate responses (e.g., check for new rows and send notifications using scheduling from Chapter 19 and messaging from Chapter 20).
- This demonstrates Python as "glue" connecting web forms + Sheets + schedulers + notifications.
Project: Fake Blockchain Cryptocurrency Scam (Boringcoin)
Spreadsheet at https://autbor.com/boringcoin stores a toy "blockchain" with columns: sender, recipient, amount.
Rules:
- If sender is
'PRE-MINE', coins are created from nothing and added to recipient. - Otherwise, subtract amount from sender and add to recipient.
- All balances are tracked by name.
Step 1: Audit the blockchain (balances)
import ezsheets
ss = ezsheets.Spreadsheet('https://autbor.com/boringcoin')
accounts = {} # name -> balance
for row in ss.sheets[0].getRows():
sender, recipient, amount = row[0], row[1], int(row[2])
if sender == 'PRE-MINE':
accounts.setdefault(recipient, 0)
accounts[recipient] += amount
else:
accounts.setdefault(sender, 0)
accounts.setdefault(recipient, 0)
accounts[sender] -= amount
accounts[recipient] += amount
print(accounts)
total = 0
for amount in accounts.values():
total += amount
print('Total Boringcoins:', total)
Output shows balances per account and total (1,000,000,000 Boringcoins from the pre-mine).
Step 2: Make transactions via script
Program addBoringcoinTransaction.py:
- Reads 3 command-line args: sender, recipient, amount.
- Appends a new row to the bottom of the blockchain sheet.
# addBoringcoinTransaction.py
import sys, ezsheets
if len(sys.argv) != 4:
print('Usage: python addBoringcoinTransaction.py SENDER RECIPIENT AMOUNT')
sys.exit(1)
sender, recipient, amount = sys.argv[1], sys.argv[2], sys.argv[3]
ss = ezsheets.Spreadsheet('https://autbor.com/boringcoin')
sheet = ss.sheets[0]
rows = sheet.getRows()
rows.append([sender, recipient, amount])
sheet.updateRows(rows)
This demonstrates combining command-line arguments, EZSheets, and Google Sheets to modify a shared "ledger".
Overall idea of the chapter
Chapter 15 shows how to automate Google Sheets with EZSheets: one-time OAuth setup, creating/uploading/downloading spreadsheets, reading and writing cells/rows/columns in bulk, managing sheets, and building real projects (Boringcoin audit + transactions). The key advantage over openpyxl is that Google Sheets are live on the web and shareable.