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) }