【问题标题】:Parsing HTML with Go用 Go 解析 HTML
【发布时间】:2017-09-20 01:04:04
【问题描述】:

我正在尝试使用 Go 构建一个网络爬虫,我对这门语言相当陌生,我不确定在使用 html 解析器时我做错了什么。我正在尝试解析 html 以查找锚标记,但我一直在获取 html.TokenTypeEnd 。

package main

import (
    "fmt"
    "golang.org/x/net/html"
    "io/ioutil"
    "net/http"
)

func GetHtml(url string) (text string, resp *http.Response, err error) {
    var bytes []byte
    if url == "https://www.coastal.edu/scs/employee" {
        resp, err = http.Get(url)
        if err != nil {
            fmt.Println("There seems to ben an error with the Employee Console.")
        }
        bytes, err = ioutil.ReadAll(resp.Body)
        if err != nil {
            fmt.Println("Cannot read byte response from Employee Console.")
        }
        text = string(bytes)
    } else {
        fmt.Println("Issue with finding URL. Looking for: " + url)
    }

    return text, resp, err
}

func main() {
    htmlSrc, response, err := GetHtml("https://www.coastal.edu/scs/employee")
    if err != nil {
        fmt.Println("Cannot read HTML source code.")
    }
    _ = htmlSrc
    htmlTokens := html.NewTokenizer(response.Body)
    i := 0
    for i < 1 {

        tt := htmlTokens.Next()
        fmt.Printf("%T", tt)
        switch tt {

        case html.ErrorToken:
            fmt.Println("End")
            i++

        case html.TextToken:
            fmt.Println(tt)

        case html.StartTagToken:
            t := htmlTokens.Token()

            isAnchor := t.Data == "a"
            if isAnchor {
                fmt.Println("We found an anchor!")
            }

        }

    }

每当我打印时,我都会收到 html.TokenTypeEnd fmt.Printf("%T", tt)

【问题讨论】:

  • 您只能阅读response.Body 一次。它已经在您的GetHtml 函数中用完。你为什么要读整个 html 字符串,然后还是扔掉它?
  • 我习惯了 Python,所以我认为我必须阅读 html 并将其作为字符串返回。这是我编写的第一个 Go 程序,我对这门语言非常陌生,所以我正在努力理解它。
  • 当您遇到io.Readers 或io.ReadClosers 时,您希望尽可能避免将其全部读入变量中。这些类型有一些优化,如果使用得当,可以提高效率。这就是为什么html.NewTokenizer 排在第一位的原因。只是一些建议。如果您确定回复不是很大,通常可以使用ioutil.ReadAll
  • 谢谢!我一定会牢记您的建议,以便继续进行未来的项目。所以 io.Reader 更像是一个缓冲区?
  • 是的。根据底层来源,它实际上可能是从例如读取网络套接字,或其他一些当时实际上不在内存中的源。像html.NewTokenizer 这样的东西可以通过读取足够的数据来获得完整的令牌来利用这一点,而不必在内存中拥有完整的输入。 Go 在幕后发生了很多很酷的事情。当您了解更多信息或想了解实际情况时,请阅读 godocs 并随时深入研究源代码(直接从文档链接)。 Go 是用 Go 编写的 :)

标签: html go web-scraping html-parsing


【解决方案1】:

应用程序读取到 GetHtml 中正文的末尾。分词器返回html.TokenTypeEnd,因为在正文上读取返回EOF。

使用此代码:

htmlTokens := html.NewTokenizer(strings.NewReader(htmlSrc))

创建分词器。

另外,关闭GetHtml 中的响应正文以防止连接泄漏。

代码可以简化为:

    response, err := http.Get("https://www.coastal.edu/scs/employee")
    if err != nil {
        log.Fatal(err)
    }
    defer response.Body.Close()
    htmlTokens := html.NewTokenizer(response.Body)
loop:
    for {
        tt := htmlTokens.Next()
        fmt.Printf("%T", tt)
        switch tt {
        case html.ErrorToken:
            fmt.Println("End")
            break loop
        case html.TextToken:
            fmt.Println(tt)
        case html.StartTagToken:
            t := htmlTokens.Token()
            isAnchor := t.Data == "a"
            if isAnchor {
                fmt.Println("We found an anchor!")
            }
        }
    }

【讨论】:

  • 谢谢,这解决了问题,我什至不知道连接泄漏。显然我对 Go 很陌生
  • 这实际上正是我所做的,哈哈。不过谢谢你,很好的建议!
猜你喜欢
  • 2022-06-16
  • 2012-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-08
  • 1970-01-01
  • 2018-12-18
相关资源
最近更新 更多