Compare commits

..
18 Commits
Author SHA1 Message Date
Martin AngersandGitHub 09540e5659 Merge pull request #234 from PuerkitoBio/wip-slice-end
Add ToEnd constant to use as special end index in Slice, closes #232.
2018-01-28 14:56:50 -05:00
Martin Angers 46f28d87ad document addition of ToEnd 2018-01-28 14:43:53 -05:00
Martin Angers 520f19d599 add ToEnd marker to Slice until the end of the Selection 2018-01-28 14:41:06 -05:00
Martin AngersandGitHub 2324bda66f Merge pull request #231 from Strum355/patch-1
Removed unnecessary conditional check
2018-01-18 09:26:56 -05:00
Noah Santschi-CooneyandGitHub 3855699cc0 Removed unnecessary if
IsMatcher returns false if len(s.Nodes) !> 0, so Is was double checking if len(s.Nodes) > 0
2018-01-17 13:49:09 +00:00
Martin Angers a41cf096e6 update changelog 2018-01-11 08:54:59 -05:00
Martin Angers 7d9aa5c1e6 misc/git: remove excessively verbose lint tools 2018-01-11 08:49:46 -05:00
Martin Angers c56812c109 fix godoc comment to comply with golint 2018-01-11 08:48:49 -05:00
Martin AngersandGitHub cab5e2c9d6 Merge pull request #229 from davidjwilkins/feature-226
Fixes #226 - Added the AddBack, AddBackFiltered, and AddBackMatcher methods.
2018-01-11 08:45:45 -05:00
David Wilkins 802bece6d0 Move deprecated notice to end of comment 2018-01-10 21:36:54 -08:00
David Wilkins 499bd47957 Fixes #226 - Added the AddBack, AddBackFiltered, and AddBackMatcher methods. Changed AndSelf to simply be an alias for AddBack, and added a deprecation notice to AddBack (as with https://api.jquery.com/andself/) 2018-01-09 20:34:18 -08:00
Martin AngersandGitHub bc4e06eb07 Merge pull request #221 from y-yagi/patch-1
Add go1.9 to travis
2017-12-06 07:16:06 -05:00
Yuji YaginumaandGitHub d8ab9abbf4 Add go1.9 to travis 2017-12-06 18:46:13 +09:00
Martin AngersandGitHub 1a71cc719d Merge pull request #215 from kataras/patch-1
README.md: Add colly - Scraping Framework to the Related Projects
2017-10-07 10:12:28 -04:00
Gerasimos (Makis) MaropoulosandGitHub 4b3a6d701e README.md: Add colly - Scraping Framework to the Related Projects 2017-10-07 07:57:40 +03:00
Martin AngersandGitHub 63e7f870c2 Merge pull request #211 from 38elements/fix-typo
Fix typo
2017-09-29 06:56:09 -04:00
38elementsandGitHub 109d3a6a56 Fix typo 2017-09-29 18:51:07 +09:00
Martin Angers ce645ea5e6 use svg travis badge, master branch 2017-08-16 09:38:31 -04:00
12 changed files with 102 additions and 168 deletions
+1
View File
@@ -9,4 +9,5 @@ go:
- 1.6.x
- 1.7.x
- 1.8.x
- 1.9.x
- tip
+4 -1
View File
@@ -1,5 +1,5 @@
# goquery - a little like that j-thing, only in Go
[![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.png)](http://travis-ci.org/PuerkitoBio/goquery) [![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
[![build status](https://secure.travis-ci.org/PuerkitoBio/goquery.svg?branch=master)](http://travis-ci.org/PuerkitoBio/goquery) [![GoDoc](https://godoc.org/github.com/PuerkitoBio/goquery?status.png)](http://godoc.org/github.com/PuerkitoBio/goquery) [![Sourcegraph Badge](https://sourcegraph.com/github.com/PuerkitoBio/goquery/-/badge.svg)](https://sourcegraph.com/github.com/PuerkitoBio/goquery?badge)
goquery brings a syntax and a set of features similar to [jQuery][] to the [Go language][go]. It is based on Go's [net/html package][html] and the CSS Selector library [cascadia][]. Since the net/html parser returns nodes, and not a full-featured DOM tree, jQuery's stateful manipulation functions (like height(), css(), detach()) have been left off.
@@ -37,6 +37,8 @@ Please note that because of the net/html dependency, goquery requires Go1.1+.
**Note that goquery's API is now stable, and will not break.**
* **2018-01-28 (v1.3.0)** : Add `ToEnd` constant to `Slice` until the end of the selection (thanks to @davidjwilkins for raising the issue).
* **2018-01-11 (v1.2.0)** : Add `AddBack*` and deprecate `AndSelf` (thanks to @davidjwilkins).
* **2017-02-12 (v1.1.0)** : Add `SetHtml` and `SetText` (thanks to @glebtv).
* **2016-12-29 (v1.0.2)** : Optimize allocations for `Selection.Text` (thanks to @radovskyb).
* **2016-08-28 (v1.0.1)** : Optimize performance for large documents.
@@ -121,6 +123,7 @@ func main() {
- [Goq][goq], an HTML deserialization and scraping library based on goquery and struct tags.
- [andybalholm/cascadia][cascadia], the CSS selector library used by goquery.
- [suntong/cascadia][cascadiacli], a command-line interface to the cascadia CSS selector library, useful to test selectors.
- [asciimoo/colly](https://github.com/asciimoo/colly), a lightning fast and elegant Scraping Framework
## License
+23 -2
View File
@@ -4,6 +4,16 @@ import (
"golang.org/x/net/html"
)
const (
maxUint = ^uint(0)
maxInt = int(maxUint >> 1)
// ToEnd is a special index value that can be used as end index in a call
// to Slice so that all elements are selected until the end of the Selection.
// It is equivalent to passing (*Selection).Length().
ToEnd = maxInt
)
// First reduces the set of matched elements to the first in the set.
// It returns a new Selection object, and an empty Selection object if the
// the selection is empty.
@@ -35,12 +45,23 @@ func (s *Selection) Eq(index int) *Selection {
}
// Slice reduces the set of matched elements to a subset specified by a range
// of indices.
// of indices. The start index is 0-based and indicates the index of the first
// element to select. The end index is 0-based and indicates the index at which
// the elements stop being selected (the end index is not selected).
//
// The indices may be negative, in which case they represent an offset from the
// end of the selection.
//
// The special value ToEnd may be specified as end index, in which case all elements
// until the end are selected. This works both for a positive and negative start
// index.
func (s *Selection) Slice(start, end int) *Selection {
if start < 0 {
start += len(s.Nodes)
}
if end < 0 {
if end == ToEnd {
end = len(s.Nodes)
} else if end < 0 {
end += len(s.Nodes)
}
return pushStack(s, s.Nodes[start:end])
+22
View File
@@ -98,6 +98,17 @@ func TestSlice(t *testing.T) {
sel := Doc().Find(".pvk-content").Slice(0, 2)
assertLength(t, sel.Nodes, 2)
assertSelectionIs(t, sel, "#pc1", "#pc2")
}
func TestSliceToEnd(t *testing.T) {
sel := Doc().Find(".pvk-content").Slice(1, ToEnd)
assertLength(t, sel.Nodes, 2)
assertSelectionIs(t, sel.Eq(0), "#pc2")
if _, ok := sel.Eq(1).Attr("id"); ok {
t.Error("Want no attribute ID, got one")
}
}
func TestSliceEmpty(t *testing.T) {
@@ -110,6 +121,11 @@ func TestSliceInvalid(t *testing.T) {
Doc().Find("").Slice(0, 2)
}
func TestSliceInvalidToEnd(t *testing.T) {
defer assertPanic(t)
Doc().Find("").Slice(2, ToEnd)
}
func TestSliceOutOfBounds(t *testing.T) {
defer assertPanic(t)
Doc().Find(".pvk-content").Slice(2, 12)
@@ -135,6 +151,12 @@ func TestNegativeSliceBoth(t *testing.T) {
assertSelectionIs(t, sel.Eq(1), "#cf3")
}
func TestNegativeSliceToEnd(t *testing.T) {
sel := Doc().Find(".container-fluid").Slice(-3, ToEnd)
assertLength(t, sel.Nodes, 3)
assertSelectionIs(t, sel, "#cf2", "#cf3", "#cf4")
}
func TestNegativeSliceOutOfBounds(t *testing.T) {
defer assertPanic(t)
Doc().Find(".container-fluid").Slice(-12, -7)
+1 -1
View File
@@ -231,6 +231,6 @@ func BenchmarkEnd(b *testing.B) {
}
}
if n != 373 {
b.Fatalf("wnat 373, got %d", n)
b.Fatalf("want 373, got %d", n)
}
}
+24
View File
@@ -41,6 +41,30 @@ func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
// AndSelf adds the previous set of elements on the stack to the current set.
// It returns a new Selection object containing the current Selection combined
// with the previous one.
// Deprecated: This function has been deprecated and is now an alias for AddBack().
func (s *Selection) AndSelf() *Selection {
return s.AddBack()
}
// AddBack adds the previous set of elements on the stack to the current set.
// It returns a new Selection object containing the current Selection combined
// with the previous one.
func (s *Selection) AddBack() *Selection {
return s.AddSelection(s.prevSel)
}
// AddBackFiltered reduces the previous set of elements on the stack to those that
// match the selector string, and adds them to the current set.
// It returns a new Selection object containing the current Selection combined
// with the filtered previous one
func (s *Selection) AddBackFiltered(selector string) *Selection {
return s.AddSelection(s.prevSel.Filter(selector))
}
// AddBackMatcher reduces the previous set of elements on the stack to those that match
// the mateher, and adds them to the curernt set.
// It returns a new Selection object containing the current Selection combined
// with the filtered previous one
func (s *Selection) AddBackMatcher(m Matcher) *Selection {
return s.AddSelection(s.prevSel.FilterMatcher(m))
}
+22
View File
@@ -94,3 +94,25 @@ func TestAndSelfRollback(t *testing.T) {
sel2 := sel.Find("a").AndSelf().End().End()
assertEqual(t, sel, sel2)
}
func TestAddBack(t *testing.T) {
sel := Doc().Find(".span12").Last().AddBack()
assertLength(t, sel.Nodes, 2)
}
func TestAddBackRollback(t *testing.T) {
sel := Doc().Find(".pvk-content")
sel2 := sel.Find("a").AddBack().End().End()
assertEqual(t, sel, sel2)
}
func TestAddBackFiltered(t *testing.T) {
sel := Doc().Find(".span12, .footer").Find("h1").AddBackFiltered(".footer")
assertLength(t, sel.Nodes, 2)
}
func TestAddBackFilteredRollback(t *testing.T) {
sel := Doc().Find(".span12, .footer")
sel2 := sel.Find("h1").AddBackFiltered(".footer").End().End()
assertEqual(t, sel, sel2)
}
+4 -3
View File
@@ -270,13 +270,14 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
return s.Remove()
}
// Set the html content of each element in the selection to specified html string.
// SetHtml sets the html content of each element in the selection to
// specified html string.
func (s *Selection) SetHtml(html string) *Selection {
return setHtmlNodes(s, parseHtml(html)...)
}
// Set the content of each element in the selection to specified content. The
// provided text string is escaped.
// SetText sets the content of each element in the selection to specified content.
// The provided text string is escaped.
func (s *Selection) SetText(text string) *Selection {
return s.SetHtml(html.EscapeString(text))
}
-15
View File
@@ -18,21 +18,6 @@ gosimple $(go list ./... | grep -v /vendor/)
echo "<<< gosimple"
echo
echo ">>> staticcheck"
staticcheck $(go list ./... | grep -v /vendor/)
echo "<<< staticcheck"
echo
echo ">>> unused"
unused $(go list ./... | grep -v /vendor/)
echo "<<< unused"
echo
echo ">>> gas"
gas $(find . -name "*.go" | grep -v /vendor/ | grep -v '_test.go$')
echo "<<< gas"
echo
# Check for gofmt problems and report if any.
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$' | grep -v /vendor/)
[ -z "$gofiles" ] && echo "EXIT $vetres" && exit $vetres
+1 -5
View File
@@ -5,11 +5,7 @@ import "golang.org/x/net/html"
// Is checks the current matched set of elements against a selector and
// returns true if at least one of these elements matches.
func (s *Selection) Is(selector string) bool {
if len(s.Nodes) > 0 {
return s.IsMatcher(compileMatcher(selector))
}
return false
return s.IsMatcher(compileMatcher(selector))
}
// IsMatcher checks the current matched set of elements against a matcher and
-56
View File
@@ -18,62 +18,6 @@ var nodeNames = []string{
html.CommentNode: "#comment",
}
// PathForNode returns a unique path to retrieve the specified node
// from its document tree. The path is a slice of int indices, starting
// at the root of the tree.
func PathForNode(n *html.Node) []int {
var indices []int
for n := n; n != nil; n = n.Parent {
ix := 0
for prev := n.PrevSibling; prev != nil; prev = prev.PrevSibling {
ix++
}
indices = append(indices, ix)
}
// reverse the slice of indices
for l, r := 0, len(indices)-1; l < r; l, r = l+1, r-1 {
indices[l], indices[r] = indices[r], indices[l]
}
return indices
}
// NodeAtPath returns the HTML node at the specified path in the
// document tree of the specified n node. The path is followed from
// the root of the tree. If no node is found by following the path,
// nil is returned.
func NodeAtPath(path []int, n *html.Node) *html.Node {
if n == nil {
return n
}
// start at root
for n.Parent != nil {
n = n.Parent
}
for n.PrevSibling != nil {
n = n.PrevSibling
}
for i, ix := range path {
if i > 0 {
n = n.FirstChild
if n == nil {
return n
}
}
for j := 0; j < ix; j++ {
n = n.NextSibling
if n == nil {
return n
}
}
}
return n
}
// NodeName returns the node name of the first element in the selection.
// It tries to behave in a similar way as the DOM's nodeName property
// (https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName).
-85
View File
@@ -9,91 +9,6 @@ import (
"golang.org/x/net/html"
)
var invalidPathNodes = []struct {
in string
path []int
}{
{"<a>", []int{0, 1, 2}},
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", []int{0, 0, 1, 2, 0}},
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", []int{1}},
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", []int{1, 2}},
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", []int{1, 2, 10}},
}
var validPathNodes = []struct {
in string
el string
path []int
}{
{"<a>", "a", []int{0, 0, 1, 0}}, // root html body(1) a
{"<html><head><meta></head><body></body></html>", "meta", []int{0, 0, 0, 0}}, // root html head meta
{"<html><head><meta><title></title></head><body></body></html>", "title", []int{0, 0, 0, 1}}, // root html head title
{"<html><head><meta><title></title></head><body><div><p></p></div></body></html>", "div", []int{0, 0, 1, 0}}, // root html body(1) div
{"<html><head><meta><title></title></head><body><div><p></p></div></body></html>", "p", []int{0, 0, 1, 0, 0}}, // root html body(1) div p
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", "a", []int{0, 0, 1, 0, 1}}, // root html body(1) div a(1)
{"<html><head><meta><title></title></head><body><div><p></p><a></a><span></span></div></body></html>", "span", []int{0, 0, 1, 0, 2}}, // root html body(1) div span(2)
}
func TestPathForNode(t *testing.T) {
for i, c := range validPathNodes {
doc, err := NewDocumentFromReader(strings.NewReader(c.in))
if err != nil {
t.Errorf("%d: failed to parse: %v", i, err)
continue
}
var n *html.Node
if sel := doc.Find(c.el); sel.Length() > 0 {
n = sel.Get(0)
}
got := PathForNode(n)
if !reflect.DeepEqual(c.path, got) {
h, _ := OuterHtml(doc.Selection)
t.Errorf("%d: want %v, got %v (html: %s)", i, c.path, got, h)
}
}
// test a nil node
if got := PathForNode(nil); got != nil {
t.Errorf("want nil for nil node, got %v", got)
}
}
func TestNodeAtPath(t *testing.T) {
// valid cases
for i, c := range validPathNodes {
n, err := html.Parse(strings.NewReader(c.in))
if err != nil {
t.Errorf("%d: failed to parse: %v", i, err)
continue
}
nn := NodeAtPath(c.path, n)
if nn.Data != c.el {
t.Errorf("%d: want element %s, got %s (%v)", i, c.el, nn.Data, nn)
}
}
// invalid cases
for i, c := range invalidPathNodes {
n, err := html.Parse(strings.NewReader(c.in))
if err != nil {
t.Errorf("%d: failed to parse: %v", i, err)
continue
}
if got := NodeAtPath(c.path, n); got != nil {
t.Errorf("%d: want nil, got %v", i, got)
}
}
// test a nil node
if got := NodeAtPath([]int{1, 2, 3}, nil); got != nil {
t.Errorf("want nil for nil node, got %v", got)
}
}
var allNodes = `<!doctype html>
<html>
<head>