Парсинг и генерация HTML в Go
Парсинг — это анализ и интерпретация структуры документа. Процесс может включать извлечение конкретных элементов, атрибутов или данных из документа и проверку того, что документ корректно сформирован и соответствует ожиданиям. Парсинг часто используется для извлечения данных из веб‑страниц, рендеринга или для предобработки HTML перед отображением пользователю.
Go предоставляет пакеты для работы с документами в форматах HTML и XML. Для разбора HTML чаще всего используют пакет golang.org/x/net/html (реализует токенизацию и построение дерева узлов), а для безопасной генерации HTML — html/template из стандартной библиотеки.
Важно: пакет golang.org/x/net/html находится в репозитории x/net (поддерживается командой Go), а пакет html/template — в стандартной библиотеке. html/template выполняет автоматическое экранирование, что помогает предотвращать XSS при генерации HTML.
Пакет html (golang.org/x/net/html)
Пакет golang.org/x/net/html предоставляет HTML5‑совместимый токенизатор и парсер, даёт возможность строить дерево узлов документа (DOM‑подобная структура), обходить его и извлекать нужные элементы.
Ключевые функции и типы:
- html.Parse(io.Reader) — парсит HTML и возвращает корневой *html.Node.
- html.ParseFragment — парсит фрагмент HTML в контексте узла (полезно для вставки в существующее дерево).
- html.Tokenizer — низкоуровневый парсер, полезен для потоковой обработки.
- html.Node — тип узла, описывающий элементы, текст, комментарии и т.д.
Пример использования html.Parse для извлечения всех ссылок со страницы сохранил в исходном виде (ниже). Код демонстрирует обход дерева и сбор значений атрибутов href.
import (
"fmt"
"golang.org/x/net/html"
"net/http"
)
func main() {
// Send an HTTP GET request to the example.com web page
resp, err := http.Get("https://www.example.com")
if err != nil {
fmt.Println("Error:", err)
return
}
defer resp.Body.Close()
// Use the html package to parse the response body from the request
doc, err := html.Parse(resp.Body)
if err != nil {
fmt.Println("Error:", err)
return
}
// Find and print all links on the web page
var links []string
var link func(*html.Node)
link = func(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key == "href" {
// adds a new link entry when the attribute matches
links = append(links, a.Val)
}
}
}
// traverses the HTML of the webpage from the first child node
for c := n.FirstChild; c != nil; c = c.NextSibling {
link(c)
}
}
link(doc)
// loops through the links slice
for _, l := range links {
fmt.Println("Link:", l)
}
} Объяснение алгоритма:
- Выполняем HTTP‑запрос и получаем тело ответа (io.Reader).
- Передаём resp.Body в html.Parse — получаем корневой узел дерева.
- Рекурсивно обходим дерево, ищем элементы и собираем значения атрибута href.
- Собранные ссылки записываем в срез и затем выводим.
Пояснение терминов:
- Узел (Node) — базовая единица дерева HTML (элемент, текст, комментарий).
- Элемент (ElementNode) — узел с тегом (например,