用Go解析HTML

用Go解析HTML,html,go,web-scraping,html-parsing,Html,Go,Web Scraping,Html Parsing,我正在尝试使用Go构建一个web scraper,我对该语言相当陌生,我不确定在使用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

我正在尝试使用Go构建一个web scraper,我对该语言相当陌生,我不确定在使用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!")
            }

        }

    }
主程序包
进口(
“fmt”
“golang.org/x/net/html”
“io/ioutil”
“net/http”
)
func GetHtml(url字符串)(文本字符串,resp*http.Response,err error){
变量字节[]字节
如果url==”https://www.coastal.edu/scs/employee" {
resp,err=http.Get(url)
如果错误!=零{
Println(“ben觉得员工控制台有错误。”)
}
字节,err=ioutil.ReadAll(resp.Body)
如果错误!=零{
fmt.Println(“无法从员工控制台读取字节响应”)
}
text=字符串(字节)
}否则{
fmt.Println(“查找URL的问题。查找:“+URL”)
}
返回文本、响应、错误
}
func main(){
htmlSrc,响应,错误:=GetHtml('https://www.coastal.edu/scs/employee")
如果错误!=零{
Println(“无法读取HTML源代码”)
}
_=htmlSrc
htmlTokens:=html.NewTokenizer(response.Body)
i:=0
对于i<1{
tt:=htmlTokens.Next()
格式打印F(“%T”,tt)
开关tt{
case html.ErrorToken:
fmt.Println(“结束”)
我++
case html.TextToken:
fmt.Println(tt)
case html.StartTagToken:
t:=htmlTokens.Token()
isAnchor:=t.数据==“a”
如果伊桑乔{
Println(“我们找到了一个锚!”)
}
}
}
无论何时打印,我都会收到html.TokenTypeEnd
fmt.Printf(“%T”,tt)
应用程序在
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!")
            }
        }
    }

应用程序在
GetHtml
中读取到主体的末尾。标记器返回
html。标记类型end
,因为主体上的读取返回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!")
            }
        }
    }

你只能读一次
response.Body
。它在你的
GetHtml
函数中已经用光了。你为什么要读整个html字符串,然后再把它扔掉呢?我习惯了Python,所以我想我必须读html并将其作为字符串返回。这是我写的第一个Go程序,我对lan非常陌生语言,所以我试着理解它。当你遇到
io.Reader
s或
io.ReadCloser
s时,如果可以的话,你希望避免将其全部读入变量中。对于这些类型,有一些优化,如果使用得当,可以使事情更加高效。这就是为什么
html.NewTokenizer
首先会采用一种语言的原因。就是这样我的建议。如果您确信响应不会很大,那么通常完全可以使用
ioutil.ReadAll
。谢谢!我会将您的建议牢记在心,并继续进行未来的项目。因此io.Reader更像是一个缓冲区?是的。根据底层源,它实际上可能是从网络套接字或其他应用程序读取的其他源代码当时实际上不在内存中。类似于
html.NewTokenizer
的东西可以利用这一点,只需读取足够的数据即可获得完整的令牌,而无需在内存中进行完整的输入。go的幕后有很多很酷的东西。阅读godocs并自由地深入研究源代码(直接从文档链接而来)当您了解更多信息,或想知道实际情况时。Go用Go:)你只能读一次
response.Body
。它在你的
GetHtml
函数中已经用光了。你为什么要读整个html字符串,然后再把它扔掉呢?我习惯了Python,所以我想我必须读html并将其作为字符串返回。这是我写的第一个Go程序,我对lan非常陌生语言,所以我试着理解它。当你遇到
io.Reader
s或
io.ReadCloser
s时,如果可以的话,你希望避免将其全部读入变量中。对于这些类型,有一些优化,如果使用得当,可以使事情更加高效。这就是为什么
html.NewTokenizer
首先会采用一种语言的原因。就是这样我的建议。如果您确信响应不会很大,那么通常完全可以使用
ioutil.ReadAll
。谢谢!我会将您的建议牢记在心,并继续进行未来的项目。因此io.Reader更像是一个缓冲区?是的。根据底层源,它实际上可能是从网络套接字或其他应用程序读取的其他源代码当时实际上不在内存中。类似于
html.NewTokenizer
的东西可以利用这一点,只需读取足够的数据即可获得完整的令牌,而无需在内存中进行完整的输入。go的幕后有很多很酷的东西。阅读godocs并自由地深入研究源代码(直接从文档链接而来)当您了解更多信息,或想知道实际情况时。Go用Go:)谢谢你,这解决了问题,我甚至没有意识到连接泄漏。我是一个很新的人,很明显这正是我所做的。谢谢你,很好的建议!谢谢你,这解决了问题,我甚至没有意识到连接泄漏。我是一个很新的人,很明显这正是我所做的。谢谢你,grea不要忠告!