有两个不错的库:
https://github.com/PuerkitoBio/goquery
一个是
http://code.google.com/p/go.net/html
html是html的解析器,把html文本解析出来,goquery基于html包,在此基础上结合cascadia 包(一个css选择器工具),实现类似于jquery的功能,操作html非常方便。
使用goquery来查找,选择相应的html节点,但如果要对选择的节点进行修改,删除操作,还需要深入使用html包。
html包把html文本解析为一个树,这个树有很多Node组成,操作的核心就在于对Node的操作。
用几个例子来说明一下吧:
doc, err := goquery.NewDocument("http://sports.sina.com.cn")
生成一个goquery的doc。
goquery用的最多的是Find函数,类似于jquery的$(),可以选择dom结构。
例1:
dhead := doc.Find("head") dcharset := dhead.Find("meta[http-equiv]") charset, _ := dcharset.Attr("content")
这个例子用来找出页面的charset。
例2:
logo := doc.Find("#retina_logo")
这个是根据html中的id来选择dom
例3:
bread := doc.Find("div.blkBreadcrumbLink")
选择doc中class为blkBreadcrumbLink的div
例4:
var faceImg string var innerImg = []string{} dom_body.Find("div.img_wrapper").Each(func(i int, s *goquery.Selection) { imgpath, exists := s.Find("img").Attr("src") if !exists { return } if i == 0 { faceImg = imgpath } innerImg = append(innerImg, imgpath) })
找出所有class为img_wrapper的div,然后在每个div下搜索img,获取img的src
例5:
dom_node := doc.Find("[bosszone='ztTopic']").Find("a")
这个是根据属性/值来查找相应的元素
如果要对html进行编辑操作,需要使用html.Node,这里提供一个清洗div的代码,使用了递归:
func clear_dom(pn *html.Node, isgb2312 bool) error { var err error for nd := pn.FirstChild; nd != nil; { switch nd.Type { case html.ElementNode: tn := strings.ToLower(nd.Data) //fmt.Printf("element node: %s\n", tn) if tn == "script" || tn == "style" { // delete the element tmp := nd nd = tmp.NextSibling pn.RemoveChild(tmp) } else if tn == "a" { tmp := nd nd = nd.NextSibling if err = convert_dom(tmp, isgb2312); err != nil { return err } } else if tn == "span" { tmp := nd nd = nd.NextSibling clear_dom(tmp, isgb2312) } else { tmp := nd nd = nd.NextSibling if err = convert_dom(tmp, isgb2312); err != nil { return err } } case html.CommentNode: tmp := nd nd = tmp.NextSibling pn.RemoveChild(tmp) case html.TextNode: tmp := nd nd = nd.NextSibling if err = convert_dom(tmp, isgb2312); err != nil { return err } default: nd = nd.NextSibling } } return nil }
其中conver_dom是对node节点的text进行转码操作,如果不需要,可以忽略。
func Nodehtml(n *html.Node) string { var buf = bytes.NewBuffer([]byte{}) html.Render(buf, n) return buf.String() } func Nodetext(node *html.Node) string { if node.Type == html.TextNode { // Keep newlines and spaces, like jQuery return node.Data } else if node.FirstChild != nil { var buf bytes.Buffer for c := node.FirstChild; c != nil; c = c.NextSibling { buf.WriteString(Nodetext(c)) } return buf.String() } return "" }
上面的两个函数,分别获取节点的html代码和text代码。html代码和text代码的区别是,html代码是原封不动的html代码,text代码仅仅显示html代码的内容,例如一段html: 例子,它的text代码是”例子”