golang使用XPath表达式从HTML文档提取数据插件库htmlquery的使用

Golang使用XPath表达式从HTML文档提取数据插件库htmlquery的使用

概述

htmlquery是一个用于HTML的XPath查询包,允许您通过XPath表达式从HTML文档中提取数据或评估内容。

htmlquery内置了基于LRU的查询对象缓存功能,该功能会缓存最近使用的XPath查询字符串。启用查询缓存可以避免每次查询时重新编译XPath表达式。

安装

go get github.com/antchfx/htmlquery

快速入门

基本查询示例

// 查询所有A元素
nodes, err := htmlquery.QueryAll(doc, "//a")
if err != nil {
    panic(`not a valid XPath expression.`)
}

// 查询所有带有href属性的A元素
list := htmlquery.Find(doc, "//a[@href]")

// 查询第三个A元素
a := htmlquery.FindOne(doc, "//a[3]")

加载HTML文档

// 从URL加载HTML文档
doc, err := htmlquery.LoadURL("http://example.com/")

// 从文件加载HTML文档
filePath := "/home/user/sample.html"
doc, err := htmlquery.LoadDoc(filePath)

// 从字符串加载HTML文档
s := `<html>....</html>`
doc, err := htmlquery.Parse(strings.NewReader(s))

提取属性值

// 查找所有A元素的href属性
list := htmlquery.Find(doc, "//a/@href")
for _, n := range list{
    fmt.Println(htmlquery.InnerText(n)) // 输出@href值
}

// 查找A元素下的img子元素并打印src属性
a := htmlquery.FindOne(doc, "//a")
img := htmlquery.FindOne(a, "//img")
fmt.Println(htmlquery.SelectAttr(img, "src")) // 输出@src值

计算元素数量

expr, _ := xpath.Compile("count(//img)")
v := expr.Evaluate(htmlquery.CreateXPathNavigator(doc)).(float64)
fmt.Printf("total count is %f", v)

完整示例

func main() {
    // 从URL加载HTML文档
    doc, err := htmlquery.LoadURL("https://www.bing.com/search?q=golang")
    if err != nil {
        panic(err)
    }
    
    // 查找所有新闻项
    list, err := htmlquery.QueryAll(doc, "//ol/li")
    if err != nil {
        panic(err)
    }
    
    // 遍历结果并提取链接和文本
    for i, n := range list {
        a := htmlquery.FindOne(n, "//a")
        if a != nil {
            fmt.Printf("%d %s(%s)\n", i, htmlquery.InnerText(a), htmlquery.SelectAttr(a, "href"))
        }
    }
}

常见问题

Find() vs QueryAll()有什么区别?

FindQueryAll都做相同的事情,搜索所有匹配的HTML节点。如果给出错误的XPath查询,Find会引发panic,而QueryAll会返回错误。

可以保存查询表达式对象以供下次查询使用吗?

是的,可以使用QuerySelectorQuerySelectorAll方法,它们接受查询表达式对象。缓存查询表达式对象(或重用)可以避免重新编译XPath查询表达式,提高查询性能。

如何禁用缓存?

htmlquery.DisableSelectorCache = true

缓存性能对比

goos: windows
goarch: amd64
pkg: github.com/antchfx/htmlquery
BenchmarkSelectorCache-4                20000000                55.2 ns/op
BenchmarkDisableSelectorCache-4           500000              3162 ns/op

支持的XPath语法

htmlquery支持XPath 1.0/2.0语法。


更多关于golang使用XPath表达式从HTML文档提取数据插件库htmlquery的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang使用XPath表达式从HTML文档提取数据插件库htmlquery的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用htmlquery库在Golang中提取HTML数据

htmlquery是一个基于XPath的HTML解析库,它允许你使用XPath表达式从HTML文档中提取数据。以下是详细的使用指南和示例代码。

安装htmlquery

首先需要安装htmlquery库:

go get github.com/antchfx/htmlquery

基本使用方法

1. 加载HTML文档

package main

import (
	"fmt"
	"log"
	"strings"

	"github.com/antchfx/htmlquery"
	"golang.org/x/net/html"
)

func main() {
	// 从字符串加载HTML
	htmlStr := `
	<html>
		<head>
			<title>示例页面</title>
		</head>
		<body>
			<div class="content">
				<h1>标题</h1>
				<p>段落1</p>
				<p>段落2</p>
				<ul>
					<li>项目1</li>
					<li>项目2</li>
				</ul>
			</div>
		</body>
	</html>`

	doc, err := htmlquery.Parse(strings.NewReader(htmlStr))
	if err != nil {
		log.Fatal(err)
	}

	// 从URL加载HTML
	// doc, err := htmlquery.LoadURL("http://example.com")
	// if err != nil {
	//     log.Fatal(err)
	// }

	// 从文件加载HTML
	// doc, err := htmlquery.LoadDoc("example.html")
	// if err != nil {
	//     log.Fatal(err)
	// }
}

2. 使用XPath查询

// 查询单个节点
title := htmlquery.FindOne(doc, "//title/text()")
fmt.Println(htmlquery.InnerText(title)) // 输出: 示例页面

// 查询多个节点
paragraphs := htmlquery.Find(doc, "//p")
for _, p := range paragraphs {
	fmt.Println(htmlquery.InnerText(p))
}
// 输出:
// 段落1
// 段落2

3. 常用XPath表达式示例

// 获取所有li元素的文本
items := htmlquery.Find(doc, "//ul/li")
for i, item := range items {
	fmt.Printf("项目%d: %s\n", i+1, htmlquery.InnerText(item))
}

// 获取特定class的元素
contentDiv := htmlquery.FindOne(doc, "//div[@class='content']")
fmt.Println(htmlquery.OutputHTML(contentDiv, true))

// 获取属性值
h1 := htmlquery.FindOne(doc, "//h1")
fmt.Println(htmlquery.SelectAttr(h1, "class")) // 获取class属性

高级用法

1. 处理相对路径

contentDiv := htmlquery.FindOne(doc, "//div[@class='content']")
if contentDiv != nil {
	// 在contentDiv下相对查找
	h1 := htmlquery.FindOne(contentDiv, "./h1")
	fmt.Println(htmlquery.InnerText(h1)) // 输出: 标题
}

2. 使用谓词

// 获取第一个p元素
firstP := htmlquery.FindOne(doc, "//p[1]")
fmt.Println(htmlquery.InnerText(firstP)) // 输出: 段落1

// 获取最后一个li元素
lastLi := htmlquery.FindOne(doc, "//li[last()]")
fmt.Println(htmlquery.InnerText(lastLi)) // 输出: 项目2

3. 提取链接

htmlStr := `
<html>
	<body>
		<a href="/page1">链接1</a>
		<a href="/page2">链接2</a>
	</body>
</html>`

doc, _ := htmlquery.Parse(strings.NewReader(htmlStr))
links := htmlquery.Find(doc, "//a/@href")
for _, link := range links {
	fmt.Println(htmlquery.InnerText(link)) // 输出: /page1 和 /page2
}

实际应用示例

示例1:提取新闻标题和链接

func scrapeNews() {
	doc, err := htmlquery.LoadURL("https://news.example.com")
	if err != nil {
		log.Fatal(err)
	}

	// 假设新闻条目在class为news-item的div中,标题在h2,链接在a标签
	nodes := htmlquery.Find(doc, "//div[@class='news-item']")
	for _, node := range nodes {
		title := htmlquery.FindOne(node, "./h2/a/text()")
		link := htmlquery.FindOne(node, "./h2/a/@href")
		
		if title != nil && link != nil {
			fmt.Printf("标题: %s\n链接: %s\n\n", 
				htmlquery.InnerText(title),
				htmlquery.InnerText(link))
		}
	}
}

示例2:提取表格数据

func scrapeTable() {
	htmlStr := `
	<table>
		<tr><th>姓名</th><th>年龄</th></tr>
		<tr><td>张三</td><td>25</td></tr>
		<tr><td>李四</td><td>30</td></tr>
	</table>`

	doc, _ := htmlquery.Parse(strings.NewReader(htmlStr))
	rows := htmlquery.Find(doc, "//tr[position()>1]") // 跳过表头
	for _, row := range rows {
		name := htmlquery.FindOne(row, "./td[1]")
		age := htmlquery.FindOne(row, "./td[2]")
		fmt.Printf("%s: %s岁\n", 
			htmlquery.InnerText(name),
			htmlquery.InnerText(age))
	}
}

注意事项

  1. htmlquery基于标准XPath 1.0,不支持XPath 2.0及更高版本的功能
  2. 对于大型HTML文档,考虑使用htmlquery.FindEach来减少内存使用
  3. 处理动态加载的内容时,可能需要结合其他工具如chromedp
  4. XPath表达式对大小写敏感

htmlquery提供了一种在Golang中处理HTML文档的便捷方式,特别适合需要精确提取特定数据的场景。通过合理使用XPath表达式,可以高效地从复杂HTML结构中提取所需信息。

回到顶部