119 lines
2.6 KiB
Go
119 lines
2.6 KiB
Go
package main
|
||
|
||
import (
|
||
"fmt"
|
||
"golang.org/x/net/html"
|
||
"io"
|
||
"net/http"
|
||
"strings"
|
||
)
|
||
|
||
type Html interface {
|
||
io.ReadCloser
|
||
Getdoc(url string) (err error)
|
||
ForeachNode(n *html.Node, pre, post func(n *html.Node))
|
||
SearchNode(tag string) []*html.Node
|
||
PrintAll()
|
||
}
|
||
|
||
// 不对外暴露,一切操作均通过方法实现
|
||
// 除了Html接口中定义的方法, 还可以自己拓展方法, 例如Show方法
|
||
type htmlParser struct {
|
||
Html
|
||
str string
|
||
node *html.Node
|
||
resp *http.Response
|
||
}
|
||
|
||
// 自定义读入
|
||
func (doc *htmlParser) Read(p []byte) (n int, err error) {
|
||
if len(p) == 0 {
|
||
return 0, fmt.Errorf("error, read data is empty")
|
||
}
|
||
n = copy(p, doc.str)
|
||
if doc.node != nil {
|
||
return 0, fmt.Errorf("error, node is not empty")
|
||
}
|
||
doc.node, err = html.Parse(strings.NewReader(doc.str))
|
||
return n, err
|
||
}
|
||
|
||
// Clear 清空
|
||
func (doc *htmlParser) Clear() {
|
||
doc.node = nil
|
||
doc.resp = nil
|
||
}
|
||
|
||
// Close 关闭连接
|
||
func (doc *htmlParser) Close() error {
|
||
return doc.resp.Body.Close()
|
||
}
|
||
|
||
// Getdoc 通过url获取网页内容
|
||
func (doc *htmlParser) Getdoc(url string) (err error) {
|
||
doc.resp, err = http.Get(url)
|
||
defer doc.Close() // 关闭连接
|
||
if doc.resp.StatusCode != http.StatusOK {
|
||
err = fmt.Errorf("http get error: %s", doc.resp.Status)
|
||
return err
|
||
}
|
||
doc.node, err = html.Parse(doc.resp.Body)
|
||
if err != nil {
|
||
return fmt.Errorf("html parse error: %s", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// ForEachNode 遍历节点
|
||
func (doc *htmlParser) ForEachNode(n *html.Node, pre, post func(n *html.Node)) {
|
||
if n == nil {
|
||
return
|
||
}
|
||
if pre != nil {
|
||
pre(n)
|
||
}
|
||
doc.ForEachNode(n.FirstChild, pre, post)
|
||
doc.ForEachNode(n.NextSibling, pre, post)
|
||
if post != nil {
|
||
post(n)
|
||
}
|
||
}
|
||
|
||
// SearchNode 搜索节点
|
||
func (doc *htmlParser) SearchNode(tag string) []*html.Node {
|
||
var nodes []*html.Node
|
||
doc.ForEachNode(doc.node, func(n *html.Node) {
|
||
if n.Type == html.ElementNode && n.Data == tag {
|
||
nodes = append(nodes, n)
|
||
}
|
||
}, nil)
|
||
return nodes
|
||
}
|
||
|
||
// PrintAll 打印所有节点
|
||
func (doc *htmlParser) PrintAll() {
|
||
doc.ForEachNode(doc.node, func(n *html.Node) {
|
||
if n.Type == html.ElementNode {
|
||
fmt.Printf("%s\n", n.Data)
|
||
}
|
||
}, nil)
|
||
}
|
||
|
||
// Show 是没有定义在接口中的方法,所以只能通过结构体实例调用
|
||
func (doc *htmlParser) Show() {
|
||
fmt.Println(doc.resp)
|
||
fmt.Println()
|
||
fmt.Println(doc.node)
|
||
}
|
||
func main() {
|
||
var doc htmlParser
|
||
doc.Getdoc("https://go.dev/")
|
||
doc.Show()
|
||
// 如果使用接口类型,则不能使用Show方法
|
||
//var doc Html
|
||
//doc.Getdoc("https://go.dev/")
|
||
// 因为接口类型中没有定义Show方法,所以不能调用
|
||
//doc.Show() // error, Show undefined (type Html has no field or method Show)
|
||
|
||
}
|