Skip to main content

GSParse

PyPI version Python License: MIT

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

  • 📊 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_strings mode 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) is A1.
  • Prefer XLSX (the default): it reads every worksheet. The csv format is deprecated — Google's CSV export only returns a single sheet, so it emits a DeprecationWarning.
  • 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, which uv sync picks 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)

Source distribution for gsparse 0.2.3
File Size Uploaded
gsparse-0.2.3.tar.gz 26.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gsparse 0.2.3
File Interpreter ABI Platform
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

Release history Release notifications | RSS feed

This release

0.2.3 This release

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page