package main
import (
"fmt"
"log"
"net/http"
"os"
"golang.org/x/net/html"
)
var (
str string = "https://docs.hacknode.org/gopl-zh/"
)
//CreatFile is a func ti make infomation in file
func CreatFile(bt []byte) {
f, err := os.OpenFile("F:/MyGo/src/practice/url.txt", os.O_CREATE|os.O_APPEND, 0666)
if err != nil {
log.Fatal(err)
}
defer f.Close()
res, err := f.Write([]byte(bt))
if err != nil {
log.Fatal(err)
}
if res == 0 {
fmt.Println("no one")
}
}
//GetURLInfomation is a func get URL infomation
func GetURLInfomation(URL string) []string {
resp, err := http.Get(URL)
if err != nil {
log.Fatal(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {//判断状态
log.Fatal("Can't connect")
}
//开始节点处理
doc, err := html.Parse(resp.Body)
if err != nil {
log.Fatal(err)
}
var links []string
ForOneNode := func(n *html.Node) {
//匿名函数,单次节点处理,为什么使用匿名函数,在下面讲解
if n.Type == html.ElementNode && n.Data == "a" {//判断是否为节点,节点为<a>标签
for _, a := range n.Attr {//遍历标签属性
if a.Key != "href" {
continue
}
link, err := resp.Request.URL.Parse(a.Val)//解析出URL地址,即属性内容
if err != nil {
log.Fatal(err)
}
links = append(links, link.String())
}
}
}
ForEachNode(doc, ForOneNode, nil)//遍历开始
return links
}
//ForEachNode is 广度优先遍历
func ForEachNode(n *html.Node, pre, post func(n *html.Node)) {
if pre != nil {
pre(n)
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
ForEachNode(c, pre, post)
}
if post != nil {
post(n)
}
}
func main() {
bt := GetURLInfomation("https://docs.hacknode.org/gopl-zh/")
for _, t := range bt {
t += "\n"
CreatFile([]byte(t))
}
//CreatFile(bt)
}
注:为什么使用匿名函数
1.在函数体内,需要将取到的值进行保留,如果使用return函数返回,函数内还要使用到*http.respone类型的变量,在递归调用的时候,参数会很繁杂
2.如果使用了上述情况,在广度优先遍历的时候,内部使用递归调用,在接收返回值的时候,会很麻烦,每一次都调用一个函数,每个函数又都返回一个值,暂时没想到怎么接收,如果有人看到这篇文章,能给一些建议,本人万分感谢
有疑问加站长微信联系(非本文作者)