Извлечение контента на практике
Это руководство поможет вам понять принципы работы и лучшие практики извлечения HTML-контента через реальные сценарии.
Обзор процесса извлечения
При вызове Extract библиотека выполняет следующие шаги:
Ввод HTML → валидация ввода → определение кодировки (автоконвертация в UTF-8) → разбор DOM → проверка глубины
→ безопасное санирование (опц.) → распознавание статьи (опц.) → извлечение содержимого → форматирование → возврат ResultПроверка глубины выполняется до санирования: сначала итеративно проверяется глубина DOM (чтобы избежать переполнения стека из-за рекурсивного обхода), а затем выполняется безопасное санирование разобранного DOM-дерева. Обе операции работают с разобранным деревом узлов, поэтому разбор DOM всегда предшествует им обеим.
Каждый шаг можно настроить через конфигурацию.
Базовое извлечение текста
Простейшее использование — извлечение контента из байтов HTML:
package main
import (
"fmt"
"log"
"github.com/cybergodev/html"
)
func main() {
data := []byte(`<html>
<head><title>Руководство по Go</title></head>
<body>
<article>
<h1>Введение в Go</h1>
<p>Go — это статически типизированный компилируемый язык со встроенной поддержкой параллелизма.</p>
<p>Он отличается быстрой компиляцией, простотой развёртывания и подходит для создания высокопроизводительных сервисов.</p>
<img src="gopher.png" alt="Талисман Gopher" />
<a href="https://go.dev">Официальный сайт Go</a>
</article>
</body>
</html>`)
result, err := html.Extract(data)
if err != nil {
log.Fatal(err)
}
fmt.Println("Заголовок:", result.Title)
// Заголовок: Руководство по Go
fmt.Println("Текст:", result.Text)
// Текст: Введение в Go
// Go — это статически типизированный компилируемый язык со встроенной поддержкой параллелизма.
// Он отличается быстрой компиляцией, простотой развёртывания и подходит для создания высокопроизводительных сервисов.
// Официальный сайт Go
fmt.Println("Слов:", result.WordCount)
// Слов: 7
fmt.Println("Время чтения:", result.ReadingTime)
// Время чтения: 2.1с (при расчёте 200 слов/мин)
fmt.Println("Изображений:", len(result.Images))
// Изображений: 1
fmt.Println("Ссылок:", len(result.Links))
// Ссылок: 1
}Понимание результата извлечения
Result содержит следующие поля:
| Поле | Тип | Описание |
|---|---|---|
Title | string | Заголовок страницы, приоритет: <title>, затем <h1>, <h2> |
Text | string | Основной текст (очищенный, без тегов и лишних пробелов) |
Images | []ImageInfo | Список извлечённых изображений |
Links | []LinkInfo | Список извлечённых ссылок |
Videos | []VideoInfo | Список извлечённых видео |
Audios | []AudioInfo | Список извлечённых аудио |
WordCount | int | Количество слов в тексте |
ReadingTime | time.Duration | Расчётное время чтения (200 слов/мин) |
ProcessingTime | time.Duration | Время обработки |
Извлечение из файла
Для обработки локальных HTML-файлов используйте ExtractFromFile:
result, err := html.ExtractFromFile("article.html")
if err != nil {
log.Fatal(err)
}
fmt.Println("Заголовок:", result.Title)Файловые операции включают встроенные проверки безопасности:
- Автоматическое обнаружение атак с обходом пути (например,
../../../etc/passwd) - Размер файла ограничен
MaxInputSize - Сообщения об ошибках скрывают полный путь через
SafePath()
Алгоритм распознавания статей
Когда ExtractArticle равен true (по умолчанию), библиотека автоматически определяет «основную область контента» на странице.
Принцип работы
- Оценка кандидатов: обход дерева DOM, вычисление оценки релевантности контента для каждого элемента
- Выбор лучшего кандидата: выбор узла с наивысшей оценкой в качестве контейнера статьи
- Механизм отката: если подходящий кандидат не найден, откат к узлу
<body>
Подходящие сценарии
Распознавание статей лучше всего подходит для новостных сайтов, блогов, документации и других страниц с чётко выраженной «областью основного контента». Для навигационных страниц и страниц-списков может быть затруднено точное определение основного текста.
Пользовательский скоринг
Настройка логики скоринга через реализацию интерфейса Scorer:
type myScorer struct{}
func (s myScorer) Score(node html.ContentNode) int {
// Возвращаем оценку на основе характеристик узла
class := node.AttrValue("class")
if strings.Contains(class, "article") || strings.Contains(class, "post") {
return 100
}
if strings.Contains(class, "sidebar") || strings.Contains(class, "comment") {
return -50
}
return 0
}
func (s myScorer) ShouldRemove(node html.ContentNode) bool {
// Возвращаем true для удаления узла
return node.Data() == "nav" || node.Data() == "footer"
}Примечание
Функция strings.Contains в этом примере из стандартного пакета strings. Полный работоспособный пример см. в Тестирование и пользовательские расширения.
Извлечение только текста
Когда нужен только текст без изображений, ссылок и других метаданных:
text, err := html.ExtractText(data)
if err != nil {
log.Fatal(err)
}
fmt.Println(text)Это особенно полезно для анализа текста, построения поисковых индексов и других сценариев.
Обработка не-UTF-8 кодировок
Библиотека автоматически определяет 15+ кодировок (включая UTF-8, GBK, Shift_JIS, Windows-1252 и др.) и автоматически конвертирует в UTF-8.
// Автоматическое определение кодировки
result, err := html.Extract(gbkEncodedData)
// Ручное указание кодировки
cfg := html.DefaultConfig()
cfg.Encoding = "gbk"
result, err = html.Extract(gbkEncodedData, cfg)Контекст и тайм-аут
Для больших файлов или HTML из ненадёжных источников рекомендуется использовать версии с контекстом:
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
result, err := html.ExtractWithContext(ctx, data)
if errors.Is(err, html.ErrProcessingTimeout) {
log.Println("Тайм-аут обработки")
}Следующие шаги
- Выбор формата вывода - Выбор подходящего формата для вашего сценария
- Повторное использование Processor и кэш - Оптимизация производительности при высокочастотных вызовах
- Справочник API: Функции пакета - Полные сигнатуры функций