Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f72d5d5eb0 | ||
|
|
1b09e6c204 | ||
|
|
4e335cfc8a | ||
|
|
db314ec938 | ||
|
|
42a40be62f | ||
|
|
ee3cac7e29 | ||
|
|
6edd18e361 | ||
|
|
f16be33dbd | ||
|
|
6665729e3f | ||
|
|
48be4bc77d |
No files matched your search
@@ -2,12 +2,6 @@
|
||||
|
||||
## Bugs
|
||||
|
||||
* deal with anchor issue (repeatedly crawling index.html#about, index.html#contact, etc.)
|
||||
* should only crawl these pages once
|
||||
* but should be included in the links part
|
||||
* when sorting links, "uniq" them as well
|
||||
* be smart about index.html?
|
||||
|
||||
## Cleanup
|
||||
|
||||
* helper function to do the whole crawl process
|
||||
|
||||
@@ -8,10 +8,15 @@ import (
|
||||
)
|
||||
|
||||
// docrawl begins crawling the site at "homeurl"
|
||||
func docrawl(homeurl string) itemSlice {
|
||||
func doCrawl(homeurl string) itemSlice {
|
||||
// set of what we have already crawled
|
||||
crawled := make(itemMap)
|
||||
|
||||
// a set of crawled URLs which have been cleaned
|
||||
// useful so that we don't crawl http://a.com/index.html#about if we've
|
||||
// already crawled http://a.com/index.html, or vice versa
|
||||
crawledStripped := make(itemMap)
|
||||
|
||||
// map (of urlString -> httpItem) of our results
|
||||
results := make(itemMap)
|
||||
|
||||
@@ -32,6 +37,7 @@ func docrawl(homeurl string) itemSlice {
|
||||
|
||||
// start the home page crawl
|
||||
crawled[homeitem.url.String()] = homeitem
|
||||
crawledStripped[stripURL(homeitem.url)] = homeitem
|
||||
go crawlItem(homeitem, rchan)
|
||||
|
||||
// wait for results
|
||||
@@ -59,10 +65,21 @@ func docrawl(homeurl string) itemSlice {
|
||||
r.children[i] = existing
|
||||
continue
|
||||
}
|
||||
if existing, ok := crawledStripped[stripURL(c.url)]; ok {
|
||||
// we crawled a different version of this same page
|
||||
// i.e. same page, different anchor. we don't need to crawl it again
|
||||
// but we do need to keep it in our results, so copy the existing
|
||||
// struct over for everything except the URLs
|
||||
r.children[i].title = existing.title
|
||||
r.children[i].linkType = existing.linkType
|
||||
r.children[i].children = existing.children
|
||||
continue
|
||||
}
|
||||
|
||||
// haven't crawled this one yet, do so now
|
||||
crawlingCount++
|
||||
crawled[c.url.String()] = c
|
||||
crawledStripped[stripURL(c.url)] = c
|
||||
go crawlItem(c, rchan)
|
||||
}
|
||||
|
||||
@@ -102,7 +119,7 @@ func crawlItem(item *httpItem, rchan chan<- *httpItem) {
|
||||
}
|
||||
|
||||
// parse links
|
||||
title, links, _ := parseLinks(text) // TODO remove error from parseLinks, not needed
|
||||
title, links := parseLinks(text) // TODO remove error from parseLinks, not needed
|
||||
if err != nil {
|
||||
item.linkType = tBroken
|
||||
rchan <- item
|
||||
@@ -126,7 +143,7 @@ func crawlItem(item *httpItem, rchan chan<- *httpItem) {
|
||||
func main() {
|
||||
// crawl each URL on the command line
|
||||
for _, u := range os.Args[1:] {
|
||||
pages := docrawl(u)
|
||||
pages := doCrawl(u)
|
||||
l := sitemapToLocations(pages)
|
||||
j, _ := locationsToJSON(l)
|
||||
fmt.Println(j)
|
||||
|
||||
@@ -76,7 +76,7 @@ func TestSimpleFetch(t *testing.T) {
|
||||
|
||||
// TestSimpleMap figures out the site map for the site in baseURL
|
||||
func TestSimpleMap(t *testing.T) {
|
||||
pages := docrawl(baseURL)
|
||||
pages := doCrawl(baseURL)
|
||||
|
||||
// because our crawl is non-deterministic, we have to do a complete
|
||||
// cycle through every page, counting stuff, finding specific pages
|
||||
@@ -185,7 +185,7 @@ func TestHeaderFetching(t *testing.T) {
|
||||
|
||||
// TestJsonOutput gets a sitemap and then converts it to json
|
||||
func TestJsonOutput(t *testing.T) {
|
||||
pages := docrawl(baseURL)
|
||||
pages := doCrawl(baseURL)
|
||||
l := sitemapToLocations(pages)
|
||||
if len(l) != 2 {
|
||||
t.Error("sitemapToLocations has the wrong number of locations")
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// custom errors
|
||||
var (
|
||||
errContentTypeNotFound = errors.New("no Content-Type header found")
|
||||
errFetchError = errors.New("couldn't fetch item")
|
||||
|
||||
@@ -13,6 +13,7 @@ type itemMap map[string]*httpItem
|
||||
// itemType is an enum so we know how to classify each item
|
||||
type itemType int
|
||||
|
||||
// enums for itemType
|
||||
const (
|
||||
tUnknown itemType = iota
|
||||
tHTMLPage
|
||||
|
||||
@@ -77,6 +77,7 @@ func sitemapToLocations(pages itemSlice) []*Location {
|
||||
return locations
|
||||
}
|
||||
|
||||
// locationsToJSON takes a *Location slice and marshals it into a JSON string
|
||||
func locationsToJSON(locations []*Location) (string, error) {
|
||||
b, err := json.Marshal(locations)
|
||||
if err != nil {
|
||||
|
||||
@@ -11,7 +11,7 @@ var reTitle = regexp.MustCompile(`(?i)<\s*title\s*>([^<]*)<\s*\/\s*title`)
|
||||
|
||||
// parseLinks takes a string and attempts to parse any html title and all links out of it,
|
||||
// and returns a slice of the captures found
|
||||
func parseLinks(s string) (string, []string, error) {
|
||||
func parseLinks(s string) (string, []string) {
|
||||
// find the title, or default to empty
|
||||
title := ""
|
||||
titleMatches := reTitle.FindStringSubmatch(s)
|
||||
@@ -25,5 +25,5 @@ func parseLinks(s string) (string, []string, error) {
|
||||
for _, match := range reURL.FindAllStringSubmatch(s, -1) {
|
||||
results = append(results, match[2])
|
||||
}
|
||||
return title, results, nil
|
||||
return title, results
|
||||
}
|
||||
@@ -16,10 +16,7 @@ func TestSimpleParse(t *testing.T) {
|
||||
<script href="scripts/blah.js"/>
|
||||
</body>
|
||||
</html>`
|
||||
title, matches, err := parseLinks(doc)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
title, matches := parseLinks(doc)
|
||||
if title != "Test Page" {
|
||||
t.Error("got wrong title")
|
||||
}
|
||||
|
||||
+17
-1
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// custom errors
|
||||
var (
|
||||
errInvalidURL = errors.New("this URL can't be parsed successfully")
|
||||
)
|
||||
@@ -50,11 +51,26 @@ func resolveURL(referringURL string, currentURL string) (*url.URL, error) {
|
||||
return uResolved, checkURL(uResolved)
|
||||
}
|
||||
|
||||
func stripAnchorFromURL(u *url.URL) string {
|
||||
// stripURL returns a version of the URL without the "Fragment" part,
|
||||
// which is anything after the '#' character, so:
|
||||
// http://a.com/blah.html#anchor becomes http://a.com/blah.html
|
||||
// ... and without files like "index.htm" in the Path (since that's
|
||||
// equivalent to "/"
|
||||
func stripURL(u *url.URL) string {
|
||||
// copy the url locally
|
||||
ucopy := *u
|
||||
|
||||
// blank out the anchor, return URI string
|
||||
ucopy.Fragment = ""
|
||||
|
||||
// remove index.html if it exists
|
||||
suffixes := []string{"index.html", "index.htm"}
|
||||
for _, s := range suffixes {
|
||||
if strings.HasSuffix(strings.ToLower(ucopy.Path), s) {
|
||||
ucopy.Path = ucopy.Path[0 : len(ucopy.Path)-len(s)]
|
||||
}
|
||||
}
|
||||
|
||||
// return new stripped down URL string
|
||||
return ucopy.String()
|
||||
}
|
||||
@@ -69,13 +69,36 @@ func TestBadResolve(t* testing.T) {
|
||||
}
|
||||
*/
|
||||
|
||||
// TestURLStripAnchor tests the stripURL() function
|
||||
func TestURLStripAnchor(t *testing.T) {
|
||||
u, err := url.Parse("http://swaits:pass@somehost.com:8765/blah/blah.html?x=y#foo")
|
||||
if err != nil {
|
||||
t.Fatal("couldn't parse a URL")
|
||||
{
|
||||
u, err := url.Parse("http://swaits:pass@somehost.com:8765/blah/blah.html?x=y#foo")
|
||||
if err != nil {
|
||||
t.Fatal("couldn't parse a URL")
|
||||
}
|
||||
stripped := stripURL(u)
|
||||
if stripped != "http://swaits:pass@somehost.com:8765/blah/blah.html?x=y" {
|
||||
t.Error("stripping anchor #foo from URL failed")
|
||||
}
|
||||
}
|
||||
stripped := stripAnchorFromURL(u)
|
||||
if stripped != "http://swaits:pass@somehost.com:8765/blah/blah.html?x=y" {
|
||||
t.Error("stripping anchor #foo from URL failed")
|
||||
{
|
||||
u, err := url.Parse("http://swaits:pass@somehost.com:8765/blah/index.html?x=y#foo")
|
||||
if err != nil {
|
||||
t.Fatal("couldn't parse a URL")
|
||||
}
|
||||
stripped := stripURL(u)
|
||||
if stripped != "http://swaits:pass@somehost.com:8765/blah/?x=y" {
|
||||
t.Error("stripping anchor #foo from URL failed")
|
||||
}
|
||||
}
|
||||
{
|
||||
u, err := url.Parse("http://swaits:pass@somehost.com:8765/blah/index.htm?x=y#foo")
|
||||
if err != nil {
|
||||
t.Fatal("couldn't parse a URL")
|
||||
}
|
||||
stripped := stripURL(u)
|
||||
if stripped != "http://swaits:pass@somehost.com:8765/blah/?x=y" {
|
||||
t.Error("stripping anchor #foo from URL failed")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestUniqStrings passes a slice of strings to uniqStrings() to see if it removes
|
||||
// all duplicates
|
||||
func TestUniqStrings(t *testing.T) {
|
||||
strs := []string{"a", "a", "b", "a", "b", "c", "d", "a", "b", "e", "d"}
|
||||
uniq := uniqStrings(strs)
|
||||
@@ -17,6 +19,7 @@ func TestUniqStrings(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestUniqStringsNilParm passes a nil slice to uniqStrings to be sure it returns nil
|
||||
func TestUniqStringsNilParm(t *testing.T) {
|
||||
uniq := uniqStrings(nil)
|
||||
if uniq != nil {
|
||||
|
||||
Reference in new issue
Block a user