📚 原创系列: “Go语言爬虫系列”
🔄 转载说明: 本文最初发布于"Gopher部落"微信公众号,经原作者授权转载。
🔗 关注原创: 欢迎扫描文末二维码,关注"Gopher部落"微信公众号获取第一手Go技术文章。
📑 Go语言爬虫系列导航
🚀 Go爬虫系列:共12篇本文是【Go语言爬虫系列】的第2篇,点击下方链接查看更多文章
- 爬虫入门与Colly框架基础
- HTML解析与Goquery技术详解👈 当前位置
- Colly高级特性与并发控制
- 爬虫架构设计与实现(即将发布)
- 反爬虫策略应对技术(即将发布)
- 模拟登录与会话维持(即将发布)
- 动态网页爬取技术(即将发布)
- 分布式爬虫设计与实现(即将发布)
- 数据存储与处理(即将发布)
- 爬虫性能优化技术(即将发布)
- 爬虫安全与合规性(即将发布)
- 综合项目实战:新闻聚合系统(即将发布)
📖 文章导读
在上一篇文章中,我们学习了爬虫的基本概念和Colly框架的基础用法。本文作为系列的第二篇,将深入HTML解析领域,重点介绍:
- HTML文档结构与DOM树的基本概念
- Goquery库的安装与核心功能
- CSS选择器语法与高级用法
- DOM元素遍历与精准选择技术
- 结构化数据提取与表格处理实战
掌握这些技术后,您将能够从任何HTML页面中精确提取所需的数据,无论它的结构多么复杂。
一、HTML文档结构与DOM基础
1.1 理解HTML与DOM
在深入学习HTML解析技术前,我们需要先理解HTML文档的结构和DOM(文档对象模型)的概念:
**HTML(超文本标记语言)**是网页的标准结构化语言,由一系列元素(elements)组成,这些元素通过标签(tags)来定义。例如:
<!DOCTYPE html>
<html>
<head>
<title>页面标题</title>
</head>
<body>
<div id="main">
<h1 class="title">标题文本</h1>
<p>段落内容</p>
<ul>
<li>列表项1</li>
<li>列表项2</li>
</ul>
</div>
</body>
</html>
**DOM(Document Object Model,文档对象模型)**是HTML文档的编程接口,它将HTML文档表示为树状结构,其中每个节点都是文档的一部分(如元素、属性或文本)。这种树状结构使我们能够通过编程方式访问和操作HTML元素。
DOM树结构示例:
上面的HTML代码在DOM中的表示形式是一棵树,html元素是根节点,head和body是其子节点,依此类推。每个元素可以有属性(如id、class)、内容和子元素。
1.2 HTML解析的挑战
在爬虫开发中,HTML解析面临以下常见挑战:
- 结构多变性:不同网站的HTML结构差异很大,甚至同一网站的不同页面也可能有不同结构
- 非标准HTML:实际网页中常见格式错误或不规范的HTML代码
- 动态内容:通过JavaScript生成的内容在源代码中不可见
- 大量冗余信息:网页中包含大量与目标数据无关的内容
- 结构变化:网站更新可能导致HTML结构改变,使原有解析逻辑失效
为了应对这些挑战,我们需要强大而灵活的HTML解析工具。在Go语言中,Goquery库提供了理想的解决方案。
二、Goquery库入门
2.1 Goquery简介
Goquery是Go语言中最流行的HTML解析库,由Martin Angers创建,深受jQuery启发。它提供了类似jQuery的语法和API,使HTML元素选择和操作变得简单直观。
Goquery的主要特点:
- 熟悉的语法:对熟悉jQuery的开发者友好
- 强大的选择器:支持几乎所有CSS选择器
- 链式操作:支持函数链式调用
- DOM遍历:提供丰富的DOM导航方法
- 高性能:基于高效的
net/html标准库
2.2 安装Goquery
使用Go Modules安装Goquery:
# 初始化模块(如果尚未初始化)
go mod init myproject
# 安装Goquery
go get github.com/PuerkitoBio/goquery
2.3 基本用法示例
下面是一个简单的Goquery使用示例:
package main
import (
"fmt"
"log"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
func main() {
// 发起HTTP请求获取网页内容
res, err := http.Get("https://news.example.com")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("状态码错误: %d %s", res.StatusCode, res.Status)
}
// 加载HTML文档
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
// 使用CSS选择器查找元素
doc.Find("article .title").Each(func(i int, s *goquery.Selection) {
// 获取文本内容
title := s.Text()
// 去除多余空白
title = strings.TrimSpace(title)
fmt.Printf("文章标题 %d: %s\n", i+1, title)
// 获取链接地址
href, exists := s.Parent().Attr("href")
if exists {
fmt.Printf("链接地址: %s\n", href)
}
})
}
这个例子展示了Goquery的基本工作流程:
- 获取HTML内容
- 创建Goquery文档对象
- 使用选择器查找元素
- 处理选中的元素(获取文本、属性等)
2.4 与Colly框架集成
如果您正在使用Colly框架(如上一篇文章所介绍的),可以很容易地集成Goquery:
c := colly.NewCollector()
c.OnHTML("article", func(e *colly.HTMLElement) {
// e.DOM是一个goquery.Selection对象
e.DOM.Find(".title").Each(func(i int, s *goquery.Selection) {
title := s.Text()
fmt.Printf("文章标题: %s\n", title)
})
})
c.Visit("https://example.com")
在Colly中,HTMLElement结构体包含一个DOM字段,它是一个goquery.Selection对象,提供了对当前元素的所有Goquery功能的访问。
三、CSS选择器详解
3.1 基本选择器
CSS选择器是从HTML文档中选择元素的模式。Goquery支持大多数CSS3选择器。以下是最常用的基本选择器:
| 选择器 | 示例 | 描述 |
|---|---|---|
| 元素选择器 | p |
选择所有<p>元素 |
| ID选择器 | #main |
选择id为"main"的元素 |
| 类选择器 | .title |
选择所有class包含"title"的元素 |
| 组合选择器 | div, p |
选择所有<div>元素和所有<p>元素 |
| 后代选择器 | div p |
选择<div>内的所有<p>元素 |
| 子元素选择器 | div > p |
选择<div>的直接子元素<p> |
| 相邻兄弟选择器 | h1 + p |
选择紧跟在<h1>后的<p>元素 |
| 一般兄弟选择器 | h1 ~ p |
选择<h1>后的所有同级<p>元素 |
3.2 属性选择器
属性选择器允许根据元素的属性来选择元素:
| 选择器 | 示例 | 描述 |
|---|---|---|
[attr] |
[href] |
选择有href属性的元素 |
[attr=value] |
[type="text"] |
选择type="text"的元素 |
[attr^=value] |
[href^="https"] |
选择href以"https"开头的元素 |
[attr$=value] |
[href$=".pdf"] |
选择href以".pdf"结尾的元素 |
[attr*=value] |
[href*="example"] |
选择href包含"example"的元素 |
3.3 伪类选择器
Goquery支持部分CSS3伪类选择器:
| 选择器 | 示例 | 描述 |
|---|---|---|
:first-child |
li:first-child |
选择作为第一个子元素的每个<li>元素 |
:last-child |
li:last-child |
选择作为最后一个子元素的每个<li>元素 |
:nth-child(n) |
tr:nth-child(2n) |
选择作为第2n个子元素的每个<tr>元素(偶数行) |
:empty |
div:empty |
选择没有子元素的<div>元素 |
:not(selector) |
div:not(.ignore) |
选择不包含class="ignore"的所有<div>元素 |
3.4 选择器组合与优先级
复杂的选择需求通常需要组合多种选择器:
// 选择具有特定class的div中的第一个段落
doc.Find("div.content > p:first-child")

最低0.47元/天 解锁文章
1万+

被折叠的 条评论
为什么被折叠?



