Graphische Sitemap der heimetli-Domains
Anstatt die Sitemap durch bash-Scripts und lynx zu erstellen wurde diesmal ein go-Programm benutzt.
Die Visualisierung erfolgte wiederum durch Gephi, weil es schlicht die beste Wahl ist.
Ein positiver Nebeneffekt dieses Projektes: es gibt jetzt weniger Fehler in den Links ;-)
Parameter dieser Darstellung
Das go-Programm sammelt nur die Links und schreibt sie in ein .CSV. Gephi importiert das problemlos, aber die Labels der Knoten fehlen. Um diese zu erzeugen geht man in den Data View und kopiert die Id der Knoten in die Labels. Ohne diese Aktion steht man ziemlich ratlos vor den vielen Punkten...
Als Algorithmus für das Layout wurde "Force Atlas" gewählt, und die Knoten mit dem Page Rank eingefärbt.
Der Spider
Zur Abwechslung mal ein go-Programm:
package main import ( "container/list" "fmt" "golang.org/x/exp/slices" "golang.org/x/net/html" "io" "net/http" "net/url" "os" "regexp" "strings" "time" ) var csv *os.File func valid(value []byte) bool { suffixes := []string{".gif", ".jpg", ".pdf", ".java", ".c", ".cpp", ".zip", ".bin", ".wav", ".war"} str := string(value) for _, suffix := range suffixes { if strings.HasSuffix(str, suffix) { return false } } if strings.Contains(str, "mailto:") { return false } return true } func notInList(lst *list.List, str string) bool { for ptr := lst.Front(); ptr != nil; ptr = ptr.Next() { if ptr.Value == str { return false } } return true } func processPage(base string, page string, scheme *regexp.Regexp, domain *regexp.Regexp) ([]string, error) { result := []string{} path, err := url.JoinPath(base, page) if err != nil { return result, err } fmt.Println(path) resp, err := http.Get(path) if err != nil { return result, err } defer resp.Body.Close() var link string tokenizer := html.NewTokenizer(resp.Body) for { token := tokenizer.Next() switch token { case html.ErrorToken: err := tokenizer.Err() if err == io.EOF { return result, nil } else { return result, err } case html.StartTagToken: name, _ := tokenizer.TagName() if string(name) == "a" { for { key, value, more := tokenizer.TagAttr() if string(key) == "href" { if valid(value) { if scheme.Match(value) { if domain.Match(value) { link := string(value) var err error if !strings.HasSuffix(link, ".html") { link, err = url.JoinPath(link, "index.html") } if err == nil { result = append(result, link) fmt.Fprintf(csv, "%s,%s\n", path, link) } else { fmt.Println("JoinPath", err) } } else { fmt.Println("Ignored", string(value)) } } else { link, err = url.JoinPath(base, string(value)) if err == nil { result = append(result, link) fmt.Fprintf(csv, "%s,%s\n", path, link) } else { fmt.Println("JoinPath", err) } } } } if !more { break } } } } } } func processDomain(start string, scheme, domain *regexp.Regexp) { todo := list.New() todo.PushBack(start) done := []string{} links := []string{} var err error for todo.Front() != nil { page := todo.Remove(todo.Front()).(string) done = append(done, page) pos := strings.LastIndexByte(page, '/') links, err = processPage(page[0:pos], page[pos+1:], scheme, domain) if err != nil { fmt.Println(err) } else { for _, link := range links { if !slices.Contains(done, link) && notInList(todo, link) { todo.PushBack(link) } } time.Sleep(10 * time.Second) } } } func main() { var err error csv, err = os.Create("network.csv") if err != nil { fmt.Println(err) os.Exit(1) } defer csv.Close() fmt.Fprintf(csv, "Source,Target\n") scheme, _ := regexp.Compile("^http[s]?:") domain, _ := regexp.Compile("www\\.ffhs\\.ch") processDomain("https://www.ffhs.ch/index.html", scheme, domain) }
Bemerkenswert ist die url.JoinPath-Methode. Die löst elegant einige knifflige Spezialfälle beim Zusammensetzen der URLs.