Files
go-crawler/README.md

132 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 не перезатирали друг друга.
- **Опциональная «починка» ссылок**: абсолютные ссылки на разрешённые домены переписываются в относительные, чтобы зеркало открывалось локально.
- **Принудительный UTF8 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] ...`.
## Лицензия
Лицензия в репозитории не указана.