我正在用colly框架解析一个网站,结果出现了一些问题。我有一个非常基本的函数getweeks()来获取和返回一些东西,但是我却得到了一个空的片段。
func getWeeks(c *colly.Collector) []string {
var wks []string
c.OnHTML("div.ltbluediv", func(div *colly.HTMLElement) {
weekName := div.DOM.Find("span").Text() // a string Week 1, Week 2 etc
wks = append(wks, weekName) // weekName has actual value is not empty
// If `wks` printed here it shows correctly how the slice gets populated on each iteration
})
return wks // returns []
}
func main() {
c := colly.NewCollector(
)
w := getWeeks(c)
fmt.Println(w) // []
c.OnRequest(func(r *colly.Request) {
r.Headers.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64)")
})
c.Visit("target url")
}发布于 2022-05-03 13:53:28
tl;dr:片头在OnHTML回调中被更新,但是您在main中打印的值是旧的片头。您应该转而使用*[]string。
首先,传递给c.OnHTML的回调实际上只有在调用c.Visit之后才能运行,因此在getWeeks之后直接打印w,在任何情况下都会显示一个空的片段。
然而,即使在c.Visit之后打印,它也将是一个空片,为什么?
Go中的片作为一个数据结构实现--称为片头(更多信息:1、2)。
当您分配getWeeks的返回值时,实际上是在复制切片头,包括它的字段Data、Len和Cap。您可以在这个操场中看到它,方法是使用%p谓词打印切片的地址(使用其他结构而不是go-colly使示例自包含):
func getWeeks(c *Foo) []string {
var wks []string
c.OnHTML("div.ltbluediv", func(text string) {
weekName := text
wks = append(wks, weekName)
})
fmt.Printf("%p\n", &wks)
return wks
}
func main() {
c := &Foo{}
w := getWeeks(c)
c.Visit("target url")
fmt.Printf("%p\n", &w)
}打印两个不同的内存地址:
0xc0000ac030
0xc0000ac018现在,如果您继续在堆栈溢出中查找关于片和append行为的信息,您可能会发现,如果该片具有足够的容量(1、2、3.),则支持数组不会重新分配。
但是,即使通过以足够的容量初始化wks来确保支持数组是相同的,w的值仍然是原始片头的副本,因此具有0长度的。这在这个操场中得到了演示,它打印:
in getWeeks reflect.SliceHeader{Data:0xc0000121b0, Len:0, Cap:3}
in callback reflect.SliceHeader{Data:0xc0000121b0, Len:1, Cap:3}
in callback reflect.SliceHeader{Data:0xc0000121b0, Len:2, Cap:3}
in callback reflect.SliceHeader{Data:0xc0000121b0, Len:3, Cap:3}
[]
in main reflect.SliceHeader{Data:0xc0000121b0, Len:0, Cap:3}您可以通过重新注册w (游乐场)来调整它的长度:
c.Visit("target url")
w = w[0:3]
fmt.Println(w) // [foo bar baz]但这意味着你需要事先知道一个不引起重新分配的合理容量,以及重新分配的最终长度。
相反,返回一个指向片的指针:
func getWeeks(c *colly.Collector) *[]string {
wks := &[]string{}
c.OnHTML("div.ltbluediv", func(div *colly.HTMLElement) {
weekName := div.DOM.Find("span").Text()
*wks = append(*wks, weekName)
})
return wks
}或者将指针传递到getWeeks
func getWeeks(c *colly.Collector, wks *[]string) {
c.OnHTML("div.ltbluediv", func(div *colly.HTMLElement) {
weekName := div.DOM.Find("span").Text()
*wks = append(*wks, weekName)
})
}https://stackoverflow.com/questions/72097676
复制相似问题