1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
| package main
import ( "fmt" "log" "net/http"
"golang.org/x/net/html" )
func main() { breadthFirst(crawl, []string{"https://golang.org/"}) }
func crawl(url string) []string { fmt.Println(url)
list, err := extract(url)
if err != nil { log.Print(err) }
return list }
func forEachNode(n *html.Node, pre, post func(n *html.Node)) { if pre != nil { pre(n) }
for c := n.FirstChild; c != nil; c = c.NextSibling { forEachNode(c, pre, post) }
if post != nil { post(n) } }
func extract(url string) ([]string, error) { resp, err := http.Get(url)
if err != nil { return nil, err }
if resp.StatusCode != http.StatusOK { resp.Body.Close() return nil, fmt.Errorf("getting %s: %s", url, resp.Status) }
doc, err := html.Parse(resp.Body)
resp.Body.Close()
if err != nil { return nil, fmt.Errorf("parsing %s as HTML: %v", url, err) }
var links []string visitNode := func(n *html.Node) { 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 }
links = append(links, link.String()) } } }
forEachNode(doc, visitNode, nil)
return links, nil }
func breadthFirst(f func(item string) []string, worklist []string) { seen := make(map[string]bool)
for len(worklist) > 0 { items := worklist worklist = nil
for _, item := range items { if !seen[item] { seen[item] = true worklist = append(worklist, f(item)...) } } } }
|