initial
This commit is contained in:
93
scraper.go
Normal file
93
scraper.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
type crawlFunc func(*html.Node)
|
||||
|
||||
func searchElem(n *html.Node, data string) chan *html.Node {
|
||||
ch := make(chan *html.Node)
|
||||
var crawl crawlFunc
|
||||
crawl = func(n *html.Node) {
|
||||
if n.Type == html.ElementNode && n.Data == data {
|
||||
ch <- n
|
||||
}
|
||||
c := n.FirstChild
|
||||
for c != nil {
|
||||
crawl(c)
|
||||
c = c.NextSibling
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
defer close(ch)
|
||||
crawl(n)
|
||||
}()
|
||||
|
||||
return ch
|
||||
}
|
||||
|
||||
func searchAttr(n *html.Node, key, contains string) chan *html.Node {
|
||||
ch := make(chan *html.Node)
|
||||
var crawl crawlFunc
|
||||
crawl = func(n *html.Node) {
|
||||
for _, a := range n.Attr {
|
||||
if a.Key == key &&
|
||||
containsWord(a.Val, contains) {
|
||||
ch <- n
|
||||
}
|
||||
}
|
||||
c := n.FirstChild
|
||||
for c != nil {
|
||||
crawl(c)
|
||||
c = c.NextSibling
|
||||
}
|
||||
}
|
||||
go func() {
|
||||
defer close(ch)
|
||||
crawl(n)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func searchElemAttr(n *html.Node, elem, key, value string) chan *html.Node {
|
||||
ch := make(chan *html.Node)
|
||||
go func() {
|
||||
defer close(ch)
|
||||
for e := range searchElem(n, elem) {
|
||||
// If document is too large there are
|
||||
// would be a hundreds of goroutines :((
|
||||
for attr := range searchAttr(e, key, value) {
|
||||
ch <- attr
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func crawlText(n *html.Node) string {
|
||||
var s = new(strings.Builder)
|
||||
var crawl crawlFunc
|
||||
crawl = func(n *html.Node) {
|
||||
if n.Type == html.TextNode {
|
||||
Must(
|
||||
s.WriteString(func() string {
|
||||
trimmed := strings.TrimSpace(n.Data)
|
||||
if n.Data == "" {
|
||||
return trimmed
|
||||
}
|
||||
return trimmed+"\n"
|
||||
}()),
|
||||
)
|
||||
}
|
||||
c := n.FirstChild
|
||||
for c != nil {
|
||||
crawl(c)
|
||||
c = c.NextSibling
|
||||
}
|
||||
}
|
||||
crawl(n)
|
||||
return s.String()
|
||||
}
|
||||
67
scraper_test.go
Normal file
67
scraper_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
const htmlStr = `<div><span id="test">Hello</span><span id="test">World</span></div>`
|
||||
|
||||
func TestSearchElem(t *testing.T) {
|
||||
doc := Must(
|
||||
html.Parse(strings.NewReader(htmlStr)),
|
||||
)
|
||||
|
||||
ch := searchElem(doc, "span")
|
||||
count := 0
|
||||
for range ch {
|
||||
count++
|
||||
}
|
||||
|
||||
if count != 2 {
|
||||
t.Errorf("Expected: 2 span elements, got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchAttr(t *testing.T) {
|
||||
doc := Must(
|
||||
html.Parse(strings.NewReader(htmlStr)),
|
||||
)
|
||||
|
||||
ch := searchAttr(doc, "id", "test")
|
||||
count := 0
|
||||
for range ch {
|
||||
count++
|
||||
}
|
||||
|
||||
if count != 2 {
|
||||
t.Errorf("Expected: 2 span elements with id 'test', got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchElemAttr(t *testing.T) {
|
||||
doc := Must(
|
||||
html.Parse(strings.NewReader(htmlStr)),
|
||||
)
|
||||
|
||||
ch := searchElemAttr(doc, "span", "id", "test")
|
||||
count := 0
|
||||
for range ch {
|
||||
count++
|
||||
}
|
||||
|
||||
if count != 2 {
|
||||
t.Errorf("Expected: 2 span elements with id 'test', got: %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCrawlTest(t *testing.T) {
|
||||
fmt.Println(
|
||||
crawlText(Must(
|
||||
html.Parse(strings.NewReader(htmlStr)),
|
||||
)),
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user