我正在研究golang网络抓取工具,它应该在某些特定搜索引擎上解析搜索结果。主要的难点 - 用并发解析,或者更确切地说,在处理分页时如
← Previous 1 2 3 4 5 ... 34 Next →
。除了递归抓取分页结果外,所有工作都正常。看看我的代码:
package main
import (
"bufio"
"errors"
"fmt"
"net"
"strings"
"github.com/antchfx/htmlquery"
"golang.org/x/net/html"
)
type Spider struct {
HandledUrls []string
}
func NewSpider(url string) *Spider {
// ...
}
func requestProvider(request string) string {
// Everything is good here
}
func connectProvider(url string) net.Conn {
// Also works
}
// getContents makes request to search engine and gets response body
func getContents(request string) *html.Node {
// ...
}
// CheckResult controls empty search results
func checkResult(node *html.Node) bool {
// ...
}
func (s *Spider) checkVisited(url string) bool {
// ...
}
// Here is the problems
func (s *Spider) Crawl(url string, channelDone chan bool, channelBody chan *html.Node) {
body := getContents(url)
defer func() {
channelDone <- true
}()
if checkResult(body) == false {
err := errors.New("Nothing found there")
ErrFatal(err)
}
channelBody <- body
s.HandledUrls = append(s.HandledUrls, url)
fmt.Println("Handled ", url)
newUrls := s.getPagination(body)
for _, u := range newUrls {
fmt.Println(u)
}
for i, newurl := range newUrls {
if s.checkVisited(newurl) == false {
fmt.Println(i)
go s.Crawl(newurl, channelDone, channelBody)
}
}
}
func (s *Spider) getPagination(node *html.Node) []string {
// ...
}
func main() {
request := requestProvider(*requestFlag)
channelBody := make(chan *html.Node, 120)
channelDone := make(chan bool)
var parsedHosts []*Host
s := NewSpider(request)
go s.Crawl(request, channelDone, channelBody)
for {
select {
case recievedNode := <-channelBody:
// ...
for _, h := range newHosts {
parsedHosts = append(parsedHosts, h)
fmt.Println("added", h.HostUrl)
}
case <-channelDone:
fmt.Println("Jobs finished")
}
break
}
}
它始终只返回第一页,没有分页。相同的GetPagination(...)效果很好。请告诉我,我的错误在哪里。 希望谷歌翻译是正确的。
答案 0 :(得分:1)
问题可能是main
在所有goroutine完成之前退出。
首先,在break
语句后面有一个select
,并且在第一次读取通道后它会无意地运行。这可确保main
func在您第一次通过channelBody
发送内容后返回。
其次,使用channelDone
不是正确的方法。最惯用的方法是使用sync.WaitGroup
。在开始每个goroutine之前,请使用WG.Add(1)
并将defer
替换为defer WG.Done()
;在main
中,使用WG.Wait()
。请注意,您应该使用指针来引用WaitGroup
。您可以阅读更多here。