gdzapi
Universal asynchronous and synchronous Python client for 9 GDZ providers with declarative engine and Pydantic v2 validation.
Quick Start · 9 Providers · Features · Usage Guide · Architecture · License
Why gdzapi
Most school scraping tools are fragmented across dozens of fragile files, lack asynchronous capability, crash on repeated awaits, or fail to handle modern SPA/lazy-loading structures.
gdzapi unifies 9 major school solution portals under a single, declarative architecture. Instead of maintaining separate scrapers for each website, all providers are driven by a single engine with non-blocking initialization, strict timeouts, protocol normalization, and Pydantic v2 models.
Supported Providers
| Provider | Alias | Sync Method | Async Method | Content Type | Status |
|---|---|---|---|---|---|
| GDZ.ru | "gdz" |
Client.gdz() |
AsyncClient.gdz() |
Images | Stable |
| Euroki.org | "euroki" |
Client.euroki() |
AsyncClient.euroki() |
Images (lazy) | Stable |
| MegaResheba.ru | "megaresheba" |
Client.megaresheba() |
AsyncClient.megaresheba() |
Images | Stable |
| GDZ-Raketa.ru | "raketa" |
Client.raketa() |
AsyncClient.raketa() |
Text, MathJax & Images | Stable |
| Reshak.ru | "reshak" |
Client.reshak() |
AsyncClient.reshak() |
Images | Stable |
| Pomogalka.me | "pomogalka" |
Client.pomogalka() |
AsyncClient.pomogalka() |
Images | Stable |
| Resh.Skysmart.ru | "skysmart" |
Client.skysmart() |
AsyncClient.skysmart() |
Steps & Images | Stable (SSR) |
| GDZ-Putina.fun | "putina" |
Client.putina() |
AsyncClient.putina() |
High-res Images (JSON) | Stable |
| GDZ.ltd | "ltd" |
Client.ltd() |
AsyncClient.ltd() |
High-res Images | Stable |
Features
- Single Unified Engine: Zero code duplication. Switch providers with a string:
Client("reshak")orClient.reshak(). - Dual Mode (Sync & Async): Native asynchronous
aiohttpengine alongside a robust synchronousrequestsclient. - Rich Solution Models:
Solutionmodels supportimage_src,text, andhtmlfor sites with formulaic or written step-by-step explanations. - Safe Lazy Navigation: Await properties (
subject.books,book.pages,page.solutions) as many times as you like withoutcannot reuse already awaited coroutinecrashes. - Pydantic v2 Models: Data transfer objects (
Class,Subject,Book,Page,Solution) with strict typing. - Fast Search: Server-side instant search on supported providers (e.g. Euroki) and concurrent catalog scanning on others.
- Resilient Networking: Explicit 15s default timeouts, modern User-Agent handling (including SSR emulation for SPAs), and protocol-relative URL normalization (
//...→https://...).
Installation
pip install gdzapi --upgrade
From source for development:
git clone https://github.com/maybewewill/gdzAPI.git
cd gdzAPI
pip install -e ".[dev]"
Quick Start
Asynchronous (Recommended)
import asyncio
from gdzapi import AsyncClient, Provider
async def main():
# Connect to any provider: "gdz", "reshak", "raketa", "euroki", etc.
async with AsyncClient(Provider.RESHAK, timeout=15) as client:
subjects = await client.get_subjects()
math = next(s for s in subjects if "математика" in s.name.lower())
books = await math.books
print(f"Book: {books[0].name}")
pages = await books[0].pages
if pages:
solutions = await pages[0].solutions
print(f"Solution image: {solutions[0].image_src}")
if __name__ == "__main__":
asyncio.run(main())
Synchronous
from gdzapi import Client
# Using provider factory method
with Client.pomogalka(timeout=15) as client:
for cls in client.classes:
if cls.id == 5:
print(f"Grade: {cls.name}")
for subj in cls.subjects:
books = subj.books
if books:
print(f"Book: {books[0].name}")
pages = books[0].pages
if pages:
print(f"Page 1 solution: {pages[0].solutions[0].image_src}")
break
break
Usage Guide
1. GDZ-Raketa (Text & Math Solutions)
gdz-raketa.ru provides full text answers with formulas in addition to images:
from gdzapi import Client
client = Client.raketa()
classes = client.classes
book = classes[4].subjects[0].books[0] # 5th grade, 1st subject, 1st book
page = book.pages[0]
solution = page.solutions[0]
print("Text explanation:", solution.text)
if solution.image_src:
print("Illustration:", solution.image_src)
2. Reshak.ru
from gdzapi import Client
client = Client.reshak()
subjects = client.subjects
for subj in subjects:
if "математика" in subj.name.lower():
for book in subj.books:
print(f"{book.name} -> {book.url}")
pages = book.pages
if pages:
print(f"First exercise solution: {pages[0].solutions[0].image_src}")
break
break
3. Euroki (Fast Search)
from gdzapi import Client
client = Client.euroki()
books = client.search_books("Геометрия 8 класс")
for book in books:
print(f"Found: {book.name}")
pages = book.pages
if pages:
solutions = pages[0].solutions
print(f"Image: {solutions[0].image_src}")
break
4. Resh.Skysmart.ru
from gdzapi import Client
client = Client.skysmart()
classes = client.classes
print(f"Loaded {len(classes)} grades from Skysmart")
5. GDZ-Putina.fun & GDZ.ltd
from gdzapi import Client
# GDZ-Putina (JSON API integration)
client_putina = Client.putina()
books = client_putina.classes[0].subjects[0].books
if books:
page = books[0].pages[0]
print(f"Putina solution: {page.solutions[0].image_src}")
# GDZ.ltd
client_ltd = Client.ltd()
math_books = client_ltd.classes[0].subjects[0].books
if math_books:
print(f"LTD book: {math_books[0].name}")
print(f"LTD solution: {math_books[0].pages[0].solutions[0].image_src}")
6. Error Handling
from gdzapi import Client, NetworkError, ParsingError
client = Client.gdz(timeout=10)
try:
books = client.get_books("/non-existent-subject")
except NetworkError as e:
print(f"Connection or HTTP status error for {e.url}: {e}")
except ParsingError as e:
print(f"Parsing error: {e}")
Architecture
gdzapi/
├── __init__.py # Unified public API and provider factory shortcuts
├── client.py # Single Sync & Async execution engine (Client, AsyncClient)
├── providers.py # Declarative registry defining all 9 providers
├── models.py # Pydantic v2 models (Class, Subject, Book, Page, Solution, AwaitableList)
├── exceptions.py # GDZError, NetworkError, ParsingError, NotFoundError
└── utils.py # URL normalization, DOM parsers, HTTP headers
Adding a New Provider in 20 Lines
Because gdzapi uses a declarative provider engine, you do not need to create new files or rewrite sync/async boilerplate to add an 8th provider. Simply define a ProviderSpec in gdzapi/providers.py:
MY_SPEC = ProviderSpec(
name="myprovider",
base_url="https://example.com",
display_name="My Provider",
extract_subjects=_my_subjects,
extract_books=_my_books,
extract_pages=_my_pages,
extract_solutions=_my_solutions,
)
The unified Client and AsyncClient automatically provide sync, async, caching, and lazy navigation support out of the box.
Testing
The test suite includes 21 isolated offline unit tests and 8 live integration tests across all 7 providers:
# Run unit tests (offline, instantaneous)
pytest -v -m "not live"
# Run all tests including live network requests
pytest -v
License
gdzapi is distributed under the terms of the MIT license.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file gdzapi-1.1.0.tar.gz.
File metadata
- Download URL: gdzapi-1.1.0.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
485d3b14dbe58c65c1fb880526a02d13ae597c5443ac5febe5c078c5258c2115
|
|
| MD5 |
2916097f0bec1e3db5ca8603a4d2f19c
|
|
| BLAKE2b-256 |
9ae7e0d80ddb9021271c241d94250f1342e1c263ce39facefbf8861a2e017d9e
|
File details
Details for the file gdzapi-1.1.0-py3-none-any.whl.
File metadata
- Download URL: gdzapi-1.1.0-py3-none-any.whl
- Upload date:
- Size: 17.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.14.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
125e7f0024dc4d38b6b01906069aa58cada0782ebf1df780348908ddd308462c
|
|
| MD5 |
ca0d59affa69ef1a7b99357c6beb7b14
|
|
| BLAKE2b-256 |
87a562a2350589fc29cf818513c74991f6ec6794afbea75fef5bffa91fc9e48e
|