作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我尝试基于Tour和其他一些关于此的答案构建并发爬虫。我目前的情况在下面,但我认为这里有两个细微的问题。
main
中的调试打印)。我知道这是因为,当我什至将WriteToSlice
更改为Read
时,有时在Read
中也从未达到“读取:结束,计数器=”,并且总是在获得16个网址时使用。err
channel 时遇到了麻烦,即使我使用类似于www.golang.org的地址运行我的主要Crawl
方法时,在此 channel 中也没有收到任何消息,因此如果没有有效的架构错误,则应该通过err
channel 发送package main
import (
"fmt"
"net/http"
"sync"
"golang.org/x/net/html"
)
type urlCache struct {
urls map[string]struct{}
sync.Mutex
}
func (v *urlCache) Set(url string) bool {
v.Lock()
defer v.Unlock()
_, exist := v.urls[url]
v.urls[url] = struct{}{}
return !exist
}
func newURLCache() *urlCache {
return &urlCache{
urls: make(map[string]struct{}),
}
}
type results struct {
data chan string
err chan error
}
func newResults() *results {
return &results{
data: make(chan string, 1),
err: make(chan error, 1),
}
}
func (r *results) close() {
close(r.data)
close(r.err)
}
func (r *results) WriteToSlice(s *[]string) {
for {
select {
case data := <-r.data:
*s = append(*s, data)
case err := <-r.err:
fmt.Println("e ", err)
}
}
}
func (r *results) Read() {
fmt.Println("Read: start")
counter := 0
for c := range r.data {
fmt.Println(c)
counter++
}
fmt.Println("Read: end, counter = ", counter)
}
func crawl(url string, depth int, wg *sync.WaitGroup, cache *urlCache, res *results) {
defer wg.Done()
if depth == 0 || !cache.Set(url) {
return
}
response, err := http.Get(url)
if err != nil {
res.err <- err
return
}
defer response.Body.Close()
node, err := html.Parse(response.Body)
if err != nil {
res.err <- err
return
}
urls := grablUrls(response, node)
res.data <- url
for _, url := range urls {
wg.Add(1)
go crawl(url, depth-1, wg, cache, res)
}
}
func grablUrls(resp *http.Response, node *html.Node) []string {
var f func(*html.Node) []string
var results []string
f = func(n *html.Node) []string {
if n.Type == html.ElementNode && n.Data == "a" {
for _, a := range n.Attr {
if a.Key != "href" {
continue
}
link, err := resp.Request.URL.Parse(a.Val)
if err != nil {
continue
}
results = append(results, link.String())
}
}
for c := n.FirstChild; c != nil; c = c.NextSibling {
f(c)
}
return results
}
res := f(node)
return res
}
// Crawl ...
func Crawl(url string, depth int) []string {
wg := &sync.WaitGroup{}
output := &[]string{}
visited := newURLCache()
results := newResults()
defer results.close()
wg.Add(1)
go crawl(url, depth, wg, visited, results)
go results.WriteToSlice(output)
// go results.Read()
wg.Wait()
return *output
}
func main() {
r := Crawl("https://www.golang.org", 2)
// r := Crawl("www.golang.org", 2) // no schema, error should be generated and send via err
fmt.Println(len(r))
}
最佳答案
您的问题1和2都是同一错误的结果。
在Crawl()
中,您不必等待该go例程完成:go results.WriteToSlice(output)
。在最后一个crawl()
函数上,释放 WaitGroup ,在WriteToSlice
函数以data
和err
channel 结束之前返回输出并打印输出。所以发生了什么事:
crawl()
完成,将数据放入results.data
和results.err
中。 wait()
解锁,导致main()
打印结果的长度[]string
WriteToSlice
将最后一个数据(或err)项添加到 channel Crawl()
返回数据,不仅要在将数据写到 channel 中完成时,还需要在从 channel 整体上读取完(包括缓冲区)时返回。做到这一点的一个好方法是,当您确定已完成 channel 时,请关闭 channel 。通过以这种方式组织代码,您可以阻止正在耗尽 channel 的执行例程,而无需使用 WaitGroup 将其释放给main,而是等到 channel 100%完成。
Crawl
以阻止
WriteToSlice
。抓取功能完成后,关闭数据 channel ,然后等待
WriteToSlice
完成。
// Crawl ...
func Crawl(url string, depth int) []string {
wg := &sync.WaitGroup{}
output := &[]string{}
visited := newURLCache()
results := newResults()
go func() {
wg.Add(1)
go crawl(url, depth, wg, visited, results)
wg.Wait()
// All data is written, this makes `WriteToSlice()` unblock
close(results.data)
}()
// This will block until results.data is closed
results.WriteToSlice(output)
close(results.err)
return *output
}
然后在写入 slice 时,您必须检查关闭的 channel 以退出for循环:
func (r *results) WriteToSlice(s *[]string) {
for {
select {
case data, open := <-r.data:
if !open {
return // All data done
}
*s = append(*s, data)
case err := <-r.err:
fmt.Println("e ", err)
}
}
}
这是完整的代码:
https://play.golang.org/p/GBpGk-lzrhd(在操场上不起作用)
关于go - 搜寻器的并发问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65980858/
我是一名优秀的程序员,十分优秀!