Skip to main content

Google Sheets Parser

Project description

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: SpreadsheetWorksheetCell / 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-basedget_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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gsparse-0.2.3.tar.gz (26.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gsparse-0.2.3-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file gsparse-0.2.3.tar.gz.

File metadata

  • Download URL: gsparse-0.2.3.tar.gz
  • Upload date:
  • Size: 26.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gsparse-0.2.3.tar.gz
Algorithm Hash digest
SHA256 9d1b529daa42328c2d3f5c1ae14ac7330b5cb52cce1f68e8d8b0468e6696c50d
MD5 304f75d63d8f277e176af71069b8f2e0
BLAKE2b-256 9149dd3399e60aec1e9924e00d8226ee6693a7234361fc9ef70ec37c5d3e6c06

See more details on using hashes here.

File details

Details for the file gsparse-0.2.3-py3-none-any.whl.

File metadata

  • Download URL: gsparse-0.2.3-py3-none-any.whl
  • Upload date:
  • Size: 25.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gsparse-0.2.3-py3-none-any.whl
Algorithm Hash digest
SHA256 7cfdb7896bb1b5bebdc5175f8568d578dc53b480aadb3fc772ffeff90ef4a8bf
MD5 0e39171f6cecbedc55d574282744a31d
BLAKE2b-256 d99d5f179ff82af3a9c6befd0d1eac3bd4a72b5ee5d244c60a9778e86e62b3eb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page