具有分页处理功能的 Golang 网络蜘蛛
Golang web spider with pagination processing
我正在开发一个 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
}
}
它总是returns 只有第一页,没有分页。同样的 GetPagination(...) 效果很好。请告诉我,我的错误在哪里。
希望 Google 翻译正确。
问题可能是 main
在所有 goroutine 完成之前就退出了。
首先,在 select
语句之后有一个 break
,它在第一次读取通道后运行异常。这确保了 main
func returns 在你第一次通过 channelBody
.
发送内容后
其次,这里使用channelDone
不是正确的方法。最惯用的方法是使用 sync.WaitGroup
。在启动每个 goroutine 之前,使用 WG.Add(1)
并将 defer
替换为 defer WG.Done()
;在 main
中,使用 WG.Wait()
。请注意,您应该使用指针来引用 WaitGroup
。您可以阅读更多 here.
我正在开发一个 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
}
}
它总是returns 只有第一页,没有分页。同样的 GetPagination(...) 效果很好。请告诉我,我的错误在哪里。 希望 Google 翻译正确。
问题可能是 main
在所有 goroutine 完成之前就退出了。
首先,在 select
语句之后有一个 break
,它在第一次读取通道后运行异常。这确保了 main
func returns 在你第一次通过 channelBody
.
其次,这里使用channelDone
不是正确的方法。最惯用的方法是使用 sync.WaitGroup
。在启动每个 goroutine 之前,使用 WG.Add(1)
并将 defer
替换为 defer WG.Done()
;在 main
中,使用 WG.Wait()
。请注意,您应该使用指针来引用 WaitGroup
。您可以阅读更多 here.