Golang解析HTML,用标签提取所有内容

时间:2015-05-07 18:34:04

标签: html go

如标题所述。我需要返回html文档的body标签内的所有内容,包括任何后续的html标签等。我很想知道最好的方法是什么。我有一个使用Gokogiri软件包的工作解决方案,但是我试图远离任何依赖于C库的软件包。有没有办法用go标准库实现这一目标?或者100%的套餐?

自发布原始问题以来,我试图使用以下未产生解决方案的软件包。 (这两者似乎都没有从身体内部返回后续的孩子或嵌套标签。例如:

<!DOCTYPE html>
<html>
    <head>
        <title>
            Title of the document
        </title>
    </head>
    <body>
        body content 
        <p>more content</p>
    </body>
</html> 

将返回正文内容,忽略后续的<p>标记及其包装的文本:

  • pkg / encoding / xml /(标准库xml包)
  • golang.org/x/net/html

所有目标都是获得一个看起来像的字符串或内容:

<body>
    body content 
    <p>more content</p>
</body>

4 个答案:

答案 0 :(得分:24)

这可以通过递归查找body节点,使用html包,然后从该节点开始渲染html来解决。

package main

import (
    "bytes"
    "errors"
    "fmt"
    "golang.org/x/net/html"
    "io"
    "strings"
)

func getBody(doc *html.Node) (*html.Node, error) {
    var b *html.Node
    var f func(*html.Node)
    f = func(n *html.Node) {
        if n.Type == html.ElementNode && n.Data == "body" {
            b = n
        }
        for c := n.FirstChild; c != nil; c = c.NextSibling {
            f(c)
        }
    }
    f(doc)
    if b != nil {
        return b, nil
    }
    return nil, errors.New("Missing <body> in the node tree")
}

func renderNode(n *html.Node) string {
    var buf bytes.Buffer
    w := io.Writer(&buf)
    html.Render(w, n)
    return buf.String()
}

func main() {
    doc, _ := html.Parse(strings.NewReader(htm))
    bn, err := getBody(doc)
    if err != nil {
        return
    }
    body := renderNode(bn)
    fmt.Println(body)
}

const htm = `<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    body content
    <p>more content</p>
</body>
</html>`

答案 1 :(得分:8)

可以使用标准encoding/xml包来完成。但它有点麻烦。此示例中的一个警告是,它不会包含封闭的body标记,但它将包含所有它的子级。

package main

import (
    "bytes"
    "encoding/xml"
    "fmt"
)

type html struct {
    Body body `xml:"body"`
}
type body struct {
    Content string `xml:",innerxml"`
}

func main() {
    b := []byte(`<!DOCTYPE html>
<html>
    <head>
        <title>
            Title of the document
        </title>
    </head>
    <body>
        body content 
        <p>more content</p>
    </body>
</html>`)

    h := html{}
    err := xml.NewDecoder(bytes.NewBuffer(b)).Decode(&h)
    if err != nil {
        fmt.Println("error", err)
        return
    }

    fmt.Println(h.Body.Content)
}

可运行的例子:
http://play.golang.org/p/ZH5iKyjRQp

答案 2 :(得分:5)

由于您没有使用html包显示您尝试的源代码,我不得不猜测您在做什么,但我怀疑您使用的是tokenizer而不是解析器。这是一个使用解析器并执行您要查找的程序:

package main

import (
    "log"
    "os"
    "strings"

    "github.com/andybalholm/cascadia"
    "golang.org/x/net/html"
)

func main() {
    r := strings.NewReader(`<!DOCTYPE html>
<html>
    <head>
        <title>
            Title of the document
        </title>
    </head>
    <body>
        body content 
        <p>more content</p>
    </body>
</html>`)
    doc, err := html.Parse(r)
    if err != nil {
        log.Fatal(err)
    }

    body := cascadia.MustCompile("body").MatchFirst(doc)
    html.Render(os.Stdout, body)
}

答案 3 :(得分:1)

您也可以使用字符串完成此操作:

func main() {
    r := strings.NewReader(`
<!DOCTYPE html>
<html>
    <head>
        <title>
            Title of the document
        </title>
    </head>
    <body>
        body content
        <p>more content</p>
    </body>
</html>
`)
    str := NewSkipTillReader(r, []byte("<body>"))
    rtr := NewReadTillReader(str, []byte("</body>"))
    bs, err := ioutil.ReadAll(rtr)
    fmt.Println(string(bs), err)
}

SkipTillReaderReadTillReader的定义如下:https://play.golang.org/p/6THLhRgLOa。 (但基本上跳过,直到你看到分隔符然后读,直到你看到分隔符)

这对于不区分大小写不起作用(尽管这不会很难改变)。