使用 Golang 解析 HTML

如何在 Golang 中解析 HTML 指南

在本文中,我将向您展示如何使用内置的 net/html 软件包在 Go 中解析 HTML。我们将介绍基础知识,还会向你介绍一些更适合特定任务的其他库。最后,你将掌握解析 HTML、提取结构化数据的技能,并能像专业人士一样使用 Go 处理网络刮擦。让我们开始吧

先决条件

在深入研究 Go 中的 HTML 解析之前,请确保您已经掌握了以下内容:

  1. 已安装 Go:如果您尚未安装 Go,请访问 Go官方网站 并按照安装说明进行操作。
  2. 对 Go 语法的基本了解:如果你是 Go 的新手,可以考虑学习一下它的语法,包括如何使用包、函数和错误处理。
  3. HTML 知识:了解 HTML 结构至关重要,因为您需要确定要提取的标记和属性。
  4. Go 模块:如果要启动一个新项目,请运行 go mod init 以确保 Go 模块已在项目中初始化。

使用 net/html 解析 HTML

Go 标准库提供了 net/html 软件包 用于解析 HTML。它提供两种主要的解析方法:标记符 API 和节点解析 API,使其成为最好的 HTML 解析器之一。在本教程中,我们将主要关注节点解析 API,因为它提供了更高层次的抽象,更易于使用。不过,为了完整起见,我们也将简要介绍标记符 API。

用 Go 进行解析的替代方案

目前最好的替代方法就是数据集。请访问我的文章 最佳数据集网站.如果您赶时间,以下是 5 大数据集提供商的简要介绍:

  1. Bright Data - 可定制和预建的跨行业数据集。
  2. Statista - 用于商业和研究的大量统计数据和报告。
  3. Datarade - 来自不同供应商的优质数据产品市场。
  4. AWS Data Exchange - 与 AWS 服务集成的第三方数据集。
  5. Zyte — 面向业务需求定制的网页抓取以及数据集。

我与上述任何提供商均无任何关联。

步骤 1:从网页获取 HTML

要解析 HTML,首先必须从网页中获取原始 HTML 内容。具体做法是向要抓取的网页 URL 请求 HTTP。在 Go 中,可以使用 net/http 包请求 HTTP。

下面是一个如何进行简单 HTTP GET 请求的示例:

package main
import (
"fmt"
"io"
"net/http"
)
func main() {
url := "https://example.com" // Replace with your target URL
resp, err := http.Get(url)
if err != nil {
fmt.Println("获取 URL 时出错:", err)
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println("读取响应体时出错:", err)
return
}
fmt.Println("获取到的 HTML:")
fmt.Println(string(body))
}

在这段代码中,我们使用 HTTP.Get() 发送 GET 请求并获取响应体。页面的 HTML 内容会以字符串形式打印出来。

步骤 2:使用节点解析 API 解析 HTML

现在我们有了原始的 HTML,让我们来解析它。net/html 软件包提供了一个名为 html.Parse() 的函数,它可以解析 HTML 文档并返回一棵由节点组成的树。树中的每个节点代表一个 HTML 元素、属性或一段文本。

为了解析 HTML,我们将响应体(即 nio.Reader)传递给 html.Parse()。这将返回一个根节点,您可以递归遍历该节点以提取所需的信息。

下面是一个解析 HTML 文档并打印标签名称的示例:

package main
import (
"fmt"
"net/http"
"golang.org/x/net/html"
"io"
)
func main() {
url := "https://example.com"
resp, err := http.Get(url)
if err != nil {
fmt.Println("获取 URL 时出错:", err)
return
}
defer resp.Body.Close()
doc, err := html.Parse(resp.Body)
if err != nil {
fmt.Println("解析 HTML 时出错:", err)
return
}
traverse(doc)
}
// Recursively traverse the HTML nodes and print their tag names
func traverse(n *html.Node) {
if n.Type == html.ElementNode {
fmt.Println("标签:", n.Data)
}
for c := n.FirstChild; c != nilc = c.NextSibling {
traverse(c)
}
}

这段代码将输出标签名称(例如<html>,<body>,<div>等等)。

步骤 3:从 HTML 中提取特定数据

要提取特定数据,需要检查 HTML 结构并确定包含所需信息的元素。例如,您想从一个电子商务页面中提取所有产品名称、价格和图片 URL。您需要查找特定的标记(如 <h2> 产品名称、 <span> 价格,以及 <img> 图像)并提取其内容。

下面是一个提取产品名称、价格和图片 URL 的函数示例:

package main
import (
"fmt"
"net/http"
"golang.org/x/net/html"
"strings"
"io"
)
func main() {
url := "https://example.com/ecommerce"
resp, err := http.Get(url)
if err != nil {
fmt.Println("获取 URL 时出错:", err)
return
}
defer resp.Body.Close()
doc, err := html.Parse(resp.Body)
if err != nil {
fmt.Println("解析 HTML 时出错:", err)
return
}
processProducts(doc)
}
// Recursively process the HTML nodes to extract product details
func processProducts(n *html.Node) {
if n.Type == html.ElementNode && n.Data == "li" {
// Look for product name
var name string
var price string
var imageURL string
for c := n.FirstChild; c != nilc = c.NextSibling {
switch c.Data {
case "h2":
if c.FirstChild != nil && c.FirstChild.Type == html.TextNode {
name = c.FirstChild.Data
}
case "span":
for _, a := range c.Attr {
if a.Key == "class" && strings.Contains(a.Val、 "price") {
if c.FirstChild != nil && c.FirstChild.Type == html.TextNode {
价格 = c.FirstChild.Data
}
}
}
case "img":
for _, a := range c.Attr {
if a.Key == "src" {
imageURL = a.Val
}
}
}
}
fmt.Println("产品名称:", name)
fmt.Println("价格:", price)
fmt.Println("图片 URL:", imageURL)
}
for c := n.FirstChild; c != nilc = c.NextSibling {
processProducts(c)
}
}

在本例中,processProducts 函数递归搜索 <li> 元素。每个 <li>它会寻找 <h2> (产品名称)、 <span> (价格),以及 <img> (图像 URL)。然后提取相关文本或属性并打印信息。

步骤 4:处理错误和边缘情况

在处理 HTML 时,您会遇到一些边缘情况,如缺少标记、空属性或畸形 HTML。重要的是要优雅地处理错误,并确保您的刮擦工具可以处理这些情况。

例如,从节点中提取文本时,一定要检查节点是否有子节点,以及子节点是否是文本节点。同样,在提取属性(如图片)时,也要在使用前确保属性存在。

if n.FirstChild != nil && n.FirstChild.Type == html.TextNode {
fmt.Println("文本:", n.FirstChild.Data)
}

在 Go 中解析 HTML 的替代库

虽然 net/html 是 Go 最常用的 HTML 解析包,但其他库也提供了额外的功能或替代 API,根据具体的使用情况,这些功能或 API 可能会很有用。

Goquery

Goquery 是一个流行的 Go 库,采用类似 jQuery 的语法来遍历和操作 HTML 文档。Goquery 基于 net/html 软件包,但提供了更方便的 API,允许使用 CSS 选择器查询元素。这使得提取特定数据变得更容易,而无需手动遍历 HTML 树。

下面是一个使用 Goquery 从页面中提取产品名称的示例:

package main
import (
"fmt"
"github.com/PuerkitoBio/goquery"
"net/http"
)
func main() {
url := "https://example.com/ecommerce"
res, err := http.Get(url)
if err != nil {
fmt.Println("错误:", err)
return
}
defer res.Body.Close()
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
fmt.Println("解析 HTML 时出错:", err)
return
}
doc.Find("li").Each(func(i int, s *goquery.Selection) {
name := s.Find("h2").Text()
fmt.Println("产品名称:", name)
})
}

通过 Goquery,您可以使用 CSS 选择器轻松选择元素,如 doc.Find("li h2").Text() ,从而使代码更加简洁易读。

其他图书馆

  • Go-html-transform:这个库提供了一个用于通过 CSS 选择器转换 HTML 文档的 API,类似于 Goquery,但采用了不同的方法。不过,它已不再积极维护。
  • Html2go:该工具主要用于将 HTML 文件转换为 Go 代码(结构体)。如果需要静态定义 HTML 文档结构,它将非常有用。

结论

使用 net/html 软件包在 Go 中解析 HTML 非常简单。您可以获取原始 HTML,然后使用 html.Parse() 浏览 HTML 树并获取所需数据。如果你想要更方便的方法,Goquery 等库提供了类似 jQuery 的功能,让处理 HTML 变得更简单。无论您是要构建一个快速的网络抓取工具,还是要参与一个大型的网络抓取项目,Go 的库和并发功能都使其成为高效处理 HTML 解析的可靠选择。

类似文章