Зеркалирование сайта это задача с длинной бородой: сделать локальную копию документации, снять статический слепок старого проекта перед выключением хостинга, собрать офлайн-архив материалов для чтения без сети. Все скачивальщики мира меркнут перед wget в этой роли: он рекурсивно обходит страницы, переписывает ссылки для локального просмотра, докачивает оборванные файлы и вежливо держит темп, чтобы не уронить чужой сервер. В статье разбираем реальную настройку: champion-набор флагов, тонкости с robots.txt, авторизация, фильтры по типам файлов и подводные камни динамических сайтов.
Базовый набор флагов для полного зеркала
Классическая команда, покрывающая большинство случаев, выглядит так:
wget --mirror --convert-links --adjust-extension --page-requisites --no-parent \
--wait=0.5 --random-wait --tries=3 --timeout=30 \
https://docs.example.com/
Что здесь делает каждый кусок. --mirror это сокращение от набора -r -N -l inf --no-remove-listing: рекурсивный обход без ограничения глубины, скачивание только новых файлов по меткам времени и сохранение листингов. --convert-links после скачивания переписывает ссылки в HTML так, чтобы они указывали на локальные файлы - именно это превращает набор страниц в работающий офлайн-сайт. --page-requisites догружает CSS, картинки и скрипты, нужные странице для отображения. --no-parent запрещает подниматься выше стартового пути, чтобы зеркало docs.example.com/manual/ не растеклось на весь домен. --wait с --random-wait разводит запросы на полсекунды с разбросом, что вежливо к серверу; --tries и --timeout определяют упорство при сетевых сбоях.
Результат укладывается в каталог вида docs.example.com/manual/..., пригодный для открытия файлом из файлового менеджера или отдачи через локальный nginx для рабочего доступа всей команды.
Robots.txt, что wget делает по умолчанию и как устроены ограничения
wget по умолчанию уважает robots.txt: если сайт запрещает обход раздела, рекурсия туда не пойдёт. Флаг -e robots=off отключает это повиновение, и тут стоит сказать прямо: отключать чужие правила ради массового скачивания чужого контента это этический и иногда юридический вопрос, а не технический приём. Robots.txt на чужом сайте отражает волю владельца, и обходить её стоит только с пониманием последствий. Справедливый сценарий для robots=off - ваш собственный сайт, где robots.txt закрывает служебные разделы для поисковиков, а вы хотите получить полный архив; или явное разрешение владельца.
Для контроля охвата без грубого отключения есть тонкие инструменты: --accept REGEX и --reject REGEX фильтруют имена файлов (скажем, скачивать только html и pdf, но не тянуть wav), --accept-regex и --reject-regex фильтруют сами URL (например, исключить раздел /forum/ из зеркала документации), а --spider в связке с --spider -r позволяет сначала сделать сухой прогон и посмотреть карту сайта, не скачивая сами файлы. Дополнительный мягкий ограничитель - --quota=500m, который останавливает процесс прежде, чем архив съест весь диск.
Авторизация, куки и формы
Зеркалирование закрытых разделов делается двумя способами. Простой - basic auth прямо в команде: --user=имя --password=пароль работает для ресурсов с HTTP-аутентификацией. Сложный и реалистичный - сначала скормить wget сессию браузера: экспортируйте cookie из браузера подходящим расширением в формате Netscape и запустите wget --load-cookies cookies.txt. Тогда обход идёт уже от имени залогиненного пользователя.
Для случаев, когда нужна своя цепочка логина, wget умеет и формы: --post-data="user=x&password=y" --save-cookies session.txt на странице логина, затем --load-cookies session.txt --keep-session-cookies для самого зеркалирования. Флаг --keep-session-cookies важен: без него wget при повторной загрузке отбрасывает куки без срока жизни, и сессионная аутентификация рассыпается.
Отдельно стоит предупредить о --http-user и --password в URL: пароли открытым текстом в истории shell это заявка на утечку. Разумный минимум - файл .wgetrc с правами 600 в домашнем каталоге или переменные окружения.
Возобновление, повторные прогоны и инкрементальность
Долгие зеркалирования обрываются: сеть, перезагрузка, банальное закрытие ноутбука. Флаг -c (continue) докачивает недокачанные файлы и делает повторный запуск безболезненным: wget -c --mirror ... просто заберёт недостающее. Для больших сайтов разумно с самого начала включать -c и --tries=0 (бесконечно попыток с мягкими паузами), а также перенаправить лог: -o wget.log, чтобы утром было что почитать о ночных неудачах.
Инкрементальное обновление зеркала опирается на -N (timestamping): wget запрашивает сервер, изменился ли файл с момента прошлого скачивания, и берёт только новое. Caveat: timestamping работает только если сервер честно отдаёт Last-Modified; на многих динамических сайтах заголовка нет, и тогда каждый прогон выглядит как полное перескачивание. Облегчающий приём для таких сайтов это --mirror в паре с --no-if-modified-since и разумным объёмным лимитом, но обычно проще смириться с повторной вытяжкой.
Ещё одна рабочая деталь: --mirror по умолчанию оставляет файлы, удалённые на сервере, в локальной копии - что логично для архива, но неожиданно для тех, кто ждал зеркала 'один в один'. Точная синхронизация требует отдельной зачистки сравнением списков, wget такого не делает по соображениям безопасности данных, и это правильное решение авторами инструмента.
Динамические сайты, честно о границах инструмента
wget статичен по своей природе: он скачивает то, что отдаёт сервер, и не исполняет JavaScript. Сайт на React со SPA-роутингом после зеркалирования превратится в пустой index.html и пачку JS-бандлов. Признаки того, что wget не под задачей: контент подгружается из API, URL выглядят как якорные маршруты, View Source показывает скелет вместо текста. Для таких случаев служат headless-браузеры или специализированные краулеры, и героически бороться с этим через wget бесполезно.
Обратное верно тоже: статическая документация, блоги на генераторах, старые сайты на чистом HTML - идеальные кандидаты, и wget собирает их за минуты, расставляя всё по полочкам. Критерий пригодности проверяется в одну команду: curl -s URL | grep -q '<article' и смотрим, есть ли в исходнике настоящий текст.
Финальная сборка для боевого прогона
Соберём законченный шаблон офлайн-копии документации проекта, которую вы поддерживаете:
wget --mirror -c -N \
--convert-links --adjust-extension --page-requisites \
--no-parent --wait=0.5 --random-wait \
--reject-regex '\\?.*' \
-e robots=off \
--load-cookies session.txt --keep-session-cookies \
https://docs.internal.example.com/handbook/ \
-o wget-mirror.log
Отдельные пояснения к выбору. Reject regex с вопросительным знаком отсекает URL с query-строками, которые на динамических движках плодят бесконечные варианты одной страницы и раздувают архив в разы. robots=off здесь применим потому, что сайт ваш. session.txt получен заранее логином через --save-cookies. Лог пишется всегда.
После завершения остаётся проверить точки отказа: открыть несколько страниц локально, кликнуть на перекрёстные ссылки, убедиться, что CSS подтянут (если дизайн сломался, добавьте --span-hosts --domains=cdn.example.com для статики на отдельном хосте, потому что wget по умолчанию не уходит с исходного домена). И последнее: сделайте тестовый запуск на маленьком разделе с --spider или ограничением -l 1, чтобы откалибровать фильтры раньше, чем длинный прогон напишет гигабайты мусора. Пятнадцать минут калибровки экономят час ночного ожидания, проверенная закономерность.
Итоговая оценка проста: wget это швейцарский нож офлайн-архивирования, который при правильном наборе флагов делает работу безупречно, а при неправильном либо недокачивает, либо пытается скачать интернет. Разница между этими исходами лежит именно в дисциплине описанных выше флагов: ограничение области, вежливый темп, корректная работа с сессиями и честное понимание, что динамический веб этому инструменту не по зубам.
## Зеркалирование не только HTML, FTP и файловые архивы
wget одинаково ловко зеркалирует и FTP-архивы: wget -m ftp://mirror.example.com/pub/linux/ собирает дерево каталогов вместе с листингами. Для файловых раздатчиков важнее всего два флага: -N для инкрементальности и -c для докачки оборванных образов на десятки гигабайт. В паре с --tries=0 такой прогон переживает ночные разрывы соединения, а утром вы получаете целый архив вместо половины ISO-образа. Если на сервере нужна авторизация, честным способом будет --ftp-user и --ftp-password в .wgetrc.
Для больших файловых коллекций полезен параметр --limit-rate=10m, который ограничивает скорость и не даёт архиву съесть весь канал офиса. Комбинируется это с запуском в нерабочие часы через cron: ночное зеркалирование с ограничением полосы и возобновлением это классическая сделка, где никто не страдает.
Повторяемость, cron, отчёты и упаковка архивов
Зеркалирование редко бывает разовым - обычно это регулярная задача обновления архива. Рабочая схема: скрипт mirror-docs.sh с корректной командой кладётся в /usr/local/bin, в cron прописывается запуск в 3 часа ночи, а вывод пишется в лог с датой в имени. Логи ротируются простым find -mtime +30 -delete в том же расписании.
Отчётность после прогона строится по логу: подсчёт строк с кодом 200 даёт число успешно скачанных файлов, а grep -E '(ERROR|failed)' собирает неудачи. На больших сайтах удобно сравнивать число файлов между прогонами через find . -type f | wc -l: резкое падение числа вдвое означает, что сайт переехал на другую CMS или поменял структуру, и фильтры пора пересматривать. Резкий прирост намекает на разрастание URL с query-параметрами, где regex-фильтр перестал удерживать дубли.
Когда архив нужен для истории, а не для онлайн-чтения, удобно после прогона паковать каталог: tar -I 'zstd -3' -cf mirror-2026-10.tar.zst docs.example.com/ убирает сотни тысяч мелких файлов в один компактный образ, который дешевле хранить и быстрее копировать на внешний диск. Распаковка обратно выполняется линейным чтением, и архив остаётся самодостаточным: HTML, картинки и ссылки уже сконвертированы, заморочек с зависимостями больше нет.
Когда wget - не лучший выбор
Есть задачи, которые wget решает со скрипом, и это надо признать. Огромные динамические порталы с пагинацией через POST, сайты с агрессивной anti-bot защитой на уровне CDN, ресурсы, требующие JS-челлендж - всё это территория специализированных инструментов. Для офлайн-копий сложных сайтов существует HTTrack, который умеет немного больше в части обхода и локальной подмены ссылок, но и он спотыкается о JavaScript-рендеринг. На чужих ресурсах с жёстким rate limiting честный ответ - либо официальный API владельца, либо его экспорт, а не борьба с защитой.
Для собственных проектов и открытых документаций wget остаётся главным кандидатом: он везде уже установлен, его поведение предсказуемо до последнего флага, а выкладку зеркала на внутренний nginx можно автоматизировать в две строки cron. Такая связка копия плюс локальный веб-сервер даёт команде офисный архив документации, который доступен и тогда, когда интернет вдруг непривычно медленный, и продолжает работать, когда внешний ресурс уже давно переехал на новый движок.
И напоследок о формате: держите эталонную команду зеркалирования в README проекта рядом с самим архивом. Через год никто не вспомнит, откуда бралась копия и какие фильтры применялись, а короткая строка с флагами мгновенно отвечает и на вопрос происхождения, и на вопрос воспроизводимости - это дешёвая страховка институциональной памяти команды.
Зеркальте в удовольствие.