GSParse
GSParse is a lightweight library for extracting data from public Google Sheets by URL — no API keys, no OAuth, no service accounts. It downloads a spreadsheet's export, parses every worksheet, and gives you a clean, convenient API for cells, rows, columns, ranges, search, and export.
from gsparse import GSParseClient
client = GSParseClient()
spreadsheet = client.load_spreadsheet("https://docs.google.com/spreadsheets/d/<SHEET_ID>/edit")
for worksheet in spreadsheet:
print(worksheet.name, worksheet.row_count, "x", worksheet.column_count)
Table of Contents
- Features
- Installation
- Quick Start
- Usage
- API Reference
- Notes & Limitations
- Development
- Contributing
- License
Features
- 📊 Reads all worksheets of a spreadsheet in one call (XLSX)
- 🔗 Loads directly by Google Sheets URL — no credentials required
- 🧩 Convenient object model:
Spreadsheet→Worksheet→Cell/Range - 🔍 Search by exact value or regular expression
- 📋 Export worksheets to lists of dictionaries (records)
- 🧹 Helpers to strip empty rows/columns
- 🔤 Optional
preserve_stringsmode to keep every value as text - 🔄 Automatic retries and encoding auto-detection
- ⚡ Supports both XLSX (default) and CSV formats
Installation
pip install gsparse
Or with uv:
uv add gsparse
Requirements: Python ≥ 3.10 · requests · chardet · openpyxl
Quick Start
from gsparse import GSParseClient
client = GSParseClient()
url = "https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit"
spreadsheet = client.load_spreadsheet(url)
# Inspect the workbook
print("Title:", spreadsheet.title)
print("Sheets:", spreadsheet.worksheet_names)
# Grab the first worksheet
worksheet = spreadsheet.get_first_worksheet()
# Read a single cell (1-based coordinates)
cell = worksheet.get_cell(1, 1) # A1
print(cell.address, "=", cell.value)
# Export rows as dictionaries (first row is used as headers)
for row in worksheet.get_data_as_dict():
print(row)
Usage
Loading data
# Load the whole spreadsheet (XLSX — reads every worksheet)
spreadsheet = client.load_spreadsheet(url)
# Load a single worksheet by name
worksheet = client.load_worksheet(url, "Sheet1")
# Load the first worksheet
worksheet = client.load_worksheet(url)
By default values keep their native types (numbers stay numbers). Pass
preserve_strings=True to the client if you want every cell returned as a string:
client = GSParseClient(preserve_strings=True)
Worksheets
spreadsheet.worksheet_count # number of sheets
spreadsheet.worksheet_names # ["Sheet1", "Data", ...]
spreadsheet.get_worksheet("Data") # by name (or None)
spreadsheet.get_worksheet_by_index(0)
spreadsheet.get_first_worksheet()
spreadsheet.get_last_worksheet()
# Dict-like and iterable access
sheet = spreadsheet["Data"]
if "Data" in spreadsheet:
...
for sheet in spreadsheet:
print(sheet.name)
Cells and ranges
worksheet = spreadsheet.get_first_worksheet()
# Single cell (row, column), both starting at 1
cell = worksheet.get_cell(2, 3) # C2
print(cell.address, cell.value, cell.is_empty)
# A range: get_range(start_row, end_row, start_column, end_column)
rng = worksheet.get_range(1, 10, 1, 3) # A1:C10
cells = worksheet.get_cells_in_range(rng)
# ...or build a range from an A1 address
rng = worksheet.get_range_by_address("A1:C10")
# Whole rows / columns
first_row = worksheet.get_row(1)
first_col = worksheet.get_column(1)
# Raw values as lists of lists
rows = worksheet.get_rows()
columns = worksheet.get_columns()
Exporting to dictionaries
# A single worksheet -> list of records
records = worksheet.get_data_as_dict(headers_row=1)
# Every worksheet at once -> {sheet_name: [records, ...]}
data = spreadsheet.export_to_dict(headers_row=1)
# Straight from a URL
data = client.export_to_dict(url, headers_row=1)
Searching
# Exact value across all worksheets
cells = client.find_data(url, "Moscow")
# Regular expression
numbers = client.find_by_pattern(url, r"^\d+$")
# Or on already-loaded objects
worksheet.find_cells_by_value("Moscow")
worksheet.find_cells_by_pattern(r"@\w+\.\w+") # emails
spreadsheet.find_cells_by_value(42)
Cleaning data
# Return a new, cleaned worksheet (originals untouched)
clean = worksheet.remove_empty_rows()
clean = worksheet.remove_empty_columns()
clean = worksheet.clean_data() # both
# ...or mutate in place
worksheet.clean_data_inplace()
Parsing from a CSV string
Useful for tests or data you already have in memory. The delimiter is auto-detected:
csv_data = """Name,Age,City
John,25,Moscow
Mary,30,St. Petersburg"""
worksheet = client.load_from_csv_string(csv_data, "My Data")
print(worksheet.get_data_as_dict())
API Reference
GSParseClient(timeout=30, max_retries=3, preserve_strings=False)
| Method | Description |
|---|---|
load_spreadsheet(url, format_type="xlsx") |
Load the whole spreadsheet |
load_worksheet(url, worksheet_name=None, format_type="xlsx") |
Load one worksheet (first if name is None) |
load_from_csv_string(csv_string, worksheet_name="Sheet1") |
Parse a worksheet from a CSV string |
export_to_dict(url, headers_row=1, format_type="xlsx") |
Export all worksheets to records |
find_data(url, value, format_type="xlsx") |
Find cells by exact value |
find_by_pattern(url, pattern, format_type="xlsx") |
Find cells by regular expression |
get_sheet_info(url) |
Basic info about the spreadsheet |
list_worksheets(url) |
Map of {worksheet_name: gid} |
Spreadsheet
Properties: title, worksheets, url, worksheet_count, worksheet_names
Methods: get_worksheet(name), get_worksheet_by_index(index), get_first_worksheet(), get_last_worksheet(), add_worksheet(ws), remove_worksheet(name), get_all_cells(), get_data_summary(), find_cells_by_value(value), find_cells_by_pattern(pattern), export_to_dict(headers_row=1)
Also supports iteration (for ws in spreadsheet), membership (name in spreadsheet) and indexing (spreadsheet[name]).
Worksheet
Properties: name, data, row_count, column_count
Methods: get_cell(row, column), get_range(start_row, end_row, start_column, end_column), get_range_by_address(address), get_all_cells(), get_cells_in_range(range_obj), get_row(n), get_column(n), get_rows(), get_columns(), get_data_as_dict(headers_row=1), find_cells_by_value(value), find_cells_by_pattern(pattern), remove_empty_rows(), remove_empty_columns(), clean_data() (plus *_inplace() variants)
Cell
Properties: row, column, value, formatted_value, formula, address (e.g. "B2"), is_empty
Range
Properties: start_row, end_row, start_column, end_column, worksheet_name, address, row_count, column_count, cell_count
Methods: contains_cell(row, column), get_cells(data), Range.from_address("Sheet1!A1:B2")
Notes & Limitations
- The spreadsheet must be publicly accessible ("Anyone with the link"). GSParse reads the public export endpoint and does not authenticate.
- All coordinates are 1-based —
get_cell(1, 1)isA1. - Prefer XLSX (the default): it reads every worksheet. The
csvformat is deprecated — Google's CSV export only returns a single sheet, so it emits aDeprecationWarning. - If an XLSX download fails, the client automatically falls back to CSV.
Development
# Install the project with its dev dependency group
uv sync # installs the "dev" group by default
# Run the test suite
uv run pytest # or just: pytest
# Lint
uv run ruff check src/ # or just: ruff check src/
Using plain
pip? Install the package in editable mode and add the tools manually:pip install -e . pytest ruff(the dev tools live in a PEP 735[dependency-groups]block, whichuv syncpicks up automatically).
Contributing
Contributions are welcome! Feel free to open an issue or submit a pull request on GitHub.
License
Released under the MIT License.
Metadata
Release files for gsparse 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gsparse-0.2.3.tar.gz | 26.9 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gsparse-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 52.4 kB
Release files / gsparse-0.2.3.tar.gz
| Download URL | gsparse-0.2.3.tar.gz |
|---|---|
| Size | 26.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9d1b529daa42328c2d3f5c1ae14ac7330b5cb52cce1f68e8d8b0468e6696c50d
|
|
BLAKE2b-256 checksum How to use checksums |
9149dd3399e60aec1e9924e00d8226ee6693a7234361fc9ef70ec37c5d3e6c06
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Release files / gsparse-0.2.3-py3-none-any.whl
| Download URL | gsparse-0.2.3-py3-none-any.whl |
|---|---|
| Size | 25.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
7cfdb7896bb1b5bebdc5175f8568d578dc53b480aadb3fc772ffeff90ef4a8bf
|
|
BLAKE2b-256 checksum How to use checksums |
d99d5f179ff82af3a9c6befd0d1eac3bd4a72b5ee5d244c60a9778e86e62b3eb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|