Golang中x/net/html的Token.Data与标准字符串无法比较的问题

Golang中x/net/html的Token.Data与标准字符串无法比较的问题 最近我在处理一些需要搜索HTML内容的工作时,遇到了一个奇怪的问题。我是从其他语言转到Go的新手,所以如果我遗漏了什么细节,请见谅。

x/net/html.TokenData结构成员是一个字符串,但我无法对它进行任何类型的字符串比较?

最小可工作示例:

Go Playground 版本

package main

import (
   "fmt"
   "io"
   "log"
   "reflect"
   "strings"

   "golang.org/x/net/html"
)

func main() {

   dat := `<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><apm_do_not_touch><body onload="document.forms[0].submit()"><noscript> <p>    <strong> Your browser does not support JavaScript, press the Continue button once to proceed. </strong></p> </noscript> <form action="https://website.com" method="post"> <input type="hidden" name="SomeName" value="SomeValue"/><noscript> <div>  <input type="submit" value="DoStuff"/></div> </noscript> </form></body></apm_do_not_touch></html>`

   reader := strings.NewReader(dat)
   tokenizer := html.NewTokenizer(reader)

   //主要来自 https://drstearns.github.io/tutorials/tokenizing/
   for {
   	//获取下一个令牌类型
   	tokenType := tokenizer.Next()

   	//如果是错误令牌,我们要么到达了
   	//文件末尾,要么HTML格式错误
   	if tokenType == html.ErrorToken {
   		err := tokenizer.Err()
   		if err == io.EOF {
   			//文件结束,跳出循环
   			break
   		}
   		//否则,令牌化过程中出现错误,
   		//这可能意味着HTML格式错误。
   		//由于这是一个简单的命令行工具,
   		//我们可以直接使用log.Fatalf()来报告错误
   		//并以非零状态码退出进程
   		log.Fatalf("error tokenizing HTML: %v", tokenizer.Err())
   	}

   	//根据令牌类型处理令牌...
   	//input是一个自闭合令牌
   	if tokenType == html.SelfClosingTagToken {

   		fmt.Println(tokenizer.Token().Data)
   		fmt.Println("input")

   		data := tokenizer.Token().Data

   		//这些似乎匹配
   		fmt.Println("t1:", reflect.TypeOf(tokenizer.Token().Data))
   		fmt.Println("t2:", reflect.TypeOf("input"))

   		//这些似乎匹配
   		fmt.Println("len1:", len(tokenizer.Token().Data))
   		fmt.Println("len2:", len("input"))
   		fmt.Println("len3:", len(data))

   		fmt.Println("compare1:", "input" == tokenizer.Token().Data)
   		fmt.Println("compare2:", strings.Compare(tokenizer.Token().Data, "input") == 0)

   		fmt.Println("compare3:", "" == tokenizer.Token().Data)
   		fmt.Println("compare4:", strings.Compare(tokenizer.Token().Data, "") == 0)

   		fmt.Println("compare5:", "input" == data)
   		fmt.Println("compare6:", strings.Compare(data, "input") == 0)

   		if tokenizer.Token().Data == "input" {
   			fmt.Println("我们从未到达这里")
   		}
   	}
   }
}

根据文档Data应该是一个字符串(reflect.TypeOf语句似乎也显示了这一点),但它不显示任何长度,而且我无法检查与它的相等性?有人知道这是怎么回事吗?我假设如果文档有误,而我实际上得到的是一个指针或引用之类的,它应该会在TypeOf检查中显示出来,但所有结果都只显示string


更多关于Golang中x/net/html的Token.Data与标准字符串无法比较的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你只能在每个循环中调用一次 Token()。第一次调用 tokenizer.Token().Data 返回 "input",但第二次调用 tokenizer.Token() 返回一个数据为空的 Token,因此所有后续检查都会失败。你应该改用 data 变量,而不是持续调用 tokenizer.Token()

func main() {
    fmt.Println("hello world")
}

更多关于Golang中x/net/html的Token.Data与标准字符串无法比较的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


好的,我现在明白了。

以下是修正后的完整代码块:

if tokenType == html.SelfClosingTagToken {
			data := tokenizer.Token().Data

			//fmt.Println(tokenizer.Token().Data)
			fmt.Println("input")
			fmt.Println(data)

			fmt.Println("len2:", len("input"))
			fmt.Println("len3:", len(data))

			fmt.Println("compare5:", "input" == data)
			fmt.Println("compare6:", strings.Compare(data, "input") == 0)

			if data == "input" {
				fmt.Println("Now we reach this!")
			}

输出:

input
input
len2: 5
len3: 5
compare5: true
compare6: true
Now we reach this!

在Go语言中,x/net/html包的Token.Data字段确实是string类型。你遇到的问题很可能是由于HTML解析器返回的令牌数据包含了额外的空白字符或不可见字符,导致字符串比较失败。

以下是修正后的代码示例,展示了如何正确处理这种情况:

package main

import (
    "fmt"
    "io"
    "log"
    "strings"
    "unicode"

    "golang.org/x/net/html"
)

func main() {
    dat := `<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><apm_do_not_touch><body onload="document.forms[0].submit()"><noscript> <p>    <strong> Your browser does not support JavaScript, press the Continue button once to proceed. </strong></p> </noscript> <form action="https://website.com" method="post"> <input type="hidden" name="SomeName" value="SomeValue"/><noscript> <div>  <input type="submit" value="DoStuff"/></div> </noscript> </form></body></apm_do_not_touch></html>`

    reader := strings.NewReader(dat)
    tokenizer := html.NewTokenizer(reader)

    for {
        tokenType := tokenizer.Next()
        
        if tokenType == html.ErrorToken {
            err := tokenizer.Err()
            if err == io.EOF {
                break
            }
            log.Fatalf("error tokenizing HTML: %v", tokenizer.Err())
        }

        if tokenType == html.SelfClosingTagToken {
            token := tokenizer.Token()
            data := token.Data
            
            // 方法1: 使用strings.TrimSpace去除空白字符
            trimmedData := strings.TrimSpace(data)
            fmt.Printf("原始数据: '%s' (长度: %d)\n", data, len(data))
            fmt.Printf("修剪后: '%s' (长度: %d)\n", trimmedData, len(trimmedData))
            
            // 方法2: 使用strings.TrimFunc去除空白字符
            cleanedData := strings.TrimFunc(data, func(r rune) bool {
                return unicode.IsSpace(r)
            })
            
            // 方法3: 直接比较(如果确定没有空白字符)
            if trimmedData == "input" {
                fmt.Println("找到input标签 (使用TrimSpace)")
            }
            
            if cleanedData == "input" {
                fmt.Println("找到input标签 (使用TrimFunc)")
            }
            
            // 方法4: 使用strings.EqualFold进行不区分大小写的比较
            if strings.EqualFold(trimmedData, "input") {
                fmt.Println("找到input标签 (不区分大小写)")
            }
            
            // 调试输出:显示每个字符的ASCII码
            fmt.Print("字符编码: ")
            for _, ch := range data {
                fmt.Printf("%d ", ch)
            }
            fmt.Println()
        }
    }
}

关键点说明:

  1. 空白字符问题:HTML解析器可能返回包含换行符、制表符或其他空白字符的字符串。使用strings.TrimSpace()可以去除这些字符。

  2. 字符编码检查:通过遍历字符串并打印每个字符的ASCII码,可以查看是否有不可见字符。

  3. 字符串比较方法

    • strings.TrimSpace():去除首尾空白字符
    • strings.TrimFunc():自定义去除函数
    • strings.EqualFold():不区分大小写的比较
  4. 调试技巧:打印字符串长度和原始字符编码有助于识别问题。

运行这个修改后的代码,你会看到Token.Data确实包含字符串"input",但可能带有额外的空白字符。使用适当的字符串清理方法后,比较操作就能正常工作了。

回到顶部