私は、ウェブサイトのリンクをクロールするための単純なWebクローラーであるgoプロジェクトを作成しています。私は、ゴルーチンやチャンネルなどの同時機能を試してみたいと思います。しかし、私がそれを実行すると、それは通過しませんでした。全く何も起こっていないかのように示されていません。何が間違っているのか分かりません。誰かが私のためにそれを指摘できますか?golangの同時コンテキストでのチャネルの正しい使用の理解
チャネルロジックを削除すると、すべてのクロールされたリンクが表示されますが、リンクをバッファされたチャネルに送信してから、プログラムを終了する前にリンクを表示する必要があります。このプログラムは、プログラムで指定されている深度まで行くことができます。現在、深さが更新1.
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
"time"
"golang.org/x/net/html"
)
// Link type to be sent over channel
type Link struct {
URL string
ok bool
}
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: crawl [URL].")
}
url := os.Args[1]
if !strings.HasPrefix(url, "http://") {
url = "http://" + url
}
ch := make(chan *Link, 5)
crawl(url, 1, ch)
visited := make(map[string]bool)
time.Sleep(2 * time.Second)
for link := range ch {
if _, ok := visited[link.URL]; !ok {
visited[link.URL] = true
}
}
close(ch)
for l := range visited {
fmt.Println(l)
}
}
func crawl(url string, n int, ch chan *Link) {
if n < 1 {
return
}
resp, err := http.Get(url)
if err != nil {
log.Fatalf("Can not reach the site. Error = %v\n", err)
os.Exit(1)
}
b := resp.Body
defer b.Close()
z := html.NewTokenizer(b)
nextN := n - 1
for {
token := z.Next()
switch token {
case html.ErrorToken:
return
case html.StartTagToken:
current := z.Token()
if current.Data != "a" {
continue
}
result, ok := getHrefTag(current)
if !ok {
continue
}
hasProto := strings.HasPrefix(result, "http")
if hasProto {
go crawl(result, nextN, ch)
ch <- &Link{result, true}
}
}
}
}
func getHrefTag(token html.Token) (result string, ok bool) {
for _, a := range token.Attr {
if a.Key == "href" {
result = a.Val
ok = true
break
}
}
return
}
です:私は、しかし、私はまだそのURLをクロールしないようにする方法がわからない、データ競合を削除するようにコードを変更すること考え出しいくつかのあいた後
私は別の質問をしなければならないかもしれません:
package main
import (
"fmt"
"log"
"net/http"
"os"
"strings"
"golang.org/x/net/html"
)
func main() {
if len(os.Args) != 2 {
fmt.Println("Usage: crawl [URL].")
}
url := os.Args[1]
if !strings.HasPrefix(url, "http://") {
url = "http://" + url
}
for link := range newCrawl(url, 1) {
fmt.Println(link)
}
}
func newCrawl(url string, num int) chan string {
ch := make(chan string, 20)
go func() {
crawl(url, 1, ch)
close(ch)
}()
return ch
}
func crawl(url string, n int, ch chan string) {
if n < 1 {
return
}
resp, err := http.Get(url)
if err != nil {
log.Fatalf("Can not reach the site. Error = %v\n", err)
os.Exit(1)
}
b := resp.Body
defer b.Close()
z := html.NewTokenizer(b)
nextN := n - 1
for {
token := z.Next()
switch token {
case html.ErrorToken:
return
case html.StartTagToken:
current := z.Token()
if current.Data != "a" {
continue
}
result, ok := getHrefTag(current)
if !ok {
continue
}
hasProto := strings.HasPrefix(result, "http")
if hasProto {
done := make(chan struct{})
go func() {
crawl(result, nextN, ch)
close(done)
}()
<-done
ch <- result
}
}
}
}
func getHrefTag(token html.Token) (result string, ok bool) {
for _, a := range token.Attr {
if a.Key == "href" {
result = a.Val
ok = true
break
}
}
return
}
メインゴルーチンが(crawl' 'への呼び出しで)チャンネルに送り、後でチャンネルから受け取ります。 5つ以上のリンクが送信された場合、プログラムはデッドロックします。 –
@CeriseLimónありがとう。私は150に変更しようとしましたが、まだデッドロックです。助言がありますか?私は普通のウェブサイトがホームページに150以上のリンクを持っているとは思わない。 – newguy
mainから 'go craw(url、1、ch)'を呼び出すことでこの問題を修正しました。次の問題は 'ch'の' main'ブロックです。メインがリンクを印刷し続けるために何かが 'ch'を閉じる必要があります。 –