316 lines
13 KiB
Markdown
316 lines
13 KiB
Markdown
# Yandex Disk Protected Folder Downloader
|
|
|
|
Скрипт для рекурсивного скачивания большой публичной папки Яндекс Диска, защищённой паролем.
|
|
|
|
Он получает список объектов через веб-API Яндекс Диска, сохраняет структуру каталогов и скачивает каждый файл отдельно через `wget`.
|
|
|
|
## Возможности
|
|
|
|
- чтение всех пользовательских настроек из файла `.env` рядом со скриптом;
|
|
- использование готового cookie `passToken` либо автоматическое получение токена по паролю через Chromium и Playwright;
|
|
- постоянный профиль Chromium с повторным использованием cookies;
|
|
- рекурсивный обход вложенных каталогов;
|
|
- двухэтапная работа: сначала полный список, затем скачивание;
|
|
- сохранение полного списка в `yandex-download-manifest.jsonl`;
|
|
- исключение служебного элемента текущего каталога из расчёта `offset`;
|
|
- сохранение исходной структуры каталогов;
|
|
- продолжение прерванной загрузки через `wget --continue`;
|
|
- автоматический пропуск файлов, размер которых уже совпадает с серверным;
|
|
- проверка итогового размера каждого скачанного файла;
|
|
- паузы между запросами прямых ссылок и периодические длительные паузы;
|
|
- повторные HTTP-запросы при временных ошибках `429`, `500`, `502`, `503` и `504`;
|
|
|
|
## Как работает двухэтапная загрузка
|
|
|
|
### Этап 1 — получение полного списка
|
|
|
|
Скрипт быстро обходит страницы каталога:
|
|
|
|
```text
|
|
0 → 40 → 80 → 120 → ... → completed=True
|
|
```
|
|
|
|
На этом этапе:
|
|
|
|
- `download-url` не запрашивается;
|
|
- `wget` не запускается;
|
|
- файлы не скачиваются;
|
|
- для каждого файла сохраняются серверные метаданные и относительный каталог;
|
|
- вложенные каталоги также обходятся полностью;
|
|
- служебное описание текущей папки не учитывается при увеличении `offset`.
|
|
|
|
После успешного завершения обхода создаётся:
|
|
|
|
```text
|
|
OUTPUT_DIR/yandex-download-manifest.jsonl
|
|
```
|
|
|
|
Manifest записывается атомарно: сначала создаётся временный файл `.tmp`, после чего он заменяет предыдущий manifest.
|
|
|
|
Пример одной строки:
|
|
|
|
```json
|
|
{"relative_directory":".","item":{"name":"FL_9J1915234BP_1654_40013103010803_V001_S.frf","type":"file","size":793122,"path":"public_hash:/ODIS/Flashdaten/Brand-A/FL_9J1915234BP_1654_40013103010803_V001_S.frf"}}
|
|
```
|
|
|
|
### Этап 2 — скачивание файлов
|
|
|
|
После получения `completed=True` скрипт начинает скачивание по полностью собранному плану:
|
|
|
|
1. проверяет наличие локального файла;
|
|
2. сравнивает его размер с серверным;
|
|
3. пропускает полностью скачанный файл;
|
|
4. для отсутствующего или неполного файла получает временный `download-url`;
|
|
5. запускает `wget --continue`;
|
|
6. проверяет размер после завершения.
|
|
|
|
Текущая версия сохраняет manifest для контроля и последующего анализа, но при новом запуске всё равно заново выполняет этап 1 и получает актуальный список с Яндекса.
|
|
|
|
## Требования
|
|
|
|
Поддерживается Linux с Python 3.
|
|
|
|
Необходимые пакеты:
|
|
|
|
```bash
|
|
sudo apt install -y python3 python3-requests wget
|
|
```
|
|
|
|
Playwright и Chromium для автоматического получения `passToken`:
|
|
|
|
```bash
|
|
python3 -m pip install --user playwright
|
|
python3 -m playwright install chromium
|
|
```
|
|
|
|
## Настройка
|
|
|
|
Все основные параметры находятся в файле sample.env
|
|
|
|
### `TARGET_URL`
|
|
|
|
Полная публичная ссылка на нужный каталог:
|
|
|
|
```dotenv
|
|
TARGET_URL="https://disk.yandex.ru/d/cWn4RgSdFcSZXw/ODIS/Flashdaten/Brand-A"
|
|
```
|
|
|
|
Для вложенного каталога необходимо указывать полный путь после идентификатора публичной ссылки.
|
|
|
|
### `PUBLIC_LINK_PASSWORD`
|
|
|
|
Пароль публичной ссылки:
|
|
|
|
```dotenv
|
|
PUBLIC_LINK_PASSWORD="реальный пароль публичной ссылки"
|
|
```
|
|
|
|
Если оставить значение пустым, программа попытается использовать cookies из постоянного профиля Chromium. Если действующего `passToken` нет и запуск производится из терминала, пароль будет запрошен без отображения введённых символов.
|
|
|
|
### OUTPUT_DIR
|
|
|
|
Каталог, куда будут сохранены файлы:
|
|
|
|
```python
|
|
OUTPUT_DIR = "/mnt/hdd1-4TB/ODIS/Flashdaten/Brand-V"
|
|
```
|
|
|
|
Каталог будет создан автоматически, если у пользователя есть права на его создание.
|
|
Внутри него также могут появиться:
|
|
|
|
```text
|
|
yandex-download-manifest.jsonl
|
|
yandex-captcha-url.txt
|
|
```
|
|
|
|
### Настройки Playwright
|
|
|
|
```dotenv
|
|
PLAYWRIGHT_PROFILE_DIR="./playwright-profile"
|
|
PLAYWRIGHT_HEADLESS="false"
|
|
PLAYWRIGHT_TIMEOUT="120"
|
|
```
|
|
|
|
- `PLAYWRIGHT_PROFILE_DIR` — постоянный профиль Chromium;
|
|
- `PLAYWRIGHT_HEADLESS=false` — показывает окно браузера и позволяет вручную пройти CAPTCHA;
|
|
- `PLAYWRIGHT_HEADLESS=true` — запускает Chromium без окна; при CAPTCHA программа остановится;
|
|
- `PLAYWRIGHT_TIMEOUT` — максимальное время ожидания получения `passToken`, в секундах.
|
|
|
|
Относительный путь профиля вычисляется относительно каталога, где находится `.env` и скрипт.
|
|
|
|
### Задержки между страницами
|
|
|
|
```dotenv
|
|
PAGE_DELAY_MIN="0.5"
|
|
PAGE_DELAY_MAX="1.5"
|
|
```
|
|
|
|
Эти задержки используются только на этапе 1 между запросами `fetch-list`.
|
|
|
|
После перехода на двухэтапную схему страницы перечисляются подряд и больше не разделяются длительным скачиванием файлов. Поэтому состояние пагинации живёт значительно меньше времени.
|
|
|
|
### Задержки между файлами
|
|
|
|
```dotenv
|
|
FILE_DELAY_MIN="1.5"
|
|
FILE_DELAY_MAX="3.5"
|
|
```
|
|
|
|
Случайная пауза перед каждым запросом `download-url` на этапе 2.
|
|
|
|
### Длительные паузы
|
|
|
|
```dotenv
|
|
COOLDOWN_EVERY_FILES="100"
|
|
COOLDOWN_MIN="60"
|
|
COOLDOWN_MAX="120"
|
|
```
|
|
|
|
После каждых 100 запросов `download-url` программа дополнительно ждёт от 60 до 120 секунд.
|
|
|
|
Чтобы отключить длительные паузы:
|
|
|
|
```dotenv
|
|
COOLDOWN_EVERY_FILES="0"
|
|
COOLDOWN_MIN="0"
|
|
COOLDOWN_MAX="0"
|
|
```
|
|
|
|
### HTTP-таймауты и повторы
|
|
|
|
```dotenv
|
|
CONNECT_TIMEOUT="30"
|
|
READ_TIMEOUT="120"
|
|
HTTP_RETRIES="5"
|
|
```
|
|
|
|
- `CONNECT_TIMEOUT` — таймаут установки соединения;
|
|
- `READ_TIMEOUT` — таймаут чтения ответа API;
|
|
- `HTTP_RETRIES` — число автоматических повторов временных ошибок.
|
|
|
|
## Пример вывода
|
|
|
|
### Получение токена
|
|
|
|
```text
|
|
[AUTH] Открываю защищённую ссылку в Chromium...
|
|
[AUTH] Используется passToken из сохранённого профиля.
|
|
```
|
|
|
|
Либо:
|
|
|
|
```text
|
|
[AUTH] Поле пароля найдено (input[type="password"]).
|
|
[AUTH] Ввожу пароль публичной ссылки...
|
|
[AUTH] Пароль введён; отправляю форму...
|
|
[AUTH] passToken получен.
|
|
```
|
|
|
|
### Этап 1
|
|
|
|
```text
|
|
[INFO] Открываю исходную ссылку...
|
|
[INFO] Папка открыта.
|
|
[INFO] Корневой hash: public_hash:/ODIS/Flashdaten/Brand-A
|
|
[INFO] Этап 1/2: получаю полный список файлов...
|
|
|
|
[DIR ] /mnt/hdd1-4TB/ODIS/Flashdaten/Brand-A
|
|
[PAGE] получено API=41, дочерних=40, служебных=1, offset=0->40, completed=False
|
|
[WAIT] 0.8 с перед следующей страницей
|
|
[PAGE] получено API=10, дочерних=9, служебных=1, offset=12080->12089, completed=True
|
|
[INFO] Полный список получен: 12089 файлов.
|
|
[INFO] Manifest сохранён: /mnt/hdd1-4TB/ODIS/Flashdaten/Brand-A/yandex-download-manifest.jsonl
|
|
```
|
|
|
|
### Этап 2
|
|
|
|
```text
|
|
[INFO] Этап 2/2: начинаю скачивание по сохранённому списку...
|
|
[SKIP] /mnt/hdd1-4TB/ODIS/Flashdaten/Brand-A/FL_0DL300014F_3123_idMA_sw.frf (2.14 MiB)
|
|
[GET ] /mnt/hdd1-4TB/ODIS/Flashdaten/Brand-A/FL_9J1915234BP_1654_40013103010803_V001_S.frf (774.53 KiB)
|
|
[WAIT] 2.6 с перед запросом ссылки
|
|
2026-08-03 URL:https://s684vla.storage.yandex.net/rdisk/... [793122/793122]
|
|
```
|
|
|
|
### Итоговая статистика
|
|
|
|
```text
|
|
================ РЕЗУЛЬТАТ ================
|
|
Найдено файлов: 12089
|
|
Скачано сейчас: 529
|
|
Уже было скачано: 11560
|
|
Ошибок: 0
|
|
Известный общий объём: 49.04 GiB
|
|
===========================================
|
|
```
|
|
|
|
## CAPTCHA
|
|
|
|
При большом количестве запросов Яндекс может вернуть CAPTCHA.
|
|
|
|
Скрипт распознаёт ответ:
|
|
|
|
```json
|
|
{
|
|
"type": "captcha"
|
|
}
|
|
```
|
|
|
|
После этого загрузка немедленно прекращается.
|
|
|
|
Будет выведена ссылка:
|
|
|
|
```text
|
|
ЯНДЕКС ПОТРЕБОВАЛ CAPTCHA
|
|
|
|
Открой ссылку ниже в обычном браузере с того же внешнего IP:
|
|
|
|
https://disk.yandex.ru/showcaptcha?...
|
|
```
|
|
|
|
Ссылка также сохраняется в файл:
|
|
|
|
```text
|
|
/mnt/hdd1-4TB/ODIS/Flashdaten/Brand-V/yandex-captcha-url.txt
|
|
```
|
|
|
|
## Ошибка `409 Conflict`
|
|
|
|
Внутренний API `/public/api/fetch-list` может вернуть `409 Conflict`, если серверное состояние пагинации изменилось или стало недействительным.
|
|
|
|
Двухэтапная схема значительно снижает вероятность такой ошибки, потому что все страницы каталога запрашиваются до начала скачивания файлов.
|
|
|
|
`409` не следует считать признаком конца каталога. Если он всё же возник на этапе 1:
|
|
|
|
1. не удаляйте уже скачанные файлы;
|
|
2. повторно запустите программу;
|
|
3. скрипт получит новый `sk` и начнёт перечисление заново;
|
|
4. на этапе 2 существующие файлы будут пропущены.
|
|
|
|
Полным считается только обход, завершившийся серверным признаком:
|
|
|
|
```text
|
|
completed=True
|
|
```
|
|
|
|
|
|
|
|
## Коды завершения
|
|
|
|
Скрипт использует следующие коды выхода:
|
|
|
|
| Код | Значение |
|
|
| ----: | ------------------------------------------ |
|
|
| `0` | загрузка завершена без ошибок |
|
|
| `1` | загрузка завершена с ошибками |
|
|
| `2` | ошибка конфигурации или отсутствует `wget` |
|
|
| `75` | Яндекс потребовал CAPTCHA |
|
|
| `130` | процесс остановлен через `Ctrl+C` |
|
|
|
|
Проверить код после завершения:
|
|
|
|
```bash
|
|
echo $?
|
|
```
|
|
|