# Go Crawler — быстрый краулер/зеркалатор сайта на Go Проект делает локальную «зеркальную» копию сайта: обходит страницы, сохраняет HTML на диск и параллельно скачивает ассеты (CSS/JS/картинки/шрифты и т. п.). Для рендеринга страниц используется headless Chrome через `chromedp`, поэтому краулер подходит для сайтов, где контент появляется после выполнения JavaScript. > Важно: используйте краулер только на сайтах, которые вы имеете право скачивать и архивировать. Учитывайте правила сайта и его ограничения (включая robots.txt и rate limits). Авторизация/капчи/антибот в проекте не реализованы. ## Основные возможности - **Параллельный обход страниц**: несколько воркеров, каждый со своим экземпляром браузера. - **Параллельная загрузка ассетов**: отдельные HTTP-воркеры скачивают ресурсы быстрее, чем через браузер. - **Ограничение по доменам**: скачиваются только URL из `ALLOWED_ORIGINS`. - **Нормализация URL**: убирается `#fragment`, приводятся относительные ссылки к абсолютным, удаляется хвостовой `/` (кроме корня) — это уменьшает дубли. - **Дедупликация**: страницы и ассеты не обрабатываются повторно. - **Сохранение на диск в структуру, похожую на URL**: - директории превращаются в `index.html` (например, `/docs/` → `/docs/index.html`), - query-параметры хешируются и добавляются к имени файла, чтобы разные варианты URL не перезатирали друг друга. - **Опциональная «починка» ссылок**: абсолютные ссылки на разрешённые домены переписываются в относительные, чтобы зеркало открывалось локально. - **Принудительный UTF‑8 meta charset**: чтобы уменьшить проблемы с кодировкой в сохранённых HTML. - **Корректное завершение** по `Ctrl+C`. ## Требования - Go 1.21+ - Установленный Chrome/Chromium (используется `chromedp`) ## Быстрый старт ```bash go mod tidy # Запуск без сборки go run . # Или сборка go build -o crawler.exe . ./crawler.exe ``` После запуска в консоли будет прогресс вида `[crawled] ...` и итоговая статистика (pages/assets/links fixed/time). ## Конфигурация Вся конфигурация сейчас задаётся константами в начале файла `crawl.go`. Ключевые параметры: ```go const ( BASE_URL = "https://rutracker.org/forum/" // Стартовая точка обхода OUT_DIR = "rutracker.org" // Папка, куда сохраняется зеркало MAX_PAGES = 50000 // Лимит страниц (защита от бесконечного обхода) WORKERS = 10 // Кол-во браузеров (воркеров страниц) ASSET_WORKERS = 20 // Кол-во HTTP-воркеров для ассетов WAIT_AFTER_LOAD = 2 * time.Second // Пауза после Navigate для прогрузки JS REQUEST_TIMEOUT = 30 * time.Second // Таймаут навигации FIX_LINKS = true // Переписывать ссылки на относительные ) var ALLOWED_ORIGINS = []string{ "https://rutracker.org", "https://www.rutracker.org", } ``` ### Как подобрать `ALLOWED_ORIGINS` Если сайт грузит ресурсы с CDN/поддоменов, их нужно добавить в `ALLOWED_ORIGINS`, иначе ассеты и ссылки на них будут пропускаться. ### Тюнинг производительности - Увеличивайте `WORKERS`, если упираетесь в скорость рендеринга страниц (но возрастут CPU/RAM). - Увеличивайте `ASSET_WORKERS`, если узкое место — скачивание ассетов. - `WAIT_AFTER_LOAD` критичен для SPA/React/Vue сайтов: слишком маленькое значение даст «пустые» HTML. ## Как это работает (в общих чертах) 1. В `pageQueue` кладётся нормализованный `BASE_URL`. 2. `WORKERS` воркеров берут URL страницы, открывают новую вкладку, делают `Navigate`, ждут `WAIT_AFTER_LOAD`, сохраняют `OuterHTML`. 3. Во время загрузки страницы подписка на `network` события собирает URL ресурсов; «не‑страницы» отправляются в `assetQueue`. 4. HTML парсится регулярками, извлекаются ссылки (`href`, `src`, `data-src`, а также некоторые JSON‑поля вроде `"url"`, `"pathname"`). 5. Если включён `FIX_LINKS`, ссылки на разрешённые домены переписываются в относительные пути на локальные файлы. 6. HTML и ассеты сохраняются в `OUT_DIR` по схеме URL→путь. ## Что считается страницей, а что ассетом Функция `isPageURL()` относит URL к ассетам по расширению (например, `.js`, `.css`, изображения, шрифты, видео/аудио, `.json`, `.xml`, `.pdf`, архивы). Всё остальное считается страницей и идёт в очередь обхода. ## Архитектура ``` ┌─────────────────────────────────────────────────────────────┐ │ main() │ │ │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │Worker 1 │ │Worker 2 │ │Worker 3 │ │ ... │ × WORKERS │ │(Browser)│ │(Browser)│ │(Browser)│ │(Browser)│ │ │ └────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ │ │ │ │ │ │ │ │ └────────────┴─────┬──────┴────────────┘ │ │ │ │ │ pageQueue (chan string) │ │ │ │ │ ┌──────────────────┴──────────────────┐ │ │ │ │ │ │ ┌────┴────┐ ┌─────────┐ ┌─────────┐ ┌───┴───┐ │ │ │Asset W1 │ │Asset W2 │ │Asset W3 │ │ ... │ × ASSET_WORKERS │ │ (HTTP) │ │ (HTTP) │ │ (HTTP) │ │ (HTTP)│ │ │ └─────────┘ └─────────┘ └─────────┘ └───────┘ │ │ │ │ │ assetQueue (chan AssetRequest) │ └─────────────────────────────────────────────────────────────┘ ``` ## Ограничения и нюансы - **Robots/лимиты**: проект не читает `robots.txt` и не ограничивает частоту запросов тонко. Для «бережного» обхода уменьшайте `WORKERS`/`ASSET_WORKERS` и/или добавляйте задержки (пока это только `WAIT_AFTER_LOAD`). - **Авторизация и сессии**: нет логина, cookie‑менеджмента, обхода капч/антибота. - **Полнота зеркала**: ссылки извлекаются регулярками и сетью браузера; динамически генерируемые URL могут быть пропущены. - **Очень большие сайты**: `MAX_PAGES` — обязательная страховка. Также очереди и карты дедупликации держатся в памяти. - **Шумный вывод**: есть отладочные строки `[debug] Found ...`, а также `[skip-external]`/`[skip-asset]` — при необходимости их можно убрать/загейтить флагом. ## Типичный сценарий использования 1. Задайте `BASE_URL` и `OUT_DIR`. 2. Заполните `ALLOWED_ORIGINS` (включая поддомены/CDN, если они нужны). 3. Настройте параллельность (`WORKERS`, `ASSET_WORKERS`) и тайминги (`WAIT_AFTER_LOAD`, `REQUEST_TIMEOUT`). 4. Запустите `go run .` и дождитесь `[done] ...`. ## Лицензия Лицензия в репозитории не указана.