DFWebsite_Downloader

Набор утилит для офлайн-зеркалирования сайтов: скачивание HTML-страниц, связанных ассетов и локальный запуск полученной копии.

Проект ориентирован на Python и подходит для:

  • статических сайтов;
  • смешанных сайтов, где часть контента объявлена в HTML/CSS/JS/JSON;
  • диагностического сохранения структуры ресурсов для последующего анализа.

Важное о лицензии

В репозитории используется лицензия PERMISSION REQUIRED LICENSE (PRL) v1.0.

Это означает, что без явного письменного разрешения автора вам не предоставляются права на использование, копирование, модификацию, публикацию и распространение кода.

Подробности — в файле LICENSE.

Возможности

website_downloader.py

  • Обход HTML-страниц выполняется только в пределах основного домена стартового URL.
  • Загрузка ассетов поддерживает:
    • CSS, JS, изображения, шрифты, JSON и прочие ресурсы;
    • опциональное скачивание ассетов с внешних доменов (--external);
    • запрет отдельных доменов (--exclude-domain, можно несколько раз).
  • Извлечение зависимостей:
    • из CSS: url(...), @import;
    • из JS: эвристическое извлечение путей/URL из строк;
    • из JSON: рекурсивный поиск строковых URL/путей с докачкой ссылок.
  • Поддержка cache-busting query (?v=..., ?t=...):
    • локальное имя файла получает суффикс __q_<hash>;
    • это предотвращает коллизии и помогает корректно раздавать такие файлы локально.
  • Дополнительная обработка популярных паттернов:
    • srcset, data-src, data-srcset;
    • meta-изображения (og:image, twitter:image);
    • video poster, audio/video/source;
    • iframe (внутридоменные добавляются в обход, внешние фиксируются в отчёте).
  • Генерация отчёта download_report.json с итоговой статистикой.

serve_site.py

Локальный сервер для каталога скачанного сайта на http://127.0.0.1:8080/ с поддержкой:

  • pretty URL fallback:
    • /lab/lab.html
    • /dir//dir/index.html
  • файлов с cache-busting query:
    • /assets/app.css?v=123/assets/app__q_<hash>.css
  • fallback на уже сохранённые варианты __q_* даже при запросе без query.

Как это работает

  1. Скрипт начинает со стартового URL и формирует очередь страниц.
  2. Для каждой страницы:
    • сохраняет HTML;
    • извлекает ресурсы и ссылки;
    • скачивает ассеты и вложенные зависимости.
  3. Для CSS/JS/JSON выполняет дополнительный разбор на скрытые зависимости.
  4. Ссылки на страницы добавляются в очередь с учётом max_depth.
  5. По завершении формируется download_report.json и печатается сводка.

Требования

  • Python 3.10+ (рекомендуется).
  • Зависимости из requirements.txt:
    • requests
    • beautifulsoup4

Установка (Windows / PowerShell)

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt

Установка (Linux/macOS)

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

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

Синтаксис

python .\website_downloader.py <URL> [output_dir] [max_depth] [max_pages] [--external] [--exclude-domain <domain>]

Параметры

  • URL — стартовый адрес сайта (обязательно).
  • output_dir — каталог сохранения (по умолчанию: website_download).
  • max_depth — максимальная глубина обхода HTML-ссылок (по умолчанию: 5).
  • max_pages — общий ограничитель на количество загруженных URL (по умолчанию: 1000).
  • --external — разрешает скачивать ассеты с внешних доменов.
  • --exclude-domain <domain> — запрещает скачивание с указанного домена (можно повторять).

По умолчанию уже добавлен исключённый домен: cdn.dribbble.com.

Примеры

# Минимальный запуск
python .\website_downloader.py https://example.com

# Папка + ограничение глубины/объёма
python .\website_downloader.py https://example.com my_site 3 500

# Разрешить внешние ассеты
python .\website_downloader.py https://example.com my_site 3 500 --external

# Разрешить внешние ассеты, но отключить конкретные домены
python .\website_downloader.py https://example.com my_site 3 500 --external --exclude-domain cdn.dribbble.com --exclude-domain fonts.example.com

Использование локального сервера

Синтаксис

python .\serve_site.py <URL-or-folder>

Что можно передать

  • URL сайта (папка будет определена по netloc), например https://www.example.com/;
  • имя папки рядом со скриптом, например example.com;
  • абсолютный путь к каталогу сайта.

Примеры

python .\serve_site.py https://www.example.com/
python .\serve_site.py example.com
python .\serve_site.py D:\PROJECTS\@NEW\DFWebsite_Downloader\my_site

После запуска откройте:

  • http://127.0.0.1:8080/

Остановка сервера: Ctrl + C.

Docker (опционально)

В проекте есть docker-compose.yml с nginx, который раздаёт каталог ./site на порту 8080.

docker compose up -d

Важно: downloader по умолчанию сохраняет в website_download, поэтому для Docker-раздачи:

  • либо запускайте downloader с output_dir=site,
  • либо скорректируйте volume в docker-compose.yml.

Структура результата

После скачивания в каталоге output_dir обычно появляются:

  • HTML-страницы (index.html, about.html, work/...);
  • ассеты (css, js, images, fonts, external/...);
  • download_report.json — итоговый отчёт.

В корне проекта создаётся лог:

  • downloader.log

Формат download_report.json

Ключевые поля отчёта:

  • start_url, domain, output_dir, timestamp;
  • total_downloaded, total_failed;
  • resources (разбивка на pages, styles, scripts, images, fonts, other);
  • skipped_external (что найдено, но пропущено политикой доменов);
  • external_iframes (внешние iframe, не включённые в обход).

Ограничения

Полная «бит-в-бит» копия сайта не всегда возможна только HTTP-скрапером. Проблемные случаи:

  • SPA-маршрутизация и контент, рендерящийся только в браузере после выполнения JS;
  • страницы и API за авторизацией;
  • динамические URL, вычисляемые в runtime и не присутствующие в исходниках;
  • антибот-защита, rate limit, гео/региональные ограничения.

Для таких случаев нужен отдельный браузерный режим (например, Playwright) с перехватом сетевых запросов.

Типовые проблемы и решения

1) 404 на файлы с ?v=...

Решено через схему __q_<hash> в downloader + соответствующий fallback в serve_site.py.

2) Не хватает ресурсов, которых нет в HTML

В проекте уже есть разбор CSS/JS/JSON, но при сложной runtime-логике часть зависимостей может не обнаружиться без браузерного выполнения JS.

3) Внешние ресурсы не скачиваются

Проверьте:

  • добавлен ли флаг --external;
  • не попадает ли домен под --exclude-domain.

Файлы проекта

  • website_downloader.py — основной downloader.
  • serve_site.py — локальный HTTP-сервер для скачанного сайта.
  • requirements.txt — Python-зависимости.
  • docker-compose.yml — опциональная раздача через nginx.
  • LICENSE — лицензионные условия.

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

python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt
python .\website_downloader.py https://example.com site 4 800 --external
python .\serve_site.py site
Description
Набор утилит для офлайн-зеркалирования сайтов: скачивание HTML-страниц, связанных ассетов и локальный запуск полученной копии.
Readme 53 KiB
Languages
Python 100%