Зеркало сайта с реальным браузером: запускает Chromium (Playwright), обходит страницы, кликает раскрывающие кнопки/аккордеоны, скроллит для lazy-load и сохраняет оригинальные тела всех HTTP-ответов (HTML + CSS + JS + шрифты + картинки + XHR) с сохранением исходной структуры путей. Это значит, что SPA (React/Vue/Vite и т.п.) продолжают работать со стилями, роутингом и логикой — нужно просто отдать зеркало локальным HTTP-сервером.
python -m venv .venv; .venv\Scripts\activate
pip install -r requirements.txt
python -m playwright install chromium
python crawler.py https://example.com --out .\site --max-pages 30
python serve.py .\site\example.com 8000Открой http://127.0.0.1:8000.
crawler.py— обход сайта, клики, сохранение ответов.serve.py— локальный HTTP-сервер с SPA-fallback.requirements.txt— зависимости (толькоplaywright).site/— сюда складывается зеркало (в.gitignore).
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
python -m playwright install chromiumpython crawler.py https://example.com --out .\site --max-pages 50Опции:
--out— корневая папка (по умолчанию./site).--max-pages— лимит страниц при BFS-обходе.--any-subdomain— разрешить поддомены исходного хоста.--headless— без окна браузера.
Результат лежит в site/<host>/... ровно с теми же путями, что и на боевом
сайте. Ничего в HTML/JS/CSS не переписывается — поэтому все абсолютные
/assets/xxx.js и относительные url(../fonts/x.woff2) резолвятся как есть.
SPA нельзя открывать через file:// — модули ES, fetch и клиентский
роутер не будут работать. Используй встроенный сервер:
python serve.py .\site\example.com 8000Открой http://127.0.0.1:8000. Для неизвестных маршрутов сервер отдаёт
index.html (SPA-fallback), чтобы клиентский роутер сам разрулил путь.
CLICK_SELECTORS в crawler.py: button, [role=button], [aria-expanded=false],
details > summary, .accordion-*, .load-more/.show-more и похожие.
Кнопки с текстом «войти/купить/удалить/подписаться/checkout» пропускаются
(SKIP_CLICK_TEXT), чтобы случайно не отправить формы.
- Динамика, которая грузится только по действию пользователя после авторизации,
требует ручного логина. Запусти без
--headless, залогинься, и перезапусти соstorage_state(см. Playwright docs) — легко допилить. - Агрессивные анти-боты (Cloudflare Turnstile и т.п.) могут блокировать.
- Серверную логику (API за токеном, приватные эндпоинты) скопировать нельзя — сохранится только то, что реально отдавалось в ответ на HTTP-запрос в твоей сессии.
- Уважай
robots.txtи ToS сайта.