10 KiB
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)
Быстрый старт
go mod tidy
# Запуск без сборки
go run .
# Или сборка
go build -o crawler.exe .
./crawler.exe
После запуска в консоли будет прогресс вида [crawled] ... и итоговая статистика (pages/assets/links fixed/time).
Конфигурация
Вся конфигурация сейчас задаётся константами в начале файла crawl.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.
Как это работает (в общих чертах)
- В
pageQueueкладётся нормализованныйBASE_URL. WORKERSворкеров берут URL страницы, открывают новую вкладку, делаютNavigate, ждутWAIT_AFTER_LOAD, сохраняютOuterHTML.- Во время загрузки страницы подписка на
networkсобытия собирает URL ресурсов; «не‑страницы» отправляются вassetQueue. - HTML парсится регулярками, извлекаются ссылки (
href,src,data-src, а также некоторые JSON‑поля вроде"url","pathname"). - Если включён
FIX_LINKS, ссылки на разрешённые домены переписываются в относительные пути на локальные файлы. - 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]— при необходимости их можно убрать/загейтить флагом.
Типичный сценарий использования
- Задайте
BASE_URLиOUT_DIR. - Заполните
ALLOWED_ORIGINS(включая поддомены/CDN, если они нужны). - Настройте параллельность (
WORKERS,ASSET_WORKERS) и тайминги (WAIT_AFTER_LOAD,REQUEST_TIMEOUT). - Запустите
go run .и дождитесь[done] ....
Лицензия
Лицензия в репозитории не указана.