Skip to content

AlexAgents/bibliotekus-downloader

Repository files navigation

📚 Bibliotekus Downloader

Полнофункциональный загрузчик книг с bibliotekus.artlebedev.ru в PDF с автоматической оптимизацией качества и размера.

Release License Python Requests

Книг Страниц Качество


📋 Оглавление


Описание

Bibliotekus Downloader — консольная утилита для автоматического скачивания книг с библиотеки Студии Артемия Лебедева в формате PDF. Проект состоит из основного загрузчика и набора вспомогательных инструментов:

  1. Main — главный загрузчик с интерактивным меню, настройкой качества и graceful shutdown.
  2. Verify — проверка скачанной библиотеки, переименование файлов, список для перекачки.
  3. Builder — сборка автономного EXE через PyInstaller с генерацией иконки.
  4. Tests — 50+ юнит и интеграционных тестов всех компонентов.

Возможности

Main Downloader

  • 🔍 Автоопределение разрешения — находит максимальное доступное качество (до 2400px) для каждой книги
  • 📉 Умная оптимизация — динамический подбор JPEG-качества и resize под целевой размер PDF
  • ⚙️ Настройка качества — 4 готовых пресета (30-200 МБ) + полностью ручные параметры
  • Многопоточность — до 12 потоков на загрузку страниц одной книги
  • 📝 Гибкий выбор — номера, диапазоны, комбинации (1,5-10,20-)
  • ⏭️ Пропуск скачанных — идентификация по уникальному slug в имени файла
  • 🛡️ Graceful shutdown — Ctrl+C корректно завершает текущую книгу и возвращает в меню
  • 🔄 Цикл работы — после скачивания выбор: вернуться в меню или выйти
  • 📊 Детализированная статистика — время, успех/ошибки, размер файлов

Verify Tool

  • 🔎 Сканирование — анализ папки books_pdf/ и сопоставление с сайтом
  • 🔄 Переименование — старые форматы → Название [slug].pdf
  • Проверка страниц — сравнение количества страниц в PDF с сайтом (через PyMuPDF, опционально)
  • 📋 Список перекачки — готовая строка для ввода в main downloader

Builder Tool

  • 🏗️ Сборка в EXE — автономный исполняемый файл (~45 МБ)
  • 🎨 Генерация иконок — автоматическое создание .ico через Pillow
  • 🧹 Очистка — удаление build/, dist/, кеша

Структура проекта

📂 Нажмите, чтобы развернуть дерево файлов
bibliotekus-downloader/
├── __main__.py              # Точка входа — скачивание книг
├── config.py                # Единая конфигурация
├── utils.py                 # Общие утилиты
├── logger.py                # Логирование (консоль + файл)
├── network.py               # HTTP-сессия с retry/backoff
├── scraper.py               # Парсинг сайта: книги, страницы, разрешения
├── downloader.py            # Оркестрация загрузки
├── image_optimizer.py       # Оптимизация изображений
├── pdf_builder.py           # Сборка PDF (fpdf2 + reportlab)
├── cli.py                   # Интерфейс: меню, выбор, настройки
├── requirements.txt         # Зависимости Python
├── .gitignore
├── README.md
├── LICENSE
├── scripts/
│   ├── __init__.py          # Описание скриптов
│   ├── tests.py             # Тесты проекта
│   ├── verify.py            # Проверка библиотеки
│   ├── builder.py           # Сборка EXE
│   └── checksum.py          # Генерация SHA-256
├── assets/
│   └── icon.ico             # Иконка для EXE
└── data/                    # Создаётся автоматически
    ├── books_pdf/           # Скачанные книги
    └── bibliotekus.log      # Лог-файл

Установка

Требования

  • Python 3.10+
  • pip

Установка зависимостей

pip install -r requirements.txt
📄 Содержимое requirements.txt
# ─── Основные ──────────────────────────────
requests>=2.28.0
beautifulsoup4>=4.11.0
Pillow>=9.0.0
fpdf2>=2.7.0

# ─── Фоллбэк PDF (опционально) ─────────────
reportlab>=3.6.0

# ─── Проверка библиотеки (опционально) ─────
# PyMuPDF>=1.23.0

# ─── Сборка EXE (опционально) ──────────────
# pyinstaller>=6.0.0

Быстрый старт

1. Запустить загрузчик

python __main__.py

Откроется интерактивное меню:

══════════════════════════════════════════════════════════════
 📚 BIBLIOTEKUS DOWNLOADER
══════════════════════════════════════════════════════════════
 📋 Список книг (95 доступно)
 ✅ Скачано: 10  ⬚ Осталось: 85
──────────────────────────────────────────────────────────────
    1. ⬜         Информационная архитектура в интернете
    2. ✅   47.8M  Искусство шрифта
    3. ⬜         Модульные сетки
   ...
──────────────────────────────────────────────────────────────
 ⚙ PDF ~80 МБ | JPEG 78-92% | Мин. 1200px
──────────────────────────────────────────────────────────────
 📝 Команды:
    y — все книги | new — нескачанные | n — отмена
    5 | 1,3,5 | 5-20 | 5- | 1-10,15,20-
    settings — настройка качества
──────────────────────────────────────────────────────────────
 Что скачать?

2. Выбрать книги

Ввод Действие
y / Enter Все книги
new Только нескачанные
5 Книга №5
1,3,5 Перечисление
5-20 Диапазон
5- С 5-й до конца
1-10,15,20- Комбинация
settings Меню настройки качества
n Отмена

3. Настройка качества (опционально)

Что скачать? settings
════════════════════════════════════════════════════════════
  ⚙  НАСТРОЙКА КАЧЕСТВА
════════════════════════════════════════════════════════════
  Текущие: PDF ~80 МБ | JPEG 78-92% | Мин. 1200px
────────────────────────────────────────────────────────────
  Пресет     Название       PDF МБ   JPEG %   Мин.px
────────────────────────────────────────────────────────────
  low        Экономный         30   65-78      800
  medium     Стандарт          80   78-92     1200   ◄
  high       Высокое          200   88-96     1600
  max        Максимум        9999   95-98     2400
────────────────────────────────────────────────────────────
  custom     Ввести свои значения
  back       Назад

4. После скачивания

══════════════════════════════════════════════════
🏁 ГОТОВО за 12.3 мин!
   ✅ 5 | ❌ 0
   📁 5 файлов, 0.39 ГБ
══════════════════════════════════════════════════

──────────────────────────────────────────────────
  ⏎ Enter — в меню | q — выйти:

Компоненты

Main (__main__.py)

Точка входа приложения. Главный цикл работы с graceful shutdown.

Ключевые функции

Функция Описание
main() Главный цикл: меню → выбор → скачивание → возврат в меню
_handle_sigint() Обработка Ctrl+C → флаг _shutdown_requested

Поток выполнения

┌──────────┐    ┌───────────┐    ┌──────────────┐    ┌──────────┐
│  Меню    │───▶│  Выбор    │───▶│  Скачивание │───▶│  Итоги   │
│  книг    │    │  книг     │    │  (downloader)│    │  (CLI)   │
└──────────┘    └───────────┘    └──────────────┘    └─────┬────┘
     ▲                                                      │
     └──────────────────────────────────────────────────────┘
                     ⏎ Enter — в меню | q — выйти

CLI (cli.py)

Интерфейс командной строки: меню книг, выбор, настройка качества, итоги.

Ключевые функции

Функция Описание
display_book_list() Отображение списка книг с бейджами (✅/⬜) и размерами
prompt_selection() Запрос выбора книг с валидацией
quality_settings_menu() Интерактивное меню настройки качества
print_summary() Итоговая статистика после скачивания

Формат выбора

# Парсинг через utils.parse_ranges()
"1,5-10,20-"  →  [1, 5, 6, 7, 8, 9, 10, 20, 21, ..., 95]
"new"         →  [индексы нескачанных книг]

Scraper (scraper.py)

Парсинг HTML: получение списка книг, страниц, автоопределение разрешения.

Ключевые функции

Функция Описание
get_book_links() Парсит главную страницу → список {"title", "url", "slug"}
get_page_count_and_images() Парсит страницу книги → количество страниц + URLs изображений
detect_best_resolution() Пробует разрешения 2400→1600→1200→800px HEAD-запросами

Умное определение разрешения

# Пробует в порядке убывания качества
resolutions = [2400, 1600, 1200, 800]
for res in resolutions:
    test_url = page_urls[0].replace("/1200/", f"/{res}/")
    if safe_head(test_url).status_code == 200:
        return res
return 800  # fallback

Downloader (downloader.py)

Оркестрация загрузки: многопоточное скачивание → оптимизация → сборка PDF.

Ключевые функции

Функция Описание
download_book() Загрузка одной книги: страницы → оптимизация → PDF
_process_book() Внутренняя функция обработки книги
_download_single_page() Потокобезопасная загрузка одной страницы

Поток выполнения

1. Определить разрешение (scraper.detect_best_resolution)
2. Получить URLs страниц (scraper.get_page_count_and_images)
3. Скачать страницы параллельно (ThreadPoolExecutor, 12 потоков)
4. Оптимизировать изображения (image_optimizer.smart_optimize_image)
5. Собрать PDF (pdf_builder.build_pdf_from_files)
6. Сохранить в data/books_pdf/

Многопоточность

    with concurrent.futures.ThreadPoolExecutor(max_workers=MAX_WORKERS_PAGES) as ex:
        futures = {ex.submit(_download_single_page, t): t[0] for t in tasks}
        for future in concurrent.futures.as_completed(futures):
            if _shutdown_requested:
                ex.shutdown(wait=False, cancel_futures=True)
                break

Image Optimizer (image_optimizer.py)

Динамическая оптимизация JPEG под целевой размер PDF.

Ключевые функции

Функция Описание
smart_optimize_image() Подбирает качество и resize для достижения target_size_per_page

Алгоритм подбора

1. Загрузить изображение (Pillow)
2. Если ширина > min_resolutionресайз с сохранением пропорций
3. Определить уровень сжатия на основе compression_needed:
   - < 30%quality_max
   - 30-50% → (quality_min + quality_max) / 2
   - > 50%quality_min
4. Сохранить оптимизированное изображение

Пример

# Пресет medium: PDF ~80 МБ, книга ~200 страниц
target_per_page = 80 * 1024 * 1024 / 200  # ~400 КБ/страница

# Исходное: 3000x4000, 2.5 МБ
# После оптимизации: 1200x1600, ~350 КБ, JPEG 84%

PDF Builder (pdf_builder.py)

Сборка PDF из оптимизированных изображений.

Ключевые функции

Функция Описание
build_pdf_from_files() Главная функция сборки (fpdf2 → reportlab фоллбэк)
_build_fpdf() Сборка через fpdf2 (приоритет)
_build_reportlab() Фоллбэк через reportlab

Формат PDF

# Каждая страница = одно изображение на всю страницу
for image_path in sorted(image_paths):
    pdf.add_page()
    pdf.image(str(image_path), x=0, y=0, w=page_width, h=page_height)

Network (network.py)

HTTP-сессия с автоматическими retry через urllib3.

Ключевые функции

Функция Описание
create_session() Создаёт requests.Session с настроенным retry
safe_get() GET-запрос с автоматическим retry
safe_head() HEAD-запрос с автоматическим retry

Retry-логика

# Автоматический retry через urllib3.util.retry.Retry
    retry = Retry(
        total=MAX_RETRIES,
        backoff_factor=RETRY_BACKOFF,
        status_forcelist=[429, 500, 502, 503, 504],
        allowed_methods=["GET", "HEAD"],
        raise_on_status=False,
    )

    adapter = HTTPAdapter(
        pool_connections=POOL_CONNECTIONS,
        pool_maxsize=POOL_MAXSIZE,
        max_retries=retry,
    )
    s.mount("https://", adapter)
    s.mount("http://", adapter)

Config (config.py)

Единая конфигурация проекта.

Основные параметры

# Пути
PROJECT_ROOT    # Корень проекта (с поддержкой PyInstaller)
DATA_DIR        # data/
OUTPUT_DIR      # data/books_pdf/

# Сеть
BASE_URL = "https://bibliotekus.artlebedev.ru"
MAX_RETRIES = 3
RETRY_BACKOFF = 0.5  # экспоненциальный backoff
MAX_WORKERS_PAGES = 12

# Пресеты качества
QUALITY_PRESETS = {
    "low":    {"target_mb": 30,   "quality": (65, 78), "min_res": 800},
    "medium": {"target_mb": 80,   "quality": (78, 92), "min_res": 1200},
    "high":   {"target_mb": 200,  "quality": (88, 96), "min_res": 1600},
    "max":    {"target_mb": 9999, "quality": (95, 98), "min_res": 2400}
}

PyInstaller-совместимость

# Определение корня проекта
    if getattr(sys, "frozen", False):
        # Запущены как EXE — берём папку где лежит .exe
        return os.path.dirname(sys.executable)
    else:
        # Обычный запуск — папка где лежит config.py
        return os.path.dirname(os.path.abspath(__file__))

Utils (utils.py)

Общие утилиты проекта.

Функция Описание
sanitize_filename(text) Безопасное имя файла (убирает спецсимволы, обрезает до 180 символов)
make_ranges() Парсит выбор книг (1,5-10,20-) → список индексов
prompt_continue() Запрос "⏎ Enter — в меню | q — выйти"
get_pdf_filename() Генерирует имя PDF: Название [slug].pdf

Logger (logger.py)

Настройка логирования: консоль + файл data/bibliotekus.log.

# Формат
[2026-03-03 18:00:00] INFO: Скачивание «Искусство шрифта» (47.8 МБ)
[2026-03-03 18:05:12] ERROR: Не удалось загрузить страницу 5

# Логирование в файл
handler = FileHandler("data/bibliotekus.log", encoding="utf-8")

Настройка качества

Пресеты

Пресет PDF МБ JPEG % Мин. px Назначение
low ~30 65-78 800 Экономия места, чтение на экране
medium ~80 78-92 1200 По умолчанию. Баланс качества и размера
high ~200 88-96 1600 Качественная коллекция
max 95-98 2400 Архивное качество, оригинальное разрешение

Custom настройки

  📝 Введите значения (Enter — оставить текущее):

  Целевой размер PDF, МБ [80]: 120
  Мин. JPEG качество, % [78]: 85
  Макс. JPEG качество, % [92]: 95
  Мин. разрешение, px [1200]: 1600

  ✅ Применены настройки:
     PDF ~120 МБ | JPEG 85-95% | Мин. 1600px

Инструменты

Verify (scripts/verify.py)

Проверка скачанной библиотеки.

python -m scripts.verify

Что делает

  1. Сканирует data/books_pdf/ и показывает все файлы
  2. Сопоставляет с книгами на сайте по slug и названию
  3. Переименовывает файлы старого формата → Название [slug].pdf
  4. Проверяет количество страниц в PDF vs на сайте (требует PyMuPDF, опционально)
  5. Выдаёт готовую строку для перекачки повреждённых/неполных
══════════════════════════════════════════════════════════════
📊 ИТОГИ
══════════════════════════════════════════════════════════════

  ✅ ОК: 89
  ❌ Не скачаны: 4
  🔻 Неполные: 2 (меньше страниц, чем на сайте)
  💀 Повреждены: 0

🔄 ПЕРЕКАЧАТЬ: 6 книг

📋 Для bibliotekus.exe введите:

   3,15,42-44,78

──────────────────────────────────────────────────
  ⏎ Enter — выйти | любая клавиша — продолжить:

Builder (scripts/builder.py)

Сборка автономного EXE через PyInstaller.

python scripts/builder.py

Меню

════════════════════════════════════════════════════════════
  🔨 BUILDER — Сборка bibliotekus.exe
════════════════════════════════════════════════════════════
  Точка входа: __main__.py
  Иконка: ✅ assets/icon.ico (45.2 КБ)
────────────────────────────────────────────────────────────
  ✅ 45.2 MB (2026-03-03 18:00)     1. Собрать bibliotekus.exe
────────────────────────────────────────────────────────────
                                     2. Перегенерировать иконку
                                     3. Очистить build/
                                     4. Очистить dist/
                                     5. Очистить всё (build + dist)
                                     q. Выход

Автогенерация иконки

# Если assets/icon.ico отсутствует — генерирует через Pillow
img = Image.new('RGBA', (256, 256), color='#3776AB')
draw = ImageDraw.Draw(img)
draw.text((64, 96), '📚', font=..., fill='white')
images.save('assets/icon.ico', format='ICO')

Tests (scripts/tests.py)

50+ юнит и интеграционных тестов.

python -m scripts.tests        # все тесты
python -m scripts.tests -v     # подробный вывод

Покрытие

Группа Тесты
Утилиты sanitize_filename, parse_ranges, get_pdf_filename
Конфиг Пути, пресеты, PyInstaller-режим
Оптимизатор Подбор качества, ресайз, валидация
PDF Builder Сборка через fpdf2/reportlab
CLI Парсинг выбора, меню качества
Scraper Моки для get_book_links, detect_best_resolution
Интеграция Полный цикл: скрапинг → оптимизация → PDF
Shutdown Graceful завершение, флаг _shutdown_requested

Формат данных

Структура папки books_pdf

data/books_pdf/
├── Искусство шрифта [art-of-type].pdf           # 47.8 МБ, 234 стр
├── Модульные сетки [grid-systems].pdf            # 52.1 МБ, 196 стр
└── ...

Формат имени файла

<Название книги> [<slug>].pdf

# Примеры:
Искусство шрифта [art-of-type].pdf
Grid Systems in Graphic Design [grid-systems].pdf

Сборка EXE

# Установить зависимости
pip install pyinstaller pillow

# Запустить builder
python scripts/builder.py

# Выбрать: 1 — Собрать bibliotekus.exe

# Результат:
# dist/bibliotekus.exe  (~45 МБ)

Использование EXE

# Положить data/ рядом с exe (или создастся автоматически)
bibliotekus.exe

# Книги сохраняются в:
# <папка с exe>/data/books_pdf/

Безопасность EXE

EXE-файл собран через PyInstaller из открытого исходного кода данного репозитория. PyInstaller упаковывает интерпретатор Python и зависимости в один файл, что часто вызывает ложные срабатывания антивирусов (false positive).

Проверка на VirusTotal

Каждый релиз проверен на VirusTotal (70+ антивирусных движков):

Файл Результат Отчёт
bibliotekus.exe 6/70 — AI срабатывания 🔗 Открыть отчёт

💡 Как проверить самостоятельно: загрузите EXE на virustotal.com → увидите результат за 1-2 минуты.

Контрольные суммы (SHA-256)

<будут сгенерированы при релизе через scripts/checksum.py>

Почему антивирус может сработать?

PyInstaller-сборки иногда детектируются эвристикой как «подозрительные», потому что:

  • EXE распаковывает Python-рантайм во временную папку при запуске
  • Такой же механизм используют некоторые вредоносные программы
  • Это не означает, что файл заражён

Решения:

  • Проверьте отчёт VirusTotal по ссылке выше
  • Сравните SHA-256 хеш скачанного файла с указанным
  • Соберите EXE самостоятельно: python scripts/builder.py
  • Добавьте файл в исключения антивируса

Windows SmartScreen

При первом запуске Windows может показать предупреждение:

"Windows защитила ваш компьютер"
→ Подробнее → Выполнить в любом случае

Это происходит потому, что EXE не подписан цифровой подписью (code signing certificate стоит $200+/год). Предупреждение исчезнет после нескольких запусков.


FAQ

Где хранятся данные?

В папке data/books_pdf/рядом с exe-файлом (или рядом с проектом при запуске из Python).

Можно ли прервать скачивание?

Да, Ctrl+C. Текущая книга завершится корректно, и вы вернётесь в меню. Скачанные книги остаются, при следующем запуске будут пропущены.

Как перекачать книгу?

  1. Удалите файл из data/books_pdf/
  2. Запустите downloader и выберите номер книги

Или используйте scripts/verify.py для списка повреждённых.

Окно EXE закрывается сразу!

Не должно — после каждого действия есть выбор:

  ⏎ Enter — в меню | q — выйти

Если всё же закрывается — запустите из cmd/PowerShell чтобы увидеть ошибку.

Скрипт скачал книгу заново, хотя она уже была!

Скрипт определяет скачанные книги по формату Название [slug].pdf. Если файл называется иначе — он не распознаётся. Запустите python -m scripts.verify для переименования.

PDF слишком большой / маленький

Введите settings в меню и выберите пресет или задайте свои параметры. low — маленькие файлы (~30 МБ), max — максимальное качество (∞).

Ошибка «fpdf2 not found»

pip install fpdf2

Если fpdf2 недоступен, скрипт автоматически использует reportlab.

Антивирус ругается на EXE

Это известная особенность PyInstaller — ложное срабатывание. Добавьте файл в исключения. Проверить можно на VirusTotal.

Как собрать EXE?

pip install pyinstaller pillow
python scripts/builder.py

⚠️ Отказ от ответственности

Лицензия

Проект распространяется под лицензией MIT — используйте как угодно, но на свой страх и риск.

Гарантии

Программное обеспечение предоставляется «КАК ЕСТЬ» (AS IS), без каких-либо явных или подразумеваемых гарантий, включая, но не ограничиваясь, гарантиями товарной пригодности и пригодности для конкретной цели.

Ответственность

Автор ни при каких обстоятельствах не несёт ответственности за:

  • прямые, косвенные, случайные, штрафные или побочные убытки
  • потерю данных, прибыли или деловой репутации
  • блокировку аккаунтов, IP-адресов, доменов
  • нарушение условий использования сторонних сервисов
  • любые претензии третьих лиц

Авторские права на данные

  • Все скачиваемые материалы являются собственностью их правообладателей
  • Сайт bibliotekus.artlebedev.ru — владелец контента
  • Пользователь обязан самостоятельно убедиться в законности скачивания и использования материалов в своей юрисдикции

Назначение

Проект создан исключительно в образовательных и исследовательских целях для демонстрации методов веб-скрапинга на Python. Автор не поощряет нарушение авторских прав или условий использования сайтов.

About

📚 Скачиваем PDF книги с сайта bibliotekus.artlebedev.ru

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages