now crawling multiple pages!
* moved Page{} and NewPage() into page.go
* queued and crawled are both maps now. used to track which pages we already know about
* tests updated to match the multipage crawling
* added another file to the testsite (scripts/blah.js)
This commit is contained in:
1 parent
0c48b94ece
commit
c95ad7ab95
5 files changed
+63
-41
No files matched your search
+21
-38
@@ -1,62 +1,38 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/url"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Page is a struct which defines a single page, which URLs (links and assets) it contains, etc.
|
|
||||||
type Page struct {
|
|
||||||
URL *url.URL
|
|
||||||
Base *Page
|
|
||||||
Title string
|
|
||||||
MediaType string
|
|
||||||
Children []*Page
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewPage takes a referring Page + a URL and returns a new &Page{}
|
|
||||||
func NewPage(referrer *Page, rawurl string) (*Page, error) {
|
|
||||||
// determine the base URL so we can resulve this into a full URL
|
|
||||||
baseURL := ""
|
|
||||||
if referrer != nil {
|
|
||||||
baseURL = referrer.URL.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve URL into a full absolute URL (non-relative)
|
|
||||||
u, err := resolveURL(baseURL, rawurl)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create struct and return
|
|
||||||
return &Page{URL: u}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// docrawl begins crawling the site at "url"
|
// docrawl begins crawling the site at "url"
|
||||||
func docrawl(url string) []*Page {
|
func docrawl(url string) []*Page {
|
||||||
// our result structure
|
// our result structure
|
||||||
var sitemap []*Page
|
var sitemap []*Page
|
||||||
|
|
||||||
// what we've already parsed, what we still need to parse
|
// what we've already parsed, what we still need to parse
|
||||||
parsed := make(map[string]*Page)
|
crawled := make(map[string]*Page)
|
||||||
var queued []*Page
|
queued := make(map[string]*Page)
|
||||||
|
|
||||||
// create first page and add to the queue
|
// create first page and add to the queue
|
||||||
u, _ := resolveURL("", url) // TODO check error
|
homepage, _ := NewPage(nil, url) // TODO check error
|
||||||
queued = append(queued, &Page{URL: u})
|
queued[homepage.URL.String()] = homepage
|
||||||
|
|
||||||
// main loop
|
// main loop
|
||||||
for len(queued) > 0 {
|
for len(queued) > 0 {
|
||||||
// pop a page off of queued
|
// pop a page off of queued
|
||||||
var dest *Page
|
var dest *Page
|
||||||
dest, queued = queued[len(queued)-1], queued[:len(queued)-1]
|
var destURL string
|
||||||
|
for destURL, dest = range queued {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
delete(queued, destURL)
|
||||||
|
|
||||||
// skip loop if we already did this URL
|
// skip loop if we already did this URL
|
||||||
if _, ok := parsed[dest.URL.String()]; ok {
|
if _, ok := crawled[dest.URL.String()]; ok {
|
||||||
continue // TODO handle
|
continue // TODO handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// mark this page as complete
|
// mark this page as complete
|
||||||
parsed[dest.URL.String()] = dest
|
crawled[dest.URL.String()] = dest
|
||||||
|
|
||||||
// fetch page
|
// fetch page
|
||||||
text, err := fetchPage(dest)
|
text, err := fetchPage(dest)
|
||||||
@@ -64,14 +40,21 @@ func docrawl(url string) []*Page {
|
|||||||
continue // TODO handle
|
continue // TODO handle
|
||||||
}
|
}
|
||||||
|
|
||||||
// parse it
|
// parse links
|
||||||
_, links, err := parseLinks(text)
|
_, links, err := parseLinks(text)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue // TODO handle
|
continue // TODO handle
|
||||||
}
|
}
|
||||||
for _, l := range links {
|
for _, l := range links {
|
||||||
u, _ := resolveURL(dest.URL.String(), l) // TODO check error
|
p, _ := NewPage(dest, l) // TODO check error
|
||||||
dest.Children = append(dest.Children, &Page{URL: u, Base: dest})
|
dest.Children = append(dest.Children, p)
|
||||||
|
|
||||||
|
// see if we should queue this new page for crawling
|
||||||
|
_, haveCrawled := crawled[p.URL.String()]
|
||||||
|
_, haveQueued := queued[p.URL.String()]
|
||||||
|
if !haveCrawled && !haveQueued {
|
||||||
|
queued[p.URL.String()] = p
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add page to list
|
// add page to list
|
||||||
|
|||||||
@@ -78,14 +78,14 @@ func TestSimpleFetch(t *testing.T) {
|
|||||||
func TestSimpleMap(t *testing.T) {
|
func TestSimpleMap(t *testing.T) {
|
||||||
pages := docrawl(baseURL)
|
pages := docrawl(baseURL)
|
||||||
t.Log("pages = ", pages)
|
t.Log("pages = ", pages)
|
||||||
if len(pages) != 1 {
|
if len(pages) != 4 {
|
||||||
t.Logf("got %v, wanted %v\n", len(pages), 1)
|
t.Logf("got %v, wanted %v\n", len(pages), 1)
|
||||||
t.Fatal("got wrong number of pages")
|
t.Fatal("got wrong number of pages")
|
||||||
}
|
}
|
||||||
if pages[0].URL.String() != baseURL {
|
if pages[0].URL.String() != baseURL {
|
||||||
t.Error("page URL is invalid")
|
t.Error("page URL is invalid")
|
||||||
}
|
}
|
||||||
if len(pages[0].Children) != 2 {
|
if len(pages[0].Children) != 3 {
|
||||||
t.Logf("got %v, wanted %v\n", len(pages[0].Children), 1)
|
t.Logf("got %v, wanted %v\n", len(pages[0].Children), 1)
|
||||||
t.Fatal("got wrong number of links")
|
t.Fatal("got wrong number of links")
|
||||||
}
|
}
|
||||||
@@ -99,6 +99,11 @@ func TestSimpleMap(t *testing.T) {
|
|||||||
t.Logf("Wanted: %q\n", baseURL+"assets/image.png")
|
t.Logf("Wanted: %q\n", baseURL+"assets/image.png")
|
||||||
t.Error("link name is incorrect")
|
t.Error("link name is incorrect")
|
||||||
}
|
}
|
||||||
|
if pages[0].Children[2].URL.String() != baseURL+"scripts/blah.js" {
|
||||||
|
t.Logf(" Got: %q\n", pages[0].Children[2].URL.String())
|
||||||
|
t.Logf("Wanted: %q\n", baseURL+"scripts/blah.js")
|
||||||
|
t.Error("link name is incorrect")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestHeaderFetching tests fetchFiletype() to see if we are getting expected results
|
// TestHeaderFetching tests fetchFiletype() to see if we are getting expected results
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Page is a struct which defines a single page, which URLs (links and assets) it contains, etc.
|
||||||
|
type Page struct {
|
||||||
|
URL *url.URL
|
||||||
|
Base *Page
|
||||||
|
Title string
|
||||||
|
MediaType string
|
||||||
|
Children []*Page
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewPage takes a referring Page + a URL and returns a new &Page{}
|
||||||
|
func NewPage(referrer *Page, rawurl string) (*Page, error) {
|
||||||
|
// determine the base URL so we can resulve this into a full URL
|
||||||
|
baseURL := ""
|
||||||
|
if referrer != nil {
|
||||||
|
baseURL = referrer.URL.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// resolve URL into a full absolute URL (non-relative)
|
||||||
|
u, err := resolveURL(baseURL, rawurl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// create struct and return
|
||||||
|
return &Page{URL: u}, nil
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img src="/assets/image.png"/>
|
<img src="/assets/image.png"/>
|
||||||
<a href="/about.html">
|
<a href="/about.html">fooey!</a>
|
||||||
|
<script src="scripts/blah.js"/>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
Reference in new issue
Block a user