Skip to main content

netschoolpy

CI PyPI Python

Асинхронный клиент для «Сетевого города». Дневник, оценки, домашние задания, объявления — всё программно, без браузера.

Репозиторий: https://github.com/vkrya/netschoolpy

Поддержать проект (ЮMoney)

Ключевые слова: Сетевой город, SGO, дневник, оценки, домашние задания, Госуслуги, ESIA, QR, API.

Документация (Docs)

Документация к проекту находится в папке docs/. Для локального просмотра:

pip install mkdocs mkdocs-material
mkdocs serve

Установка

pip install netschoolpy

# Для отображения QR-кода в терминале (опционально):
pip install netschoolpy[qr]

Способы входа

По логину/паролю SGO

import asyncio
from netschoolpy import NetSchool

async def main():
    async with NetSchool("https://sgo.example.ru") as ns:
        await ns.login("ИвановИ", "password", "Школа №1")
        diary = await ns.diary()

asyncio.run(main())

Через Госуслуги (логин + пароль ЕСИА)

Программный вход через Госуслуги — без браузера. Поддерживает SMS, TOTP (приложение-аутентификатор) и MAX (Госключ) в качестве второго фактора.

async with NetSchool("https://sgo.example.ru") as ns:
    await ns.login_via_gosuslugi(
        esia_login="+79001234567",     # телефон, email или СНИЛС
        esia_password="your_password",
        school="Школа №1",             # если к аккаунту привязано несколько организаций
    )
    diary = await ns.diary()

При MFA код из SMS/TOTP/MAX будет запрошен через input(). Если esia_login / esia_password не указаны, они тоже запрашиваются через input(). Если к аккаунту привязано несколько организаций и school не указан — будет предложен интерактивный выбор.

Через Госуслуги (QR-код)

Вход без ввода логина и пароля — нужно отсканировать QR-код в мобильном приложении «Госуслуги».

async def show_qr(qr_data: str):
    """qr_data — deep-link gosuslugi://auth/signed_token=..."""
    import qrcode
    qrcode.make(qr_data).save("qr.png")
    print("Отсканируйте QR в приложении Госуслуги!")

async with NetSchool("https://sgo.example.ru") as ns:
    await ns.login_via_gosuslugi_qr(
        qr_callback=show_qr,   # вызовется после генерации QR
        qr_timeout=120,         # секунд ожидания сканирования
        school="Школа №1",     # если привязано несколько организаций
    )
    diary = await ns.diary()
  • qr_callback — async/sync функция, получает deep-link gosuslugi://auth/signed_token=... для кодирования в QR. Если не указан — QR печатается в stdout (pip install qrcode).
  • qr_timeout — таймаут ожидания сканирования (по-умолчанию 120 сек).
  • school — название организации (подстрока). Если привязано несколько — выбирает автоматически. Без параметра — интерактивный выбор.

По токену / куки (продвинутое)

# По accessToken из localStorage SGO:
await ns.login_with_token("eyJ...")

# По session-store из localStorage:
await ns.login_with_session_store('{"accessToken":"eyJ..."}')

# По Cookie-строке из DevTools:
await ns.login_with_cookies("NSSESSIONID=abc123")

Удержание сессии (keep-alive)

После любого входа автоматически запускается фоновая задача, которая каждые 5 минут пингует сервер, не давая сессии истечь.

# Изменить интервал (в секундах):
ns.set_keepalive_interval(120)   # каждые 2 минуты

# Отключить keep-alive:
ns.set_keepalive_interval(0)

При вызове logout() keep-alive останавливается автоматически.

Справочник регионов

Библиотека включает справочник из 26 регионов с готовыми URL.

from netschoolpy import REGIONS, get_url, list_regions

# Все доступные регионы
for name in list_regions():
    print(name, "→", REGIONS[name])

# Быстрый поиск (регистронезависимый, поддерживает подстроки)
url = get_url("Челябинская область")   # "https://sgo.edu-74.ru"
url = get_url("челябинская")           # тоже найдёт

# Использование с клиентом
async with NetSchool(get_url("Краснодарский край")) as ns:
    ...

Примечание: Иркутская, Ростовская и Свердловская области имеют несколько независимых серверов — для них URL нужно указывать вручную.

Экспорт/импорт сессии

Чтобы не авторизоваться каждый раз, можно сохранить и восстановить сессию:

from pathlib import Path

# Сохранить после логина:
session_data = ns.export_session()
Path("session.json").write_text(session_data)

# Восстановить при следующем запуске:
async with NetSchool("https://sgo.example.ru") as ns:
    try:
        await ns.import_session(Path("session.json").read_text())
    except netschoolpy.SessionExpired:
        await ns.login(...)  # сессия истекла — логинимся заново

API

async with NetSchool("https://sgo.example.ru") as ns:
    await ns.login(...)

    diary = await ns.diary()                    # дневник (текущая неделя)
    diary = await ns.diary(start, end)          # дневник за период

    overdue = await ns.overdue()                # просроченные задания
    announcements = await ns.announcements()    # объявления
    attachments = await ns.attachments(...)     # вложения к заданию
    info = await ns.school_info()               # информация о школе

    await ns.download_attachment(id, buffer)    # скачать вложение
    data = await ns.download_attachment_as_bytes(id)
    await ns.download_profile_picture(id, buf)  # аватар пользователя

    kinds = await ns.assignment_types()         # типы заданий (ДЗ, К, …)
    limits = await ns.upload_limits()           # лимиты загрузки файлов, КБ
    settings = await ns.my_settings()           # профиль (личные данные!)

Почта

Одинаково в NetSchool и MobileNetSchool:

from netschoolpy import RecipientGroup

page = await ns.mail_list("Inbox", search="контрольная")   # поиск по теме
page = await ns.mail_list("Inbox", search="Петрова", search_field="author", ascending=True)
msg = await ns.mail_read(message_id)

teachers = await ns.mail_recipients(RecipientGroup.TEACHERS)  # веб: группы адресной книги
mid = await ns.mail_send("Тема", "Текст", [teachers[0].id],
                         attachments=[("дз.pdf", pdf_bytes)])  # вернёт ID письма

draft = await ns.mail_draft(message_id, "reply")   # «Re: …», цитата и адресат
await ns.mail_send(draft.subject, "Ответ" + draft.text, [r.id for r in draft.to])

await ns.mail_mark_read([mid])                     # read=False — непрочитанным
await ns.mail_delete([mid])                        # в «Удалённые»
await ns.mail_restore([mid])                       # обратно
await ns.mail_delete([mid], permanent=True)        # из «Удалённых» — навсегда

RecipientGroup: ADMINISTRATION (по умолчанию), HEAD_TEACHERS, CLASS_TEACHERS, TEACHERS, CLASSMATES. Мобильный mail_recipients() возвращает всю адресную книгу сразу.

Отчёты

Любой отчёт со страницы «Отчёты» — тем же путём, что и сайт (очередь задач и SignalR):

for group in await ns.reports():
    print(group.title, [r.id for r in group.reports])

filters = await ns.report_filters("StudentAverageMark")   # что можно выбрать

report = await ns.report(
    "StudentTotal",
    {"TERMID": "2 четверть"},          # value или название пункта
    start=date(2026, 9, 1), end=date(2026, 9, 15),   # для фильтра-интервала
)
report.html                            # как показывает сайт
for row in report.tables()[1]:         # таблицы: строки → тексты ячеек
    print(row)

Не указанные фильтры берут значение по умолчанию. total_marks() и assigned_marks() — готовые разборы отчётов StudentTotalMarks и StudentGrades.

Мобильное API (9.0)

MobileNetSchool работает через API приложения NetSchool (/api/mobile/*). Интерфейс совместим с NetSchool: diary(), announcements(), mail_list(), mail_read(), mail_recipients(), mail_send(), attachments(), assignment_details(), export_session() / import_session() возвращают те же модели.

Чем оно лучше веб-API:

  • токен живёт 60 дней, keep-alive не нужен;
  • есть время публикации задания (Assignment.published_at), учитель, тема и посещаемость урока (Lesson.teacher, topic, absence);
  • итоги по всем предметам одним запросом, аналитика по предмету, лента событий, портфолио.

Для получения токена нужен client_secret приложения parent-mobile: передайте client_secret= или задайте NETSCHOOL_MOBILE_CLIENT_SECRET. С библиотекой он не поставляется.

from netschoolpy import MobileNetSchool, NetSchool, published_at

# По логину и паролю — как в приложении
async with MobileNetSchool("https://sgo.e-mordovia.ru") as ns:
    await ns.login("ИвановИ", "password", "МОУ Школа №1")
    diary = await ns.diary()
    for day in diary.schedule:
        for lesson in day.lessons:
            for a in lesson.assignments:
                print(lesson.subject, a.content, a.published_at)

# Из уже вошедшего веб-клиента (в том числе через Госуслуги) — без пароля
mobile = await MobileNetSchool.from_web(web_ns)

# Сохранить и восстановить
saved = mobile.export_session()
await MobileNetSchool(url).import_session(saved)

Сырые маршруты возвращают JSON сервера:

await ns.users()                                  # профиль, школы, дети
await ns.education()                              # годы обучения
await ns.terms(); await ns.subjects_raw()         # четверти, предметы
await ns.classmeetings(start, end)                # уроки
await ns.classmeeting(classmeeting_id)
await ns.assignments([id1, id2]); await ns.assignment(id)
await ns.current_term_assignments()               # «Д/З» текущей четверти
await ns.assignment_answer(id)                    # ответ ученика
await ns.attachments_raw(assignment_id)
await ns.period_events(period_days=14)            # новые оценки и задания
await ns.period_events(types=["TermTotalInfo"])   # только итоговые за период
await ns.totals()                                 # итоговые оценки
await ns.subject_performance(subject_id, term_id) # «Итоги по предмету»
await ns.mail_messages("INBOX"); await ns.mail_unread_count()
await ns.mail_read_raw(id)                        # помечает прочитанным
await ns.address_book(); await ns.recipient(user_id)
await ns.mail_blocked_users()
await ns.mail_block(user_id); await ns.mail_unblock(user_id)  # эффект не подтверждён
await ns.announcements_raw(); await ns.push_settings()
await ns.portfolios(); await ns.portfolio(id); await ns.download_portfolio_doc(id, doc_id)
await ns.token_endpoints()

Факультативы определяются по признакам сервера extraActivity (внеурочная деятельность) и addEducation (дополнительное образование), без анализа названия:

from netschoolpy import is_elective

is_elective(await ns.classmeeting(classmeeting_id))  # сырой ответ API
for day in (await ns.diary()).schedule:
    for lesson in day.lessons:
        print(lesson.subject, lesson.is_elective)

Каталог регионов приложения — без авторизации: await netschoolpy.list_mobile_regions(), await netschoolpy.get_mobile_region(server_id). Последняя версия приложения в каталоге — await netschoolpy.published_app_version(). Токен можно продлить через await ns.refresh().

Исключения

import netschoolpy

netschoolpy.NetSchoolError      # базовое
netschoolpy.LoginError          # ошибка авторизации
netschoolpy.MFAError            # ошибка двухфакторной (SMS/TOTP/PUSH)
netschoolpy.ESIAError           # ошибка на стороне Госуслуг
netschoolpy.SchoolNotFound      # школа не найдена
netschoolpy.SessionExpired      # сессия истекла (401)
netschoolpy.ServerUnavailable   # сервер не ответил

Логирование

Библиотека использует стандартный logging. Для отладки:

import logging
logging.basicConfig(level=logging.DEBUG)

Благодарности

«Сетевой город» не публикует документацию к своему API, и многое о нём сначала узнали другие. Код netschoolpy написан с нуля, но что и где искать — подсказали эти проекты:

  • nm17/netschoolapi — Даниил Николаев, Иван Горланов, Елисей Власенко, и форк MrConsoleka/netschoolapi: первая Python-библиотека для «Сетевого города» — вход, дневник, вложения;
  • lentryd/NetSchoolApi (JavaScript): отчёты через очередь задач и SignalR;
  • TheEntropyShard/NetSchoolAPI (Java): действия с письмами веб-API, группы адресной книги, список отчётов, типы заданий, лимиты загрузки;
  • chstudios-ru/mobnslib: почта мобильного API — удаление, заготовки ответа, получатель по id, блокировка — и типы событий ленты.

Каждый маршрут перед добавлением проверен на живом сервере; там, где проверить не удалось, это сказано в документации метода.

Лицензия

© 2026 vkrya. GNU GPLv3. Подробнее — LICENSE.

Metadata

Release files for netschoolpy 9.4.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for netschoolpy 9.4.1
File Size Uploaded
netschoolpy-9.4.1.tar.gz 102.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for netschoolpy 9.4.1
File Interpreter ABI Platform
netschoolpy-9.4.1-py3-none-any.whl Python 3 none any Details

Total release size: 186.2 kB

Release files / netschoolpy-9.4.1.tar.gz

Download URL netschoolpy-9.4.1.tar.gz
Size 102.9 kB
Tags Source
SHA-256 checksum
How to use checksums
bd3ba8dc7b00c1d7bc18af76fe3312160937d2d57a352da39a39f060a5993974
BLAKE2b-256 checksum
How to use checksums
500109e09c5dc1cf9856a164c33a875d81e4a9895ace05759ef6c6568e77c96f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / netschoolpy-9.4.1-py3-none-any.whl

Download URL netschoolpy-9.4.1-py3-none-any.whl
Size 83.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
22bd78d9cb7cddddae072b933b607c5030ce93796833caa0100cfeb28ee4bcf1
BLAKE2b-256 checksum
How to use checksums
f419fae5ac4c6a35110de63383269d4079ddfc95a9dd1fe3bccbf51408d0eac3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

9.5.0

2 release files

9.4.3

2 release files

9.4.2

2 release files

This release

9.4.1 This release

2 release files

9.4.0

2 release files

8.1.0

2 release files

8.0.2

2 release files

8.0.1

2 release files

7.1.3

2 release files

7.1.2

2 release files

7.1.1

2 release files

7.1.0

2 release files

7.0.0

2 release files

6.0.0

2 release files

4.0.0

2 release files

3.4.0

2 release files

3.3.3

2 release files

3.3.2

2 release files

3.3.1

2 release files

3.3.0

2 release files

3.2.1

2 release files

3.2.0

2 release files

3.1.0

2 release files

3.0.4

2 release files

2.0.0

2 release files

1.1.0

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.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