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.