Golang网页蜘蛛与分页处理

时间:2018-01-10 23:39:04

标签: go concurrency pagination web-crawler screen-scraping

我正在研究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(...)效果很好。请告诉我,我的错误在哪里。 希望谷歌翻译是正确的。

1 个答案:

答案 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