mirror of
https://github.com/PuerkitoBio/goquery.git
synced 2024-04-21 12:31:36 +00:00
Compare commits
90
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d7b0271f0 | ||
|
|
d89086ead1 | ||
|
|
99c8f1ecd5 | ||
|
|
c942deb265 | ||
|
|
0132bb8147 | ||
|
|
6f0f9d6b87 | ||
|
|
cd12820764 | ||
|
|
51a0e1a4bb | ||
|
|
89946c829f | ||
|
|
8b2a6e25d5 | ||
|
|
538dd4a913 | ||
|
|
844d6bd46b | ||
|
|
1b16b92ddf | ||
|
|
3d11372b35 | ||
|
|
bd23f0c6e0 | ||
|
|
56ba1e6f08 | ||
|
|
b17a2bbb6d | ||
|
|
c70bac62f5 | ||
|
|
8674076365 | ||
|
|
6dcff85b2e | ||
|
|
3dcf72e6c1 | ||
|
|
22fd749627 | ||
|
|
9b813b2309 | ||
|
|
0255f16068 | ||
|
|
2d2796f417 | ||
|
|
80f0084f4c | ||
|
|
68fe358134 | ||
|
|
2af3d16e2b | ||
|
|
6c2cec4567 | ||
|
|
8311f594d7 | ||
|
|
dc2ec5c7ca | ||
|
|
7757e27950 | ||
|
|
ea1bc64a63 | ||
|
|
dd77530fdb | ||
|
|
a86ea07301 | ||
|
|
b4912d23e5 | ||
|
|
106fcbf95a | ||
|
|
61aa1975b1 | ||
|
|
7a8f63580d | ||
|
|
6285ceb1e9 | ||
|
|
b55d492104 | ||
|
|
ef34a70bad | ||
|
|
5819122076 | ||
|
|
09540e5659 | ||
|
|
46f28d87ad | ||
|
|
520f19d599 | ||
|
|
2324bda66f | ||
|
|
3855699cc0 | ||
|
|
a41cf096e6 | ||
|
|
7d9aa5c1e6 | ||
|
|
c56812c109 | ||
|
|
cab5e2c9d6 | ||
|
|
802bece6d0 | ||
|
|
499bd47957 | ||
|
|
bc4e06eb07 | ||
|
|
d8ab9abbf4 | ||
|
|
1a71cc719d | ||
|
|
4b3a6d701e | ||
|
|
63e7f870c2 | ||
|
|
109d3a6a56 | ||
|
|
ce645ea5e6 | ||
|
|
8806ada2a4 | ||
|
|
2dc93891ab | ||
|
|
4a7586a83f | ||
|
|
ed7d758e9a | ||
|
|
c641b87978 | ||
|
|
28076b7c97 | ||
|
|
e1271ee34c | ||
|
|
f5c5de8530 | ||
|
|
46c32cef0e | ||
|
|
755fd592bb | ||
|
|
3cb3b86568 | ||
|
|
2ee2e49ac5 | ||
|
|
c0b805c7dc | ||
|
|
152b1a2c8f | ||
|
|
9043fe2599 | ||
|
|
56a198aba8 | ||
|
|
5dccaa115b | ||
|
|
26f4f54c91 | ||
|
|
e89fe653fd | ||
|
|
ab2bb46123 | ||
|
|
b91267330e | ||
|
|
d85ad7e2ed | ||
|
|
6841d28c6f | ||
|
|
7bd630023a | ||
|
|
c7533ba696 | ||
|
|
ad8b3d8222 | ||
|
|
7f4291566c | ||
|
|
f0d75731e0 | ||
|
|
77cbaef46a |
+10
-1
@@ -1,7 +1,16 @@
|
||||
# editor temporary files
|
||||
*.sublime-*
|
||||
.DS_Store
|
||||
*.swp
|
||||
#*.*#
|
||||
tags
|
||||
goquery.test
|
||||
|
||||
# direnv config
|
||||
.env*
|
||||
|
||||
# test binaries
|
||||
*.test
|
||||
|
||||
# coverage and profilte outputs
|
||||
*.out
|
||||
|
||||
|
||||
+13
-6
@@ -1,10 +1,17 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
||||
- 1.5
|
||||
- 1.6
|
||||
- 1.2.x
|
||||
- 1.3.x
|
||||
- 1.4.x
|
||||
- 1.5.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- tip
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# goquery - a little like that j-thing, only in Go [](http://travis-ci.org/PuerkitoBio/goquery) [](http://godoc.org/github.com/PuerkitoBio/goquery)
|
||||
# goquery - a little like that j-thing, only in Go
|
||||
[](http://travis-ci.org/PuerkitoBio/goquery) [](http://godoc.org/github.com/PuerkitoBio/goquery) [](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.
|
||||
|
||||
@@ -6,6 +7,16 @@ Also, because the net/html parser requires UTF-8 encoding, so does goquery: it i
|
||||
|
||||
Syntax-wise, it is as close as possible to jQuery, with the same function names when possible, and that warm and fuzzy chainable interface. jQuery being the ultra-popular library that it is, I felt that writing a similar HTML-manipulating library was better to follow its API than to start anew (in the same spirit as Go's `fmt` package), even though some of its methods are less than intuitive (looking at you, [index()][index]...).
|
||||
|
||||
## Table of Contents
|
||||
|
||||
* [Installation](#installation)
|
||||
* [Changelog](#changelog)
|
||||
* [API](#api)
|
||||
* [Examples](#examples)
|
||||
* [Related Projects](#related-projects)
|
||||
* [Support](#support)
|
||||
* [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
Please note that because of the net/html dependency, goquery requires Go1.1+.
|
||||
@@ -13,7 +24,7 @@ Please note that because of the net/html dependency, goquery requires Go1.1+.
|
||||
$ go get github.com/PuerkitoBio/goquery
|
||||
|
||||
(optional) To run unit tests:
|
||||
|
||||
|
||||
$ cd $GOPATH/src/github.com/PuerkitoBio/goquery
|
||||
$ go test
|
||||
|
||||
@@ -26,6 +37,16 @@ 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.**
|
||||
|
||||
* **2020-02-04 (v1.5.1)** : Update module dependencies.
|
||||
* **2018-11-15 (v1.5.0)** : Go module support (thanks @Zaba505).
|
||||
* **2018-06-07 (v1.4.1)** : Add `NewDocumentFromReader` examples.
|
||||
* **2018-03-24 (v1.4.0)** : Deprecate `NewDocument(url)` and `NewDocumentFromResponse(response)`.
|
||||
* **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.
|
||||
* **2016-07-27 (v1.0.0)** : Tag version 1.0.0.
|
||||
* **2016-06-15** : Invalid selector strings internally compile to a `Matcher` implementation that never matches any node (instead of a panic). So for example, `doc.Find("~")` returns an empty `*Selection` object.
|
||||
* **2016-02-02** : Add `NodeName` utility function similar to the DOM's `nodeName` property. It returns the tag name of the first element in a selection, and other relevant values of non-element nodes (see godoc for details). Add `OuterHtml` utility function similar to the DOM's `outerHTML` property (named `OuterHtml` in small caps for consistency with the existing `Html` method on the `Selection`).
|
||||
* **2015-04-20** : Add `AttrOr` helper method to return the attribute's value or a default value if absent. Thanks to [piotrkowalczuk][piotr].
|
||||
@@ -77,18 +98,32 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
func ExampleScrape() {
|
||||
doc, err := goquery.NewDocument("http://metalsucks.net")
|
||||
// Request the HTML page.
|
||||
res, err := http.Get("http://metalsucks.net")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *goquery.Selection) {
|
||||
band := s.Find("h3").Text()
|
||||
// Find the review items
|
||||
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
|
||||
// For each item found, get the band and title
|
||||
band := s.Find("a").Text()
|
||||
title := s.Find("i").Text()
|
||||
fmt.Printf("Review %d: %s - %s\n", i, band, title)
|
||||
})
|
||||
@@ -99,6 +134,35 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Related Projects
|
||||
|
||||
- [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.
|
||||
- [gocolly/colly](https://github.com/gocolly/colly), a lightning fast and elegant Scraping Framework
|
||||
- [gnulnx/goperf](https://github.com/gnulnx/goperf), a website performance test tool that also fetches static assets.
|
||||
- [MontFerret/ferret](https://github.com/MontFerret/ferret), declarative web scraping.
|
||||
- [tacusci/berrycms](https://github.com/tacusci/berrycms), a modern simple to use CMS with easy to write plugins
|
||||
- [Dataflow kit](https://github.com/slotix/dataflowkit), Web Scraping framework for Gophers.
|
||||
- [Geziyor](https://github.com/geziyor/geziyor), a fast web crawling & scraping framework for Go. Supports JS rendering.
|
||||
- [Pagser](https://github.com/foolin/pagser), a simple, easy, extensible, configurable HTML parser to struct based on goquery and struct tags.
|
||||
|
||||
## Support
|
||||
|
||||
There are a number of ways you can support the project:
|
||||
|
||||
* Use it, star it, build something with it, spread the word!
|
||||
- If you do build something open-source or otherwise publicly-visible, let me know so I can add it to the [Related Projects](#related-projects) section!
|
||||
* Raise issues to improve the project (note: doc typos and clarifications are issues too!)
|
||||
- Please search existing issues before opening a new one - it may have already been adressed.
|
||||
* Pull requests: please discuss new code in an issue first, unless the fix is really trivial.
|
||||
- Make sure new code is tested.
|
||||
- Be mindful of existing code - PRs that break existing code have a high probability of being declined, unless it fixes a serious issue.
|
||||
|
||||
If you desperately want to send money my way, I have a BuyMeACoffee.com page:
|
||||
|
||||
<a href="https://www.buymeacoffee.com/mna" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
|
||||
|
||||
## License
|
||||
|
||||
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
|
||||
@@ -106,6 +170,7 @@ The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia'
|
||||
[jquery]: http://jquery.com/
|
||||
[go]: http://golang.org/
|
||||
[cascadia]: https://github.com/andybalholm/cascadia
|
||||
[cascadiacli]: https://github.com/suntong/cascadia
|
||||
[bsd]: http://opensource.org/licenses/BSD-3-Clause
|
||||
[golic]: http://golang.org/LICENSE
|
||||
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
|
||||
@@ -116,3 +181,4 @@ The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia'
|
||||
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
|
||||
[thatguystone]: https://github.com/thatguystone
|
||||
[piotr]: https://github.com/piotrkowalczuk
|
||||
[goq]: https://github.com/andrewstuart/goq
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
BenchmarkFirst-4 30000000 50.7 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkLast-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkEq-4 30000000 55.7 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkSlice-4 500000000 3.45 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndex-4 3000000 541 ns/op 248 B/op 10 allocs/op
|
||||
BenchmarkIndexSelector-4 200000 10749 ns/op 2464 B/op 17 allocs/op
|
||||
BenchmarkIndexOfNode-4 200000000 6.47 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndexOfSelection-4 200000000 7.27 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMetalReviewExample-4 10000 138426 ns/op 12240 B/op 319 allocs/op
|
||||
BenchmarkAdd-4 200000 10192 ns/op 208 B/op 9 allocs/op
|
||||
BenchmarkAddSelection-4 10000000 158 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAndSelf-4 1000000 1588 ns/op 1008 B/op 5 allocs/op
|
||||
BenchmarkFilter-4 100000 20427 ns/op 360 B/op 8 allocs/op
|
||||
BenchmarkNot-4 100000 23508 ns/op 136 B/op 5 allocs/op
|
||||
BenchmarkFilterFunction-4 50000 34178 ns/op 22976 B/op 755 allocs/op
|
||||
BenchmarkNotFunction-4 50000 38173 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterNodes-4 50000 34001 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotNodes-4 30000 40344 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterSelection-4 50000 33308 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotSelection-4 30000 40748 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkHas-4 5000 263346 ns/op 1816 B/op 48 allocs/op
|
||||
BenchmarkHasNodes-4 10000 160840 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkHasSelection-4 10000 165410 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEach-4 300000 4664 ns/op 3304 B/op 118 allocs/op
|
||||
BenchmarkMap-4 200000 8286 ns/op 5572 B/op 184 allocs/op
|
||||
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
|
||||
BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkText-4 200000 8909 ns/op 7536 B/op 110 allocs/op
|
||||
BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHtml-4 3000000 422 ns/op 120 B/op 2 allocs/op
|
||||
BenchmarkIs-4 100000 22615 ns/op 88 B/op 4 allocs/op
|
||||
BenchmarkIsPositional-4 50000 26655 ns/op 1112 B/op 10 allocs/op
|
||||
BenchmarkIsFunction-4 1000000 1208 ns/op 784 B/op 28 allocs/op
|
||||
BenchmarkIsSelection-4 50000 33497 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkIsNodes-4 50000 33572 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkHasClass-4 10000 232802 ns/op 14944 B/op 976 allocs/op
|
||||
BenchmarkContains-4 200000000 7.33 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFind-4 200000 10715 ns/op 2464 B/op 17 allocs/op
|
||||
BenchmarkFindWithinSelection-4 50000 35878 ns/op 2176 B/op 78 allocs/op
|
||||
BenchmarkFindSelection-4 10000 194356 ns/op 2672 B/op 82 allocs/op
|
||||
BenchmarkFindNodes-4 10000 195510 ns/op 2672 B/op 82 allocs/op
|
||||
BenchmarkContents-4 1000000 2252 ns/op 864 B/op 34 allocs/op
|
||||
BenchmarkContentsFiltered-4 500000 3015 ns/op 1016 B/op 39 allocs/op
|
||||
BenchmarkChildren-4 5000000 364 ns/op 152 B/op 7 allocs/op
|
||||
BenchmarkChildrenFiltered-4 1000000 2212 ns/op 352 B/op 15 allocs/op
|
||||
BenchmarkParent-4 50000 24643 ns/op 4048 B/op 381 allocs/op
|
||||
BenchmarkParentFiltered-4 50000 25967 ns/op 4248 B/op 388 allocs/op
|
||||
BenchmarkParents-4 30000 50000 ns/op 27776 B/op 830 allocs/op
|
||||
BenchmarkParentsFiltered-4 30000 53107 ns/op 28360 B/op 838 allocs/op
|
||||
BenchmarkParentsUntil-4 100000 22423 ns/op 10352 B/op 353 allocs/op
|
||||
BenchmarkParentsUntilSelection-4 20000 86925 ns/op 51144 B/op 1516 allocs/op
|
||||
BenchmarkParentsUntilNodes-4 20000 87597 ns/op 51144 B/op 1516 allocs/op
|
||||
BenchmarkParentsFilteredUntil-4 300000 5568 ns/op 2232 B/op 86 allocs/op
|
||||
BenchmarkParentsFilteredUntilSelection-4 200000 10966 ns/op 5440 B/op 190 allocs/op
|
||||
BenchmarkParentsFilteredUntilNodes-4 200000 10919 ns/op 5440 B/op 190 allocs/op
|
||||
BenchmarkSiblings-4 30000 46018 ns/op 15400 B/op 204 allocs/op
|
||||
BenchmarkSiblingsFiltered-4 30000 50566 ns/op 16496 B/op 213 allocs/op
|
||||
BenchmarkNext-4 200000 7921 ns/op 3216 B/op 112 allocs/op
|
||||
BenchmarkNextFiltered-4 200000 8804 ns/op 3416 B/op 118 allocs/op
|
||||
BenchmarkNextAll-4 50000 31098 ns/op 9912 B/op 138 allocs/op
|
||||
BenchmarkNextAllFiltered-4 50000 34677 ns/op 11008 B/op 147 allocs/op
|
||||
BenchmarkPrev-4 200000 7920 ns/op 3216 B/op 112 allocs/op
|
||||
BenchmarkPrevFiltered-4 200000 8913 ns/op 3416 B/op 118 allocs/op
|
||||
BenchmarkPrevAll-4 200000 10845 ns/op 4376 B/op 113 allocs/op
|
||||
BenchmarkPrevAllFiltered-4 100000 12030 ns/op 4576 B/op 119 allocs/op
|
||||
BenchmarkNextUntil-4 100000 19193 ns/op 5760 B/op 260 allocs/op
|
||||
BenchmarkNextUntilSelection-4 50000 34829 ns/op 18480 B/op 542 allocs/op
|
||||
BenchmarkNextUntilNodes-4 100000 14459 ns/op 7944 B/op 248 allocs/op
|
||||
BenchmarkPrevUntil-4 20000 66296 ns/op 12856 B/op 448 allocs/op
|
||||
BenchmarkPrevUntilSelection-4 30000 45037 ns/op 23432 B/op 689 allocs/op
|
||||
BenchmarkPrevUntilNodes-4 200000 11525 ns/op 6152 B/op 203 allocs/op
|
||||
BenchmarkNextFilteredUntil-4 100000 12940 ns/op 4512 B/op 173 allocs/op
|
||||
BenchmarkNextFilteredUntilSelection-4 50000 38924 ns/op 19160 B/op 567 allocs/op
|
||||
BenchmarkNextFilteredUntilNodes-4 50000 38528 ns/op 19160 B/op 567 allocs/op
|
||||
BenchmarkPrevFilteredUntil-4 100000 12980 ns/op 4664 B/op 175 allocs/op
|
||||
BenchmarkPrevFilteredUntilSelection-4 50000 39671 ns/op 19936 B/op 587 allocs/op
|
||||
BenchmarkPrevFilteredUntilNodes-4 50000 39484 ns/op 19936 B/op 587 allocs/op
|
||||
BenchmarkClosest-4 500000 3310 ns/op 160 B/op 8 allocs/op
|
||||
BenchmarkClosestSelection-4 5000000 361 ns/op 96 B/op 6 allocs/op
|
||||
BenchmarkClosestNodes-4 5000000 359 ns/op 96 B/op 6 allocs/op
|
||||
PASS
|
||||
ok github.com/PuerkitoBio/goquery 163.718s
|
||||
@@ -0,0 +1,85 @@
|
||||
BenchmarkFirst-4 30000000 50.9 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkLast-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkEq-4 30000000 50.5 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkSlice-4 500000000 3.53 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGet-4 2000000000 1.66 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndex-4 2000000 832 ns/op 248 B/op 10 allocs/op
|
||||
BenchmarkIndexSelector-4 100000 16073 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkIndexOfNode-4 200000000 6.38 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndexOfSelection-4 200000000 7.14 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMetalReviewExample-4 10000 140737 ns/op 12418 B/op 320 allocs/op
|
||||
BenchmarkAdd-4 100000 13162 ns/op 974 B/op 10 allocs/op
|
||||
BenchmarkAddSelection-4 500000 3160 ns/op 814 B/op 2 allocs/op
|
||||
BenchmarkAddNodes-4 500000 3159 ns/op 814 B/op 2 allocs/op
|
||||
BenchmarkAndSelf-4 200000 7423 ns/op 2404 B/op 9 allocs/op
|
||||
BenchmarkFilter-4 100000 19671 ns/op 360 B/op 8 allocs/op
|
||||
BenchmarkNot-4 100000 22577 ns/op 136 B/op 5 allocs/op
|
||||
BenchmarkFilterFunction-4 50000 33960 ns/op 22976 B/op 755 allocs/op
|
||||
BenchmarkNotFunction-4 50000 37909 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterNodes-4 50000 34196 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotNodes-4 30000 40446 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterSelection-4 50000 33091 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotSelection-4 30000 40609 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkHas-4 5000 262936 ns/op 2371 B/op 50 allocs/op
|
||||
BenchmarkHasNodes-4 10000 148631 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkHasSelection-4 10000 153117 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkEnd-4 2000000000 1.02 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEach-4 300000 4653 ns/op 3304 B/op 118 allocs/op
|
||||
BenchmarkMap-4 200000 8257 ns/op 5572 B/op 184 allocs/op
|
||||
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
|
||||
BenchmarkAttr-4 100000000 22.0 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkText-4 200000 8913 ns/op 7536 B/op 110 allocs/op
|
||||
BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHtml-4 5000000 398 ns/op 120 B/op 2 allocs/op
|
||||
BenchmarkIs-4 100000 22392 ns/op 88 B/op 4 allocs/op
|
||||
BenchmarkIsPositional-4 50000 26259 ns/op 1112 B/op 10 allocs/op
|
||||
BenchmarkIsFunction-4 1000000 1212 ns/op 784 B/op 28 allocs/op
|
||||
BenchmarkIsSelection-4 50000 33222 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkIsNodes-4 50000 33408 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkHasClass-4 10000 233208 ns/op 14944 B/op 976 allocs/op
|
||||
BenchmarkContains-4 200000000 7.57 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFind-4 100000 16121 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkFindWithinSelection-4 20000 68019 ns/op 11521 B/op 97 allocs/op
|
||||
BenchmarkFindSelection-4 5000 387582 ns/op 59787 B/op 176 allocs/op
|
||||
BenchmarkFindNodes-4 5000 389246 ns/op 59797 B/op 176 allocs/op
|
||||
BenchmarkContents-4 200000 11475 ns/op 2878 B/op 42 allocs/op
|
||||
BenchmarkContentsFiltered-4 200000 11222 ns/op 2498 B/op 46 allocs/op
|
||||
BenchmarkChildren-4 2000000 650 ns/op 152 B/op 7 allocs/op
|
||||
BenchmarkChildrenFiltered-4 500000 2568 ns/op 352 B/op 15 allocs/op
|
||||
BenchmarkParent-4 2000 702513 ns/op 194478 B/op 828 allocs/op
|
||||
BenchmarkParentFiltered-4 2000 690778 ns/op 194658 B/op 835 allocs/op
|
||||
BenchmarkParents-4 10000 124855 ns/op 49869 B/op 868 allocs/op
|
||||
BenchmarkParentsFiltered-4 10000 128535 ns/op 50456 B/op 876 allocs/op
|
||||
BenchmarkParentsUntil-4 20000 72982 ns/op 23802 B/op 388 allocs/op
|
||||
BenchmarkParentsUntilSelection-4 10000 156099 ns/op 72453 B/op 1549 allocs/op
|
||||
BenchmarkParentsUntilNodes-4 10000 156610 ns/op 72455 B/op 1549 allocs/op
|
||||
BenchmarkParentsFilteredUntil-4 100000 15549 ns/op 4068 B/op 94 allocs/op
|
||||
BenchmarkParentsFilteredUntilSelection-4 100000 20564 ns/op 7276 B/op 198 allocs/op
|
||||
BenchmarkParentsFilteredUntilNodes-4 100000 20635 ns/op 7276 B/op 198 allocs/op
|
||||
BenchmarkSiblings-4 3000 565114 ns/op 205910 B/op 336 allocs/op
|
||||
BenchmarkSiblingsFiltered-4 3000 580264 ns/op 206993 B/op 345 allocs/op
|
||||
BenchmarkNext-4 20000 93177 ns/op 26810 B/op 169 allocs/op
|
||||
BenchmarkNextFiltered-4 20000 94171 ns/op 27013 B/op 175 allocs/op
|
||||
BenchmarkNextAll-4 5000 270320 ns/op 89289 B/op 237 allocs/op
|
||||
BenchmarkNextAllFiltered-4 5000 275283 ns/op 90375 B/op 246 allocs/op
|
||||
BenchmarkPrev-4 20000 92777 ns/op 26810 B/op 169 allocs/op
|
||||
BenchmarkPrevFiltered-4 20000 95577 ns/op 27007 B/op 175 allocs/op
|
||||
BenchmarkPrevAll-4 20000 86339 ns/op 27515 B/op 151 allocs/op
|
||||
BenchmarkPrevAllFiltered-4 20000 87759 ns/op 27715 B/op 157 allocs/op
|
||||
BenchmarkNextUntil-4 10000 163930 ns/op 48541 B/op 330 allocs/op
|
||||
BenchmarkNextUntilSelection-4 30000 56382 ns/op 23880 B/op 556 allocs/op
|
||||
BenchmarkNextUntilNodes-4 100000 18883 ns/op 8703 B/op 252 allocs/op
|
||||
BenchmarkPrevUntil-4 3000 484668 ns/op 145402 B/op 611 allocs/op
|
||||
BenchmarkPrevUntilSelection-4 20000 72125 ns/op 28865 B/op 705 allocs/op
|
||||
BenchmarkPrevUntilNodes-4 100000 14722 ns/op 6510 B/op 205 allocs/op
|
||||
BenchmarkNextFilteredUntil-4 50000 39006 ns/op 10990 B/op 192 allocs/op
|
||||
BenchmarkNextFilteredUntilSelection-4 20000 66048 ns/op 25641 B/op 586 allocs/op
|
||||
BenchmarkNextFilteredUntilNodes-4 20000 65314 ns/op 25640 B/op 586 allocs/op
|
||||
BenchmarkPrevFilteredUntil-4 50000 33312 ns/op 9709 B/op 189 allocs/op
|
||||
BenchmarkPrevFilteredUntilSelection-4 20000 64197 ns/op 24981 B/op 601 allocs/op
|
||||
BenchmarkPrevFilteredUntilNodes-4 20000 64505 ns/op 24982 B/op 601 allocs/op
|
||||
BenchmarkClosest-4 500000 4065 ns/op 160 B/op 8 allocs/op
|
||||
BenchmarkClosestSelection-4 2000000 756 ns/op 96 B/op 6 allocs/op
|
||||
BenchmarkClosestNodes-4 2000000 753 ns/op 96 B/op 6 allocs/op
|
||||
PASS
|
||||
ok github.com/PuerkitoBio/goquery 162.053s
|
||||
@@ -0,0 +1,85 @@
|
||||
BenchmarkFirst-4 30000000 51.8 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkLast-4 30000000 50.1 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkEq-4 30000000 51.4 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkSlice-4 500000000 3.52 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGet-4 2000000000 1.65 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndex-4 2000000 787 ns/op 248 B/op 10 allocs/op
|
||||
BenchmarkIndexSelector-4 100000 16952 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkIndexOfNode-4 200000000 6.42 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndexOfSelection-4 200000000 7.12 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMetalReviewExample-4 10000 141994 ns/op 12418 B/op 320 allocs/op
|
||||
BenchmarkAdd-4 200000 10367 ns/op 208 B/op 9 allocs/op
|
||||
BenchmarkAddSelection-4 10000000 152 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAddNodes-4 10000000 147 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAndSelf-4 1000000 1647 ns/op 1008 B/op 5 allocs/op
|
||||
BenchmarkFilter-4 100000 19522 ns/op 360 B/op 8 allocs/op
|
||||
BenchmarkNot-4 100000 22546 ns/op 136 B/op 5 allocs/op
|
||||
BenchmarkFilterFunction-4 50000 35087 ns/op 22976 B/op 755 allocs/op
|
||||
BenchmarkNotFunction-4 50000 39123 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterNodes-4 50000 34890 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotNodes-4 30000 41145 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterSelection-4 50000 33735 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotSelection-4 30000 41334 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkHas-4 5000 264058 ns/op 2370 B/op 50 allocs/op
|
||||
BenchmarkHasNodes-4 10000 151718 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkHasSelection-4 10000 156955 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkEnd-4 2000000000 1.01 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEach-4 300000 4660 ns/op 3304 B/op 118 allocs/op
|
||||
BenchmarkMap-4 200000 8404 ns/op 5572 B/op 184 allocs/op
|
||||
BenchmarkEachWithBreak-4 2000000 806 ns/op 560 B/op 20 allocs/op
|
||||
BenchmarkAttr-4 100000000 21.6 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkText-4 200000 8911 ns/op 7536 B/op 110 allocs/op
|
||||
BenchmarkLength-4 2000000000 0.34 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHtml-4 3000000 405 ns/op 120 B/op 2 allocs/op
|
||||
BenchmarkIs-4 100000 22228 ns/op 88 B/op 4 allocs/op
|
||||
BenchmarkIsPositional-4 50000 26469 ns/op 1112 B/op 10 allocs/op
|
||||
BenchmarkIsFunction-4 1000000 1240 ns/op 784 B/op 28 allocs/op
|
||||
BenchmarkIsSelection-4 50000 33709 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkIsNodes-4 50000 33711 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkHasClass-4 10000 236005 ns/op 14944 B/op 976 allocs/op
|
||||
BenchmarkContains-4 200000000 7.47 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFind-4 100000 16075 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkFindWithinSelection-4 30000 41418 ns/op 3539 B/op 82 allocs/op
|
||||
BenchmarkFindSelection-4 10000 209490 ns/op 5616 B/op 89 allocs/op
|
||||
BenchmarkFindNodes-4 10000 208206 ns/op 5614 B/op 89 allocs/op
|
||||
BenchmarkContents-4 300000 4751 ns/op 1420 B/op 36 allocs/op
|
||||
BenchmarkContentsFiltered-4 300000 5454 ns/op 1570 B/op 41 allocs/op
|
||||
BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
|
||||
BenchmarkChildrenFiltered-4 1000000 2484 ns/op 352 B/op 15 allocs/op
|
||||
BenchmarkParent-4 50000 34724 ns/op 6940 B/op 387 allocs/op
|
||||
BenchmarkParentFiltered-4 50000 35596 ns/op 7141 B/op 394 allocs/op
|
||||
BenchmarkParents-4 20000 62094 ns/op 30720 B/op 837 allocs/op
|
||||
BenchmarkParentsFiltered-4 20000 63223 ns/op 31304 B/op 845 allocs/op
|
||||
BenchmarkParentsUntil-4 50000 30391 ns/op 11828 B/op 358 allocs/op
|
||||
BenchmarkParentsUntilSelection-4 20000 99962 ns/op 54075 B/op 1523 allocs/op
|
||||
BenchmarkParentsUntilNodes-4 20000 98763 ns/op 54073 B/op 1523 allocs/op
|
||||
BenchmarkParentsFilteredUntil-4 200000 7982 ns/op 2787 B/op 88 allocs/op
|
||||
BenchmarkParentsFilteredUntilSelection-4 100000 13618 ns/op 5995 B/op 192 allocs/op
|
||||
BenchmarkParentsFilteredUntilNodes-4 100000 13639 ns/op 5994 B/op 192 allocs/op
|
||||
BenchmarkSiblings-4 20000 75287 ns/op 28453 B/op 225 allocs/op
|
||||
BenchmarkSiblingsFiltered-4 20000 80139 ns/op 29543 B/op 234 allocs/op
|
||||
BenchmarkNext-4 100000 14270 ns/op 4659 B/op 117 allocs/op
|
||||
BenchmarkNextFiltered-4 100000 15352 ns/op 4860 B/op 123 allocs/op
|
||||
BenchmarkNextAll-4 20000 60811 ns/op 22771 B/op 157 allocs/op
|
||||
BenchmarkNextAllFiltered-4 20000 69079 ns/op 23871 B/op 166 allocs/op
|
||||
BenchmarkPrev-4 100000 14417 ns/op 4659 B/op 117 allocs/op
|
||||
BenchmarkPrevFiltered-4 100000 15443 ns/op 4859 B/op 123 allocs/op
|
||||
BenchmarkPrevAll-4 100000 22008 ns/op 7346 B/op 120 allocs/op
|
||||
BenchmarkPrevAllFiltered-4 100000 23212 ns/op 7544 B/op 126 allocs/op
|
||||
BenchmarkNextUntil-4 50000 30589 ns/op 8767 B/op 267 allocs/op
|
||||
BenchmarkNextUntilSelection-4 30000 40875 ns/op 19862 B/op 546 allocs/op
|
||||
BenchmarkNextUntilNodes-4 100000 15987 ns/op 8134 B/op 249 allocs/op
|
||||
BenchmarkPrevUntil-4 20000 98799 ns/op 25727 B/op 467 allocs/op
|
||||
BenchmarkPrevUntilSelection-4 30000 51874 ns/op 24875 B/op 694 allocs/op
|
||||
BenchmarkPrevUntilNodes-4 100000 12901 ns/op 6334 B/op 204 allocs/op
|
||||
BenchmarkNextFilteredUntil-4 100000 19869 ns/op 5909 B/op 177 allocs/op
|
||||
BenchmarkNextFilteredUntilSelection-4 30000 45412 ns/op 20557 B/op 571 allocs/op
|
||||
BenchmarkNextFilteredUntilNodes-4 30000 45363 ns/op 20557 B/op 571 allocs/op
|
||||
BenchmarkPrevFilteredUntil-4 100000 19357 ns/op 6033 B/op 179 allocs/op
|
||||
BenchmarkPrevFilteredUntilSelection-4 30000 46396 ns/op 21305 B/op 591 allocs/op
|
||||
BenchmarkPrevFilteredUntilNodes-4 30000 46133 ns/op 21305 B/op 591 allocs/op
|
||||
BenchmarkClosest-4 500000 3448 ns/op 160 B/op 8 allocs/op
|
||||
BenchmarkClosestSelection-4 3000000 528 ns/op 96 B/op 6 allocs/op
|
||||
BenchmarkClosestNodes-4 3000000 523 ns/op 96 B/op 6 allocs/op
|
||||
PASS
|
||||
ok github.com/PuerkitoBio/goquery 162.012s
|
||||
@@ -0,0 +1,86 @@
|
||||
BenchmarkFirst-4 30000000 51.7 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkLast-4 30000000 51.9 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkEq-4 30000000 50.0 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkSlice-4 500000000 3.47 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkGet-4 2000000000 1.68 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndex-4 2000000 804 ns/op 248 B/op 10 allocs/op
|
||||
BenchmarkIndexSelector-4 100000 16285 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkIndexOfNode-4 200000000 6.50 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkIndexOfSelection-4 200000000 7.02 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkMetalReviewExample-4 10000 143160 ns/op 12417 B/op 320 allocs/op
|
||||
BenchmarkAdd-4 200000 10326 ns/op 208 B/op 9 allocs/op
|
||||
BenchmarkAddSelection-4 10000000 155 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAddNodes-4 10000000 156 ns/op 48 B/op 1 allocs/op
|
||||
BenchmarkAddNodesBig-4 20000 94439 ns/op 21847 B/op 37 allocs/op
|
||||
BenchmarkAndSelf-4 1000000 1791 ns/op 1008 B/op 5 allocs/op
|
||||
BenchmarkFilter-4 100000 19470 ns/op 360 B/op 8 allocs/op
|
||||
BenchmarkNot-4 100000 22500 ns/op 136 B/op 5 allocs/op
|
||||
BenchmarkFilterFunction-4 50000 34578 ns/op 22976 B/op 755 allocs/op
|
||||
BenchmarkNotFunction-4 50000 38703 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterNodes-4 50000 34486 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotNodes-4 30000 41094 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkFilterSelection-4 50000 33623 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkNotSelection-4 30000 41483 ns/op 29120 B/op 757 allocs/op
|
||||
BenchmarkHas-4 5000 266628 ns/op 2371 B/op 50 allocs/op
|
||||
BenchmarkHasNodes-4 10000 152617 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkHasSelection-4 10000 156682 ns/op 21184 B/op 752 allocs/op
|
||||
BenchmarkEnd-4 2000000000 1.00 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkEach-4 300000 4712 ns/op 3304 B/op 118 allocs/op
|
||||
BenchmarkMap-4 200000 8434 ns/op 5572 B/op 184 allocs/op
|
||||
BenchmarkEachWithBreak-4 2000000 819 ns/op 560 B/op 20 allocs/op
|
||||
BenchmarkAttr-4 100000000 21.7 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkText-4 200000 9376 ns/op 7536 B/op 110 allocs/op
|
||||
BenchmarkLength-4 2000000000 0.35 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkHtml-4 5000000 401 ns/op 120 B/op 2 allocs/op
|
||||
BenchmarkIs-4 100000 22214 ns/op 88 B/op 4 allocs/op
|
||||
BenchmarkIsPositional-4 50000 26559 ns/op 1112 B/op 10 allocs/op
|
||||
BenchmarkIsFunction-4 1000000 1228 ns/op 784 B/op 28 allocs/op
|
||||
BenchmarkIsSelection-4 50000 33471 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkIsNodes-4 50000 34461 ns/op 20960 B/op 749 allocs/op
|
||||
BenchmarkHasClass-4 10000 232429 ns/op 14944 B/op 976 allocs/op
|
||||
BenchmarkContains-4 200000000 7.62 ns/op 0 B/op 0 allocs/op
|
||||
BenchmarkFind-4 100000 16114 ns/op 3839 B/op 21 allocs/op
|
||||
BenchmarkFindWithinSelection-4 30000 42520 ns/op 3540 B/op 82 allocs/op
|
||||
BenchmarkFindSelection-4 10000 209801 ns/op 5615 B/op 89 allocs/op
|
||||
BenchmarkFindNodes-4 10000 209082 ns/op 5614 B/op 89 allocs/op
|
||||
BenchmarkContents-4 300000 4836 ns/op 1420 B/op 36 allocs/op
|
||||
BenchmarkContentsFiltered-4 200000 5495 ns/op 1570 B/op 41 allocs/op
|
||||
BenchmarkChildren-4 3000000 527 ns/op 152 B/op 7 allocs/op
|
||||
BenchmarkChildrenFiltered-4 500000 2499 ns/op 352 B/op 15 allocs/op
|
||||
BenchmarkParent-4 50000 34072 ns/op 6942 B/op 387 allocs/op
|
||||
BenchmarkParentFiltered-4 50000 36077 ns/op 7141 B/op 394 allocs/op
|
||||
BenchmarkParents-4 20000 64118 ns/op 30719 B/op 837 allocs/op
|
||||
BenchmarkParentsFiltered-4 20000 63432 ns/op 31303 B/op 845 allocs/op
|
||||
BenchmarkParentsUntil-4 50000 29589 ns/op 11829 B/op 358 allocs/op
|
||||
BenchmarkParentsUntilSelection-4 10000 101033 ns/op 54076 B/op 1523 allocs/op
|
||||
BenchmarkParentsUntilNodes-4 10000 100584 ns/op 54076 B/op 1523 allocs/op
|
||||
BenchmarkParentsFilteredUntil-4 200000 8061 ns/op 2787 B/op 88 allocs/op
|
||||
BenchmarkParentsFilteredUntilSelection-4 100000 13848 ns/op 5995 B/op 192 allocs/op
|
||||
BenchmarkParentsFilteredUntilNodes-4 100000 13766 ns/op 5995 B/op 192 allocs/op
|
||||
BenchmarkSiblings-4 20000 75135 ns/op 28453 B/op 225 allocs/op
|
||||
BenchmarkSiblingsFiltered-4 20000 80532 ns/op 29544 B/op 234 allocs/op
|
||||
BenchmarkNext-4 100000 14200 ns/op 4660 B/op 117 allocs/op
|
||||
BenchmarkNextFiltered-4 100000 15284 ns/op 4859 B/op 123 allocs/op
|
||||
BenchmarkNextAll-4 20000 60889 ns/op 22774 B/op 157 allocs/op
|
||||
BenchmarkNextAllFiltered-4 20000 65125 ns/op 23869 B/op 166 allocs/op
|
||||
BenchmarkPrev-4 100000 14448 ns/op 4659 B/op 117 allocs/op
|
||||
BenchmarkPrevFiltered-4 100000 15444 ns/op 4859 B/op 123 allocs/op
|
||||
BenchmarkPrevAll-4 100000 22019 ns/op 7344 B/op 120 allocs/op
|
||||
BenchmarkPrevAllFiltered-4 100000 23307 ns/op 7545 B/op 126 allocs/op
|
||||
BenchmarkNextUntil-4 50000 30287 ns/op 8766 B/op 267 allocs/op
|
||||
BenchmarkNextUntilSelection-4 30000 41476 ns/op 19862 B/op 546 allocs/op
|
||||
BenchmarkNextUntilNodes-4 100000 16106 ns/op 8133 B/op 249 allocs/op
|
||||
BenchmarkPrevUntil-4 20000 98951 ns/op 25728 B/op 467 allocs/op
|
||||
BenchmarkPrevUntilSelection-4 30000 52390 ns/op 24875 B/op 694 allocs/op
|
||||
BenchmarkPrevUntilNodes-4 100000 12986 ns/op 6334 B/op 204 allocs/op
|
||||
BenchmarkNextFilteredUntil-4 100000 19365 ns/op 5908 B/op 177 allocs/op
|
||||
BenchmarkNextFilteredUntilSelection-4 30000 45334 ns/op 20555 B/op 571 allocs/op
|
||||
BenchmarkNextFilteredUntilNodes-4 30000 45292 ns/op 20556 B/op 571 allocs/op
|
||||
BenchmarkPrevFilteredUntil-4 100000 19412 ns/op 6032 B/op 179 allocs/op
|
||||
BenchmarkPrevFilteredUntilSelection-4 30000 46286 ns/op 21304 B/op 591 allocs/op
|
||||
BenchmarkPrevFilteredUntilNodes-4 30000 46554 ns/op 21305 B/op 591 allocs/op
|
||||
BenchmarkClosest-4 500000 3480 ns/op 160 B/op 8 allocs/op
|
||||
BenchmarkClosestSelection-4 2000000 722 ns/op 96 B/op 6 allocs/op
|
||||
BenchmarkClosestNodes-4 2000000 719 ns/op 96 B/op 6 allocs/op
|
||||
PASS
|
||||
ok github.com/PuerkitoBio/goquery 160.565s
|
||||
+12
-4
@@ -70,7 +70,9 @@ func BenchmarkIndex(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.Index()
|
||||
}
|
||||
b.Logf("Index=%d", j)
|
||||
if j != 3 {
|
||||
b.Fatalf("want 3, got %d", j)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexSelector(b *testing.B) {
|
||||
@@ -82,7 +84,9 @@ func BenchmarkIndexSelector(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexSelector("dd")
|
||||
}
|
||||
b.Logf("IndexSelector=%d", j)
|
||||
if j != 4 {
|
||||
b.Fatalf("want 4, got %d", j)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexOfNode(b *testing.B) {
|
||||
@@ -96,7 +100,9 @@ func BenchmarkIndexOfNode(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexOfNode(n)
|
||||
}
|
||||
b.Logf("IndexOfNode=%d", j)
|
||||
if j != 2 {
|
||||
b.Fatalf("want 2, got %d", j)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIndexOfSelection(b *testing.B) {
|
||||
@@ -108,5 +114,7 @@ func BenchmarkIndexOfSelection(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
j = sel.IndexOfSelection(sel2)
|
||||
}
|
||||
b.Logf("IndexOfSelection=%d", j)
|
||||
if j != 2 {
|
||||
b.Fatalf("want 2, got %d", j)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,4 @@ func BenchmarkMetalReviewExample(b *testing.B) {
|
||||
}
|
||||
})
|
||||
}
|
||||
b.Log(buf.String())
|
||||
b.Logf("MetalReviewExample=%d", n)
|
||||
}
|
||||
|
||||
+36
-4
@@ -17,7 +17,9 @@ func BenchmarkAdd(b *testing.B) {
|
||||
sel.Add("h2[title]")
|
||||
}
|
||||
}
|
||||
b.Logf("Add=%d", n)
|
||||
if n != 43 {
|
||||
b.Fatalf("want 43, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddSelection(b *testing.B) {
|
||||
@@ -34,7 +36,9 @@ func BenchmarkAddSelection(b *testing.B) {
|
||||
sel.AddSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("AddSelection=%d", n)
|
||||
if n != 43 {
|
||||
b.Fatalf("want 43, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddNodes(b *testing.B) {
|
||||
@@ -52,7 +56,33 @@ func BenchmarkAddNodes(b *testing.B) {
|
||||
sel.AddNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("AddNodes=%d", n)
|
||||
if n != 43 {
|
||||
b.Fatalf("want 43, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAddNodesBig(b *testing.B) {
|
||||
var n int
|
||||
|
||||
doc := DocW()
|
||||
sel := doc.Find("li")
|
||||
// make nodes > 1000
|
||||
nodes := sel.Nodes
|
||||
nodes = append(nodes, nodes...)
|
||||
nodes = append(nodes, nodes...)
|
||||
sel = doc.Find("xyz")
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
if n == 0 {
|
||||
n = sel.AddNodes(nodes...).Length()
|
||||
} else {
|
||||
sel.AddNodes(nodes...)
|
||||
}
|
||||
}
|
||||
if n != 373 {
|
||||
b.Fatalf("want 373, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAndSelf(b *testing.B) {
|
||||
@@ -68,5 +98,7 @@ func BenchmarkAndSelf(b *testing.B) {
|
||||
sel.AndSelf()
|
||||
}
|
||||
}
|
||||
b.Logf("AndSelf=%d", n)
|
||||
if n != 44 {
|
||||
b.Fatalf("want 44, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
+36
-12
@@ -17,7 +17,9 @@ func BenchmarkFilter(b *testing.B) {
|
||||
sel.Filter(".toclevel-1")
|
||||
}
|
||||
}
|
||||
b.Logf("Filter=%d", n)
|
||||
if n != 13 {
|
||||
b.Fatalf("want 13, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNot(b *testing.B) {
|
||||
@@ -33,7 +35,9 @@ func BenchmarkNot(b *testing.B) {
|
||||
sel.Filter(".toclevel-2")
|
||||
}
|
||||
}
|
||||
b.Logf("Not=%d", n)
|
||||
if n != 371 {
|
||||
b.Fatalf("want 371, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilterFunction(b *testing.B) {
|
||||
@@ -52,7 +56,9 @@ func BenchmarkFilterFunction(b *testing.B) {
|
||||
sel.FilterFunction(f)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterFunction=%d", n)
|
||||
if n != 112 {
|
||||
b.Fatalf("want 112, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNotFunction(b *testing.B) {
|
||||
@@ -71,7 +77,9 @@ func BenchmarkNotFunction(b *testing.B) {
|
||||
sel.NotFunction(f)
|
||||
}
|
||||
}
|
||||
b.Logf("NotFunction=%d", n)
|
||||
if n != 261 {
|
||||
b.Fatalf("want 261, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilterNodes(b *testing.B) {
|
||||
@@ -89,7 +97,9 @@ func BenchmarkFilterNodes(b *testing.B) {
|
||||
sel.FilterNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterNodes=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNotNodes(b *testing.B) {
|
||||
@@ -107,7 +117,9 @@ func BenchmarkNotNodes(b *testing.B) {
|
||||
sel.NotNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NotNodes=%d", n)
|
||||
if n != 360 {
|
||||
b.Fatalf("want 360, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFilterSelection(b *testing.B) {
|
||||
@@ -124,7 +136,9 @@ func BenchmarkFilterSelection(b *testing.B) {
|
||||
sel.FilterSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("FilterSelection=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNotSelection(b *testing.B) {
|
||||
@@ -141,7 +155,9 @@ func BenchmarkNotSelection(b *testing.B) {
|
||||
sel.NotSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NotSelection=%d", n)
|
||||
if n != 360 {
|
||||
b.Fatalf("want 360, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHas(b *testing.B) {
|
||||
@@ -157,7 +173,9 @@ func BenchmarkHas(b *testing.B) {
|
||||
sel.Has(".editsection")
|
||||
}
|
||||
}
|
||||
b.Logf("Has=%d", n)
|
||||
if n != 13 {
|
||||
b.Fatalf("want 13, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHasNodes(b *testing.B) {
|
||||
@@ -175,7 +193,9 @@ func BenchmarkHasNodes(b *testing.B) {
|
||||
sel.HasNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("HasNodes=%d", n)
|
||||
if n != 15 {
|
||||
b.Fatalf("want 15, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHasSelection(b *testing.B) {
|
||||
@@ -192,7 +212,9 @@ func BenchmarkHasSelection(b *testing.B) {
|
||||
sel.HasSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("HasSelection=%d", n)
|
||||
if n != 15 {
|
||||
b.Fatalf("want 15, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEnd(b *testing.B) {
|
||||
@@ -208,5 +230,7 @@ func BenchmarkEnd(b *testing.B) {
|
||||
sel.End()
|
||||
}
|
||||
}
|
||||
b.Logf("End=%d", n)
|
||||
if n != 373 {
|
||||
b.Fatalf("want 373, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
+11
-4
@@ -1,6 +1,7 @@
|
||||
package goquery
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -19,7 +20,9 @@ func BenchmarkEach(b *testing.B) {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Each=%d", n)
|
||||
if n != 59 {
|
||||
b.Fatalf("want 59, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMap(b *testing.B) {
|
||||
@@ -29,7 +32,7 @@ func BenchmarkMap(b *testing.B) {
|
||||
sel := DocW().Find("td")
|
||||
f := func(i int, s *Selection) string {
|
||||
tmp++
|
||||
return string(tmp)
|
||||
return strconv.Itoa(tmp)
|
||||
}
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
@@ -38,7 +41,9 @@ func BenchmarkMap(b *testing.B) {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Map=%d", n)
|
||||
if n != 59 {
|
||||
b.Fatalf("want 59, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEachWithBreak(b *testing.B) {
|
||||
@@ -58,5 +63,7 @@ func BenchmarkEachWithBreak(b *testing.B) {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
b.Logf("Each=%d", n)
|
||||
if n != 10 {
|
||||
b.Fatalf("want 10, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,9 @@ func BenchmarkAttr(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
s, _ = sel.Attr("id")
|
||||
}
|
||||
b.Logf("Attr=%s", s)
|
||||
if s != "firstHeading" {
|
||||
b.Fatalf("want firstHeading, got %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkText(b *testing.B) {
|
||||
@@ -34,7 +36,9 @@ func BenchmarkLength(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
n = sel.Length()
|
||||
}
|
||||
b.Logf("Length=%d", n)
|
||||
if n != 14 {
|
||||
b.Fatalf("want 14, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHtml(b *testing.B) {
|
||||
|
||||
+21
-7
@@ -13,7 +13,9 @@ func BenchmarkIs(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Is(".toclevel-2")
|
||||
}
|
||||
b.Logf("Is=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsPositional(b *testing.B) {
|
||||
@@ -25,7 +27,9 @@ func BenchmarkIsPositional(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Is("li:nth-child(2)")
|
||||
}
|
||||
b.Logf("IsPositional=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsFunction(b *testing.B) {
|
||||
@@ -40,7 +44,9 @@ func BenchmarkIsFunction(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsFunction(f)
|
||||
}
|
||||
b.Logf("IsFunction=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsSelection(b *testing.B) {
|
||||
@@ -53,7 +59,9 @@ func BenchmarkIsSelection(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsSelection(sel2)
|
||||
}
|
||||
b.Logf("IsSelection=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkIsNodes(b *testing.B) {
|
||||
@@ -67,7 +75,9 @@ func BenchmarkIsNodes(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.IsNodes(nodes...)
|
||||
}
|
||||
b.Logf("IsNodes=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHasClass(b *testing.B) {
|
||||
@@ -79,7 +89,9 @@ func BenchmarkHasClass(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.HasClass("official")
|
||||
}
|
||||
b.Logf("HasClass=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkContains(b *testing.B) {
|
||||
@@ -93,5 +105,7 @@ func BenchmarkContains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
y = sel.Contains(node)
|
||||
}
|
||||
b.Logf("Contains=%v", y)
|
||||
if !y {
|
||||
b.Fatal("want true")
|
||||
}
|
||||
}
|
||||
|
||||
+129
-43
@@ -15,7 +15,9 @@ func BenchmarkFind(b *testing.B) {
|
||||
DocB().Find("dd")
|
||||
}
|
||||
}
|
||||
b.Logf("Find=%d", n)
|
||||
if n != 41 {
|
||||
b.Fatalf("want 41, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFindWithinSelection(b *testing.B) {
|
||||
@@ -31,7 +33,9 @@ func BenchmarkFindWithinSelection(b *testing.B) {
|
||||
sel.Find("a[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("FindWithinSelection=%d", n)
|
||||
if n != 39 {
|
||||
b.Fatalf("want 39, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFindSelection(b *testing.B) {
|
||||
@@ -48,7 +52,9 @@ func BenchmarkFindSelection(b *testing.B) {
|
||||
sel.FindSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("FindSelection=%d", n)
|
||||
if n != 73 {
|
||||
b.Fatalf("want 73, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkFindNodes(b *testing.B) {
|
||||
@@ -66,7 +72,9 @@ func BenchmarkFindNodes(b *testing.B) {
|
||||
sel.FindNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("FindNodes=%d", n)
|
||||
if n != 73 {
|
||||
b.Fatalf("want 73, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkContents(b *testing.B) {
|
||||
@@ -82,7 +90,9 @@ func BenchmarkContents(b *testing.B) {
|
||||
sel.Contents()
|
||||
}
|
||||
}
|
||||
b.Logf("Contents=%d", n)
|
||||
if n != 16 {
|
||||
b.Fatalf("want 16, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkContentsFiltered(b *testing.B) {
|
||||
@@ -98,7 +108,9 @@ func BenchmarkContentsFiltered(b *testing.B) {
|
||||
sel.ContentsFiltered("a[href=\"#Examples\"]")
|
||||
}
|
||||
}
|
||||
b.Logf("ContentsFiltered=%d", n)
|
||||
if n != 1 {
|
||||
b.Fatalf("want 1, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChildren(b *testing.B) {
|
||||
@@ -114,7 +126,9 @@ func BenchmarkChildren(b *testing.B) {
|
||||
sel.Children()
|
||||
}
|
||||
}
|
||||
b.Logf("Children=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkChildrenFiltered(b *testing.B) {
|
||||
@@ -130,7 +144,9 @@ func BenchmarkChildrenFiltered(b *testing.B) {
|
||||
sel.ChildrenFiltered(".editsection")
|
||||
}
|
||||
}
|
||||
b.Logf("ChildrenFiltered=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParent(b *testing.B) {
|
||||
@@ -146,7 +162,9 @@ func BenchmarkParent(b *testing.B) {
|
||||
sel.Parent()
|
||||
}
|
||||
}
|
||||
b.Logf("Parent=%d", n)
|
||||
if n != 55 {
|
||||
b.Fatalf("want 55, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentFiltered(b *testing.B) {
|
||||
@@ -162,7 +180,9 @@ func BenchmarkParentFiltered(b *testing.B) {
|
||||
sel.ParentFiltered("ul[id]")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentFiltered=%d", n)
|
||||
if n != 4 {
|
||||
b.Fatalf("want 4, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParents(b *testing.B) {
|
||||
@@ -178,7 +198,9 @@ func BenchmarkParents(b *testing.B) {
|
||||
sel.Parents()
|
||||
}
|
||||
}
|
||||
b.Logf("Parents=%d", n)
|
||||
if n != 73 {
|
||||
b.Fatalf("want 73, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsFiltered(b *testing.B) {
|
||||
@@ -194,7 +216,9 @@ func BenchmarkParentsFiltered(b *testing.B) {
|
||||
sel.ParentsFiltered("tr")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFiltered=%d", n)
|
||||
if n != 18 {
|
||||
b.Fatalf("want 18, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntil(b *testing.B) {
|
||||
@@ -210,7 +234,9 @@ func BenchmarkParentsUntil(b *testing.B) {
|
||||
sel.ParentsUntil("table")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntil=%d", n)
|
||||
if n != 52 {
|
||||
b.Fatalf("want 52, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntilSelection(b *testing.B) {
|
||||
@@ -227,7 +253,9 @@ func BenchmarkParentsUntilSelection(b *testing.B) {
|
||||
sel.ParentsUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntilSelection=%d", n)
|
||||
if n != 70 {
|
||||
b.Fatalf("want 70, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsUntilNodes(b *testing.B) {
|
||||
@@ -245,7 +273,9 @@ func BenchmarkParentsUntilNodes(b *testing.B) {
|
||||
sel.ParentsUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsUntilNodes=%d", n)
|
||||
if n != 70 {
|
||||
b.Fatalf("want 70, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntil(b *testing.B) {
|
||||
@@ -261,7 +291,9 @@ func BenchmarkParentsFilteredUntil(b *testing.B) {
|
||||
sel.ParentsFilteredUntil(":nth-child(1)", "ul")
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntil=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
|
||||
@@ -278,7 +310,9 @@ func BenchmarkParentsFilteredUntilSelection(b *testing.B) {
|
||||
sel.ParentsFilteredUntilSelection(":nth-child(1)", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntilSelection=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
|
||||
@@ -296,7 +330,9 @@ func BenchmarkParentsFilteredUntilNodes(b *testing.B) {
|
||||
sel.ParentsFilteredUntilNodes(":nth-child(1)", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ParentsFilteredUntilNodes=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSiblings(b *testing.B) {
|
||||
@@ -312,7 +348,9 @@ func BenchmarkSiblings(b *testing.B) {
|
||||
sel.Siblings()
|
||||
}
|
||||
}
|
||||
b.Logf("Siblings=%d", n)
|
||||
if n != 293 {
|
||||
b.Fatalf("want 293, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSiblingsFiltered(b *testing.B) {
|
||||
@@ -328,7 +366,9 @@ func BenchmarkSiblingsFiltered(b *testing.B) {
|
||||
sel.SiblingsFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("SiblingsFiltered=%d", n)
|
||||
if n != 46 {
|
||||
b.Fatalf("want 46, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNext(b *testing.B) {
|
||||
@@ -344,7 +384,9 @@ func BenchmarkNext(b *testing.B) {
|
||||
sel.Next()
|
||||
}
|
||||
}
|
||||
b.Logf("Next=%d", n)
|
||||
if n != 49 {
|
||||
b.Fatalf("want 49, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextFiltered(b *testing.B) {
|
||||
@@ -360,7 +402,9 @@ func BenchmarkNextFiltered(b *testing.B) {
|
||||
sel.NextFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("NextFiltered=%d", n)
|
||||
if n != 6 {
|
||||
b.Fatalf("want 6, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextAll(b *testing.B) {
|
||||
@@ -376,7 +420,9 @@ func BenchmarkNextAll(b *testing.B) {
|
||||
sel.NextAll()
|
||||
}
|
||||
}
|
||||
b.Logf("NextAll=%d", n)
|
||||
if n != 234 {
|
||||
b.Fatalf("want 234, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextAllFiltered(b *testing.B) {
|
||||
@@ -392,7 +438,9 @@ func BenchmarkNextAllFiltered(b *testing.B) {
|
||||
sel.NextAllFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("NextAllFiltered=%d", n)
|
||||
if n != 33 {
|
||||
b.Fatalf("want 33, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrev(b *testing.B) {
|
||||
@@ -408,7 +456,9 @@ func BenchmarkPrev(b *testing.B) {
|
||||
sel.Prev()
|
||||
}
|
||||
}
|
||||
b.Logf("Prev=%d", n)
|
||||
if n != 49 {
|
||||
b.Fatalf("want 49, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevFiltered(b *testing.B) {
|
||||
@@ -426,7 +476,9 @@ func BenchmarkPrevFiltered(b *testing.B) {
|
||||
}
|
||||
// There is one more Prev li with a class, compared to Next li with a class
|
||||
// (confirmed by looking at the HTML, this is ok)
|
||||
b.Logf("PrevFiltered=%d", n)
|
||||
if n != 7 {
|
||||
b.Fatalf("want 7, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevAll(b *testing.B) {
|
||||
@@ -442,7 +494,9 @@ func BenchmarkPrevAll(b *testing.B) {
|
||||
sel.PrevAll()
|
||||
}
|
||||
}
|
||||
b.Logf("PrevAll=%d", n)
|
||||
if n != 78 {
|
||||
b.Fatalf("want 78, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevAllFiltered(b *testing.B) {
|
||||
@@ -458,7 +512,9 @@ func BenchmarkPrevAllFiltered(b *testing.B) {
|
||||
sel.PrevAllFiltered("[class]")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevAllFiltered=%d", n)
|
||||
if n != 6 {
|
||||
b.Fatalf("want 6, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextUntil(b *testing.B) {
|
||||
@@ -474,7 +530,9 @@ func BenchmarkNextUntil(b *testing.B) {
|
||||
sel.NextUntil(":nth-child(4)")
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntil=%d", n)
|
||||
if n != 84 {
|
||||
b.Fatalf("want 84, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextUntilSelection(b *testing.B) {
|
||||
@@ -491,7 +549,9 @@ func BenchmarkNextUntilSelection(b *testing.B) {
|
||||
sel.NextUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntilSelection=%d", n)
|
||||
if n != 42 {
|
||||
b.Fatalf("want 42, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextUntilNodes(b *testing.B) {
|
||||
@@ -509,7 +569,9 @@ func BenchmarkNextUntilNodes(b *testing.B) {
|
||||
sel.NextUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NextUntilNodes=%d", n)
|
||||
if n != 12 {
|
||||
b.Fatalf("want 12, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntil(b *testing.B) {
|
||||
@@ -525,7 +587,9 @@ func BenchmarkPrevUntil(b *testing.B) {
|
||||
sel.PrevUntil(":nth-child(4)")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntil=%d", n)
|
||||
if n != 238 {
|
||||
b.Fatalf("want 238, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntilSelection(b *testing.B) {
|
||||
@@ -542,7 +606,9 @@ func BenchmarkPrevUntilSelection(b *testing.B) {
|
||||
sel.PrevUntilSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntilSelection=%d", n)
|
||||
if n != 49 {
|
||||
b.Fatalf("want 49, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevUntilNodes(b *testing.B) {
|
||||
@@ -560,7 +626,9 @@ func BenchmarkPrevUntilNodes(b *testing.B) {
|
||||
sel.PrevUntilNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevUntilNodes=%d", n)
|
||||
if n != 11 {
|
||||
b.Fatalf("want 11, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntil(b *testing.B) {
|
||||
@@ -576,7 +644,9 @@ func BenchmarkNextFilteredUntil(b *testing.B) {
|
||||
sel.NextFilteredUntil("p", "div")
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntil=%d", n)
|
||||
if n != 22 {
|
||||
b.Fatalf("want 22, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntilSelection(b *testing.B) {
|
||||
@@ -593,7 +663,9 @@ func BenchmarkNextFilteredUntilSelection(b *testing.B) {
|
||||
sel.NextFilteredUntilSelection("p", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntilSelection=%d", n)
|
||||
if n != 22 {
|
||||
b.Fatalf("want 22, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkNextFilteredUntilNodes(b *testing.B) {
|
||||
@@ -611,7 +683,9 @@ func BenchmarkNextFilteredUntilNodes(b *testing.B) {
|
||||
sel.NextFilteredUntilNodes("p", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("NextFilteredUntilNodes=%d", n)
|
||||
if n != 22 {
|
||||
b.Fatalf("want 22, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntil(b *testing.B) {
|
||||
@@ -627,7 +701,9 @@ func BenchmarkPrevFilteredUntil(b *testing.B) {
|
||||
sel.PrevFilteredUntil("p", "div")
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntil=%d", n)
|
||||
if n != 20 {
|
||||
b.Fatalf("want 20, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
|
||||
@@ -644,7 +720,9 @@ func BenchmarkPrevFilteredUntilSelection(b *testing.B) {
|
||||
sel.PrevFilteredUntilSelection("p", sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntilSelection=%d", n)
|
||||
if n != 20 {
|
||||
b.Fatalf("want 20, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
|
||||
@@ -662,7 +740,9 @@ func BenchmarkPrevFilteredUntilNodes(b *testing.B) {
|
||||
sel.PrevFilteredUntilNodes("p", nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("PrevFilteredUntilNodes=%d", n)
|
||||
if n != 20 {
|
||||
b.Fatalf("want 20, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkClosest(b *testing.B) {
|
||||
@@ -678,7 +758,9 @@ func BenchmarkClosest(b *testing.B) {
|
||||
sel.Closest(".pvk-content")
|
||||
}
|
||||
}
|
||||
b.Logf("Closest=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkClosestSelection(b *testing.B) {
|
||||
@@ -695,7 +777,9 @@ func BenchmarkClosestSelection(b *testing.B) {
|
||||
sel.ClosestSelection(sel2)
|
||||
}
|
||||
}
|
||||
b.Logf("ClosestSelection=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkClosestNodes(b *testing.B) {
|
||||
@@ -712,5 +796,7 @@ func BenchmarkClosestNodes(b *testing.B) {
|
||||
sel.ClosestNodes(nodes...)
|
||||
}
|
||||
}
|
||||
b.Logf("ClosestNodes=%d", n)
|
||||
if n != 2 {
|
||||
b.Fatalf("want 2, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -4,16 +4,16 @@
|
||||
|
||||
The `go.net/html` package used by `goquery` requires that the html document is UTF-8 encoded. When you know the encoding of the html page is not UTF-8, you can use the `iconv` package to convert it to UTF-8 (there are various implementation of the `iconv` API, see [godoc.org][iconv] for other options):
|
||||
|
||||
```
|
||||
```bash
|
||||
$ go get -u github.com/djimenez/iconv-go
|
||||
```
|
||||
|
||||
and then:
|
||||
|
||||
```
|
||||
```golang
|
||||
// Load the URL
|
||||
res, err := http.Get(url)
|
||||
if e != nil {
|
||||
if err != nil {
|
||||
// handle error
|
||||
}
|
||||
defer res.Body.Close()
|
||||
@@ -51,7 +51,7 @@ You can find a code example using `otto` [in this gist][exotto]. Thanks to githu
|
||||
|
||||
If all you need is a normal `for` loop over all nodes in the current selection, where `Map/Each`-style iteration is not necessary, you can use the following:
|
||||
|
||||
```
|
||||
```golang
|
||||
sel := Doc().Find(".selector")
|
||||
for i := range sel.Nodes {
|
||||
single := sel.Eq(i)
|
||||
@@ -65,4 +65,4 @@ Thanks to github user @jmoiron.
|
||||
[otto]: https://github.com/robertkrimen/otto
|
||||
[exotto]: https://gist.github.com/cryptix/87127f76a94183747b53
|
||||
[iconv]: http://godoc.org/?q=iconv
|
||||
[text]: http://godoc.org/code.google.com/p/go.text/encoding
|
||||
[text]: https://godoc.org/golang.org/x/text/encoding
|
||||
|
||||
+55
-3
@@ -3,22 +3,35 @@ package goquery_test
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// This example scrapes the reviews shown on the home page of metalsucks.net.
|
||||
func Example() {
|
||||
// Request the HTML page.
|
||||
res, err := http.Get("http://metalsucks.net")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode != 200 {
|
||||
log.Fatalf("status code error: %d %s", res.StatusCode, res.Status)
|
||||
}
|
||||
|
||||
// Load the HTML document
|
||||
doc, err := goquery.NewDocument("http://metalsucks.net")
|
||||
doc, err := goquery.NewDocumentFromReader(res.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Find the review items
|
||||
doc.Find(".reviews-wrap article .review-rhs").Each(func(i int, s *goquery.Selection) {
|
||||
doc.Find(".sidebar-reviews article .content-block").Each(func(i int, s *goquery.Selection) {
|
||||
// For each item found, get the band and title
|
||||
band := s.Find("h3").Text()
|
||||
band := s.Find("a").Text()
|
||||
title := s.Find("i").Text()
|
||||
fmt.Printf("Review %d: %s - %s\n", i, band, title)
|
||||
})
|
||||
@@ -28,3 +41,42 @@ func Example() {
|
||||
|
||||
// xOutput: voluntarily fail the Example output.
|
||||
}
|
||||
|
||||
// This example shows how to use NewDocumentFromReader from a file.
|
||||
func ExampleNewDocumentFromReader_file() {
|
||||
// create from a file
|
||||
f, err := os.Open("some/file.html")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
doc, err := goquery.NewDocumentFromReader(f)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// use the goquery document...
|
||||
_ = doc.Find("h1")
|
||||
}
|
||||
|
||||
// This example shows how to use NewDocumentFromReader from a string.
|
||||
func ExampleNewDocumentFromReader_string() {
|
||||
// create from a string
|
||||
data := `
|
||||
<html>
|
||||
<head>
|
||||
<title>My document</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Header</h1>
|
||||
</body>
|
||||
</html>`
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(strings.NewReader(data))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
header := doc.Find("h1").Text()
|
||||
fmt.Println(header)
|
||||
|
||||
// Output: Header
|
||||
}
|
||||
|
||||
@@ -35,12 +35,36 @@ func (s *Selection) Union(sel *Selection) *Selection {
|
||||
// AddNodes adds the specified nodes to those in the
|
||||
// current selection and returns a new Selection object.
|
||||
func (s *Selection) AddNodes(nodes ...*html.Node) *Selection {
|
||||
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes))
|
||||
return pushStack(s, appendWithoutDuplicates(s.Nodes, nodes, nil))
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
@@ -66,6 +66,24 @@ func TestAddNodesRollback(t *testing.T) {
|
||||
assertEqual(t, sel, sel2)
|
||||
}
|
||||
|
||||
func TestAddNodesBig(t *testing.T) {
|
||||
doc := DocW()
|
||||
sel := doc.Find("li")
|
||||
assertLength(t, sel.Nodes, 373)
|
||||
sel2 := doc.Find("xyz")
|
||||
assertLength(t, sel2.Nodes, 0)
|
||||
|
||||
nodes := sel.Nodes
|
||||
sel2 = sel2.AddNodes(nodes...)
|
||||
assertLength(t, sel2.Nodes, 373)
|
||||
nodes2 := append(nodes, nodes...)
|
||||
sel2 = sel2.End().AddNodes(nodes2...)
|
||||
assertLength(t, sel2.Nodes, 373)
|
||||
nodes3 := append(nodes2, nodes...)
|
||||
sel2 = sel2.End().AddNodes(nodes3...)
|
||||
assertLength(t, sel2.Nodes, 373)
|
||||
}
|
||||
|
||||
func TestAndSelf(t *testing.T) {
|
||||
sel := Doc().Find(".span12").Last().AndSelf()
|
||||
assertLength(t, sel.Nodes, 2)
|
||||
@@ -76,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)
|
||||
}
|
||||
|
||||
@@ -139,8 +139,18 @@ func winnow(sel *Selection, m Matcher, keep bool) []*html.Node {
|
||||
// Filter based on an array of nodes, and the indicator to keep (Filter) or
|
||||
// to get rid of (Not) the matching elements.
|
||||
func winnowNodes(sel *Selection, nodes []*html.Node, keep bool) []*html.Node {
|
||||
if len(nodes)+len(sel.Nodes) < minNodesForSet {
|
||||
return grep(sel, func(i int, s *Selection) bool {
|
||||
return isInSlice(nodes, s.Get(0)) == keep
|
||||
})
|
||||
}
|
||||
|
||||
set := make(map[*html.Node]bool)
|
||||
for _, n := range nodes {
|
||||
set[n] = true
|
||||
}
|
||||
return grep(sel, func(i int, s *Selection) bool {
|
||||
return isInSlice(nodes, s.Get(0)) == keep
|
||||
return set[s.Get(0)] == keep
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module github.com/PuerkitoBio/goquery
|
||||
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.1.0
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2
|
||||
)
|
||||
|
||||
go 1.13
|
||||
@@ -0,0 +1,8 @@
|
||||
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
|
||||
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
+143
-20
@@ -39,8 +39,14 @@ func (s *Selection) AfterSelection(sel *Selection) *Selection {
|
||||
// AfterHtml parses the html and inserts it after the set of matched elements.
|
||||
//
|
||||
// This follows the same rules as Selection.Append.
|
||||
func (s *Selection) AfterHtml(html string) *Selection {
|
||||
return s.AfterNodes(parseHtml(html)...)
|
||||
func (s *Selection) AfterHtml(htmlStr string) *Selection {
|
||||
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
if node.Parent != nil {
|
||||
node.Parent.InsertBefore(n, node.NextSibling)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AfterNodes inserts the nodes after each element in the set of matched elements.
|
||||
@@ -85,8 +91,12 @@ func (s *Selection) AppendSelection(sel *Selection) *Selection {
|
||||
}
|
||||
|
||||
// AppendHtml parses the html and appends it to the set of matched elements.
|
||||
func (s *Selection) AppendHtml(html string) *Selection {
|
||||
return s.AppendNodes(parseHtml(html)...)
|
||||
func (s *Selection) AppendHtml(htmlStr string) *Selection {
|
||||
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
node.AppendChild(n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// AppendNodes appends the specified nodes to each node in the set of matched elements.
|
||||
@@ -123,8 +133,14 @@ func (s *Selection) BeforeSelection(sel *Selection) *Selection {
|
||||
// BeforeHtml parses the html and inserts it before the set of matched elements.
|
||||
//
|
||||
// This follows the same rules as Selection.Append.
|
||||
func (s *Selection) BeforeHtml(html string) *Selection {
|
||||
return s.BeforeNodes(parseHtml(html)...)
|
||||
func (s *Selection) BeforeHtml(htmlStr string) *Selection {
|
||||
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
if node.Parent != nil {
|
||||
node.Parent.InsertBefore(n, node)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// BeforeNodes inserts the nodes before each element in the set of matched elements.
|
||||
@@ -184,8 +200,12 @@ func (s *Selection) PrependSelection(sel *Selection) *Selection {
|
||||
}
|
||||
|
||||
// PrependHtml parses the html and prepends it to the set of matched elements.
|
||||
func (s *Selection) PrependHtml(html string) *Selection {
|
||||
return s.PrependNodes(parseHtml(html)...)
|
||||
func (s *Selection) PrependHtml(htmlStr string) *Selection {
|
||||
return s.eachNodeHtml(htmlStr, false, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
node.InsertBefore(n, node.FirstChild)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// PrependNodes prepends the specified nodes to each node in the set of
|
||||
@@ -212,14 +232,19 @@ func (s *Selection) Remove() *Selection {
|
||||
return s
|
||||
}
|
||||
|
||||
// RemoveFiltered removes the set of matched elements by selector.
|
||||
// It returns the Selection of removed nodes.
|
||||
// RemoveFiltered removes from the current set of matched elements those that
|
||||
// match the selector filter. It returns the Selection of removed nodes.
|
||||
//
|
||||
// For example if the selection s contains "<h1>", "<h2>" and "<h3>"
|
||||
// and s.RemoveFiltered("h2") is called, only the "<h2>" node is removed
|
||||
// (and returned), while "<h1>" and "<h3>" are kept in the document.
|
||||
func (s *Selection) RemoveFiltered(selector string) *Selection {
|
||||
return s.RemoveMatcher(compileMatcher(selector))
|
||||
}
|
||||
|
||||
// RemoveMatcher removes the set of matched elements.
|
||||
// It returns the Selection of removed nodes.
|
||||
// RemoveMatcher removes from the current set of matched elements those that
|
||||
// match the Matcher filter. It returns the Selection of removed nodes.
|
||||
// See RemoveFiltered for additional information.
|
||||
func (s *Selection) RemoveMatcher(m Matcher) *Selection {
|
||||
return s.FilterMatcher(m).Remove()
|
||||
}
|
||||
@@ -256,8 +281,15 @@ func (s *Selection) ReplaceWithSelection(sel *Selection) *Selection {
|
||||
// It returns the removed elements.
|
||||
//
|
||||
// This follows the same rules as Selection.Append.
|
||||
func (s *Selection) ReplaceWithHtml(html string) *Selection {
|
||||
return s.ReplaceWithNodes(parseHtml(html)...)
|
||||
func (s *Selection) ReplaceWithHtml(htmlStr string) *Selection {
|
||||
s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
if node.Parent != nil {
|
||||
node.Parent.InsertBefore(n, node.NextSibling)
|
||||
}
|
||||
}
|
||||
})
|
||||
return s.Remove()
|
||||
}
|
||||
|
||||
// ReplaceWithNodes replaces each element in the set of matched elements with
|
||||
@@ -270,6 +302,27 @@ func (s *Selection) ReplaceWithNodes(ns ...*html.Node) *Selection {
|
||||
return s.Remove()
|
||||
}
|
||||
|
||||
// SetHtml sets the html content of each element in the selection to
|
||||
// specified html string.
|
||||
func (s *Selection) SetHtml(htmlStr string) *Selection {
|
||||
for _, context := range s.Nodes {
|
||||
for c := context.FirstChild; c != nil; c = context.FirstChild {
|
||||
context.RemoveChild(c)
|
||||
}
|
||||
}
|
||||
return s.eachNodeHtml(htmlStr, true, func(node *html.Node, nodes []*html.Node) {
|
||||
for _, n := range nodes {
|
||||
node.AppendChild(n)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// Unwrap removes the parents of the set of matched elements, leaving the matched
|
||||
// elements (and their siblings, if any) in their place.
|
||||
// It returns the original selection.
|
||||
@@ -317,8 +370,23 @@ func (s *Selection) WrapSelection(sel *Selection) *Selection {
|
||||
// most child of the given HTML.
|
||||
//
|
||||
// It returns the original set of elements.
|
||||
func (s *Selection) WrapHtml(html string) *Selection {
|
||||
return s.wrapNodes(parseHtml(html)...)
|
||||
func (s *Selection) WrapHtml(htmlStr string) *Selection {
|
||||
nodesMap := make(map[html.NodeType][]*html.Node)
|
||||
for _, context := range s.Nodes {
|
||||
var parent *html.Node
|
||||
if context.Parent != nil {
|
||||
parent = context.Parent
|
||||
} else {
|
||||
parent = &html.Node{Type: html.ElementNode}
|
||||
}
|
||||
nodes, found := nodesMap[parent.Type]
|
||||
if !found {
|
||||
nodes = parseHtmlWithContext(htmlStr, parent)
|
||||
nodesMap[parent.Type] = nodes
|
||||
}
|
||||
newSingleSelection(context, s.document).wrapAllNodes(cloneNodes(nodes)...)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// WrapNode wraps each element in the set of matched elements inside the inner-
|
||||
@@ -370,8 +438,18 @@ func (s *Selection) WrapAllSelection(sel *Selection) *Selection {
|
||||
// document.
|
||||
//
|
||||
// It returns the original set of elements.
|
||||
func (s *Selection) WrapAllHtml(html string) *Selection {
|
||||
return s.wrapAllNodes(parseHtml(html)...)
|
||||
func (s *Selection) WrapAllHtml(htmlStr string) *Selection {
|
||||
var context *html.Node
|
||||
var nodes []*html.Node
|
||||
if len(s.Nodes) > 0 {
|
||||
context = s.Nodes[0]
|
||||
if context.Parent != nil {
|
||||
nodes = parseHtmlWithContext(htmlStr, context)
|
||||
} else {
|
||||
nodes = parseHtml(htmlStr)
|
||||
}
|
||||
}
|
||||
return s.wrapAllNodes(nodes...)
|
||||
}
|
||||
|
||||
func (s *Selection) wrapAllNodes(ns ...*html.Node) *Selection {
|
||||
@@ -440,8 +518,17 @@ func (s *Selection) WrapInnerSelection(sel *Selection) *Selection {
|
||||
// cloned before being inserted into the document.
|
||||
//
|
||||
// It returns the original set of elements.
|
||||
func (s *Selection) WrapInnerHtml(html string) *Selection {
|
||||
return s.wrapInnerNodes(parseHtml(html)...)
|
||||
func (s *Selection) WrapInnerHtml(htmlStr string) *Selection {
|
||||
nodesMap := make(map[html.NodeType][]*html.Node)
|
||||
for _, context := range s.Nodes {
|
||||
nodes, found := nodesMap[context.Type]
|
||||
if !found {
|
||||
nodes = parseHtmlWithContext(htmlStr, context)
|
||||
nodesMap[context.Type] = nodes
|
||||
}
|
||||
newSingleSelection(context, s.document).wrapInnerNodes(cloneNodes(nodes)...)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// WrapInnerNode wraps an HTML structure, matched by the given selector, around
|
||||
@@ -481,6 +568,16 @@ func parseHtml(h string) []*html.Node {
|
||||
return nodes
|
||||
}
|
||||
|
||||
func parseHtmlWithContext(h string, context *html.Node) []*html.Node {
|
||||
// Errors are only returned when the io.Reader returns any error besides
|
||||
// EOF, but strings.Reader never will
|
||||
nodes, err := html.ParseFragment(strings.NewReader(h), context)
|
||||
if err != nil {
|
||||
panic("goquery: failed to parse HTML: " + err.Error())
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
// Get the first child that is an ElementNode
|
||||
func getFirstChildEl(n *html.Node) *html.Node {
|
||||
c := n.FirstChild
|
||||
@@ -548,3 +645,29 @@ func (s *Selection) manipulateNodes(ns []*html.Node, reverse bool,
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// eachNodeHtml parses the given html string and inserts the resulting nodes in the dom with the mergeFn.
|
||||
// The parsed nodes are inserted for each element of the selection.
|
||||
// isParent can be used to indicate that the elements of the selection should be treated as the parent for the parsed html.
|
||||
// A cache is used to avoid parsing the html multiple times should the elements of the selection result in the same context.
|
||||
func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n *html.Node, nodes []*html.Node)) *Selection {
|
||||
// cache to avoid parsing the html for the same context multiple times
|
||||
nodeCache := make(map[string][]*html.Node)
|
||||
var context *html.Node
|
||||
for _, n := range s.Nodes {
|
||||
if isParent {
|
||||
context = n.Parent
|
||||
} else {
|
||||
context = n
|
||||
}
|
||||
if context != nil {
|
||||
nodes, found := nodeCache[nodeName(context)]
|
||||
if !found {
|
||||
nodes = parseHtmlWithContext(htmlStr, context)
|
||||
nodeCache[nodeName(context)] = nodes
|
||||
}
|
||||
mergeFn(n, cloneNodes(nodes))
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -56,6 +56,25 @@ func TestAfterHtml(t *testing.T) {
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAfterHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Before1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Before2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table tr td").AfterHtml("<td>Test</td>")
|
||||
assertLength(t, doc.Find("table tr td").Nodes, 4)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppend(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").Append("#nf6")
|
||||
@@ -113,6 +132,27 @@ func TestAppendHtml(t *testing.T) {
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestAppendHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Before1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Before2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table tr").AppendHtml("<td class='new-node'>new node</td>")
|
||||
|
||||
assertLength(t, doc.Find("table td").Nodes, 4)
|
||||
assertClass(t, doc.Find("table td").Last(), "new-node")
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBefore(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#main").Before("#nf6")
|
||||
@@ -151,6 +191,27 @@ func TestBeforeHtml(t *testing.T) {
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestBeforeHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Before1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Before2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table tr td:first-child").BeforeHtml("<td class='new-node'>new node</td>")
|
||||
|
||||
assertLength(t, doc.Find("table td").Nodes, 4)
|
||||
assertClass(t, doc.Find("table td").First(), "new-node")
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
s := doc.Find("#main").Empty()
|
||||
@@ -218,6 +279,27 @@ func TestPrependHtml(t *testing.T) {
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestPrependHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Before1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Before2</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table tr").PrependHtml("<td class='c1'>new node</td><td class='c2'>other new node</td>")
|
||||
|
||||
assertLength(t, doc.Find("table td").Nodes, 6)
|
||||
assertClass(t, doc.Find("table tr td").First(), "c1")
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestRemove(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
doc.Find("#nf1").Remove()
|
||||
@@ -278,6 +360,109 @@ func TestReplaceWithHtml(t *testing.T) {
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestReplaceWithHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Before1</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Before2</th>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table th").ReplaceWithHtml("<td>Test</td><td>Replace</td>")
|
||||
|
||||
assertLength(t, doc.Find("table th").Nodes, 0)
|
||||
assertLength(t, doc.Find("table tr td").Nodes, 4)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestSetHtml(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
q := doc.Find("#main, #foot")
|
||||
q.SetHtml(`<div id="replace">test</div>`)
|
||||
|
||||
assertLength(t, doc.Find("#replace").Nodes, 2)
|
||||
assertLength(t, doc.Find("#main, #foot").Nodes, 2)
|
||||
|
||||
if q.Text() != "testtest" {
|
||||
t.Errorf("Expected text to be %v, found %v", "testtest", q.Text())
|
||||
}
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestSetHtmlNoMatch(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
q := doc.Find("#notthere")
|
||||
q.SetHtml(`<div id="replace">test</div>`)
|
||||
|
||||
assertLength(t, doc.Find("#replace").Nodes, 0)
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestSetHtmlEmpty(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
q := doc.Find("#main")
|
||||
q.SetHtml(``)
|
||||
|
||||
assertLength(t, doc.Find("#main").Nodes, 1)
|
||||
assertLength(t, doc.Find("#main").Children().Nodes, 0)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestSetHtmlContext(t *testing.T) {
|
||||
doc := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table>
|
||||
<tr>
|
||||
<th>Before1</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Before2</th>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`)
|
||||
doc.Find("table tr").SetHtml("<td class='c1'>Test</td><td class='c2'>Again</td>")
|
||||
|
||||
assertLength(t, doc.Find("table th").Nodes, 0)
|
||||
assertLength(t, doc.Find("table td").Nodes, 4)
|
||||
assertLength(t, doc.Find("table t2").Nodes, 2)
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestSetText(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
q := doc.Find("#main, #foot")
|
||||
repl := "<div id=\"replace\">test</div>"
|
||||
q.SetText(repl)
|
||||
|
||||
assertLength(t, doc.Find("#replace").Nodes, 0)
|
||||
assertLength(t, doc.Find("#main, #foot").Nodes, 2)
|
||||
|
||||
if q.Text() != (repl + repl) {
|
||||
t.Errorf("Expected text to be %v, found %v", (repl + repl), q.Text())
|
||||
}
|
||||
|
||||
h, err := q.Html()
|
||||
if err != nil {
|
||||
t.Errorf("Error: %v", err)
|
||||
}
|
||||
esc := "<div id="replace">test</div>"
|
||||
if h != esc {
|
||||
t.Errorf("Expected html to be %v, found %v", esc, h)
|
||||
}
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestReplaceWithSelection(t *testing.T) {
|
||||
doc := Doc2Clone()
|
||||
sel := doc.Find("#nf6").ReplaceWithSelection(doc.Find("#nf5"))
|
||||
@@ -451,3 +636,54 @@ func TestWrapInnerHtml(t *testing.T) {
|
||||
|
||||
printSel(t, doc.Selection)
|
||||
}
|
||||
|
||||
func TestParsingRespectsVaryingContext(t *testing.T) {
|
||||
docA := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<a class="x"></a>
|
||||
</body>
|
||||
</html>`)
|
||||
docTable := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table class="x"></table>
|
||||
</body>
|
||||
</html>`)
|
||||
docBoth := loadString(t, `
|
||||
<html>
|
||||
<body>
|
||||
<table class="x"></table>
|
||||
<a class="x"></a>
|
||||
</body>
|
||||
</html>`)
|
||||
|
||||
sA := docA.Find(".x").AppendHtml("<tr><td>Hello</td></tr>")
|
||||
sTable := docTable.Find(".x").AppendHtml("<tr><td>Hello</td></tr>")
|
||||
sBoth := docBoth.Find(".x").AppendHtml("<tr><td>Hello</td></tr>")
|
||||
|
||||
printSel(t, docA.Selection)
|
||||
printSel(t, docTable.Selection)
|
||||
printSel(t, docBoth.Selection)
|
||||
|
||||
oA, _ := sA.Html()
|
||||
oTable, _ := sTable.Html()
|
||||
|
||||
if oA == oTable {
|
||||
t.Errorf("Expected inner html of <a> and <table> to not be equal, but got %s and %s", oA, oTable)
|
||||
}
|
||||
|
||||
oBothTable, _ := sBoth.First().Html()
|
||||
if oBothTable != oTable {
|
||||
t.Errorf("Expected inner html of <table> and <table> in doc containing both tags to be equal, but got %s and %s",
|
||||
oTable,
|
||||
oBothTable)
|
||||
}
|
||||
|
||||
oBothA, _ := sBoth.Last().Html()
|
||||
if oBothA != oA {
|
||||
t.Errorf("Expected inner html of <a> and <a> in doc containing both tags to be equal, but got %s and %s",
|
||||
oA,
|
||||
oBothA)
|
||||
}
|
||||
}
|
||||
|
||||
+27
-27
@@ -1,37 +1,37 @@
|
||||
#!/bin/sh
|
||||
# Copyright 2012 The Go Authors. All rights reserved.
|
||||
# Use of this source code is governed by a BSD-style
|
||||
# license that can be found in the LICENSE file.
|
||||
|
||||
# git gofmt pre-commit hook
|
||||
#
|
||||
# To use, store as .git/hooks/pre-commit inside your repository and make sure
|
||||
# it has execute permissions.
|
||||
#
|
||||
# This script does not handle file names that contain spaces.
|
||||
echo ">>> golint"
|
||||
for dir in $(go list ./... | grep -v /vendor/)
|
||||
do
|
||||
golint "${dir}"
|
||||
done
|
||||
echo "<<< golint"
|
||||
echo
|
||||
|
||||
# golint is purely informational, it doesn't fail with exit code != 0 if it finds something,
|
||||
# because it may find a lot of false positives. Just print out its result for information.
|
||||
echo "lint result (informational only):"
|
||||
golint .
|
||||
echo ">>> go vet"
|
||||
go vet $(go list ./... | grep -v /vendor/)
|
||||
echo "<<< go vet"
|
||||
echo
|
||||
|
||||
# go vet returns 1 if an error was found. Exit the hook with this exit code.
|
||||
go vet ./...
|
||||
vetres=$?
|
||||
echo ">>> gosimple"
|
||||
gosimple $(go list ./... | grep -v /vendor/)
|
||||
echo "<<< gosimple"
|
||||
echo
|
||||
|
||||
# Check for gofmt problems and report if any.
|
||||
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$')
|
||||
gofiles=$(git diff --cached --name-only --diff-filter=ACM | grep '.go$' | grep -v /vendor/)
|
||||
[ -z "$gofiles" ] && echo "EXIT $vetres" && exit $vetres
|
||||
|
||||
unformatted=$(gofmt -l $gofiles)
|
||||
[ -z "$unformatted" ] && echo "EXIT $vetres" && exit $vetres
|
||||
if [ -n "$gofiles" ]; then
|
||||
unformatted=$(gofmt -l $gofiles)
|
||||
|
||||
# Some files are not gofmt'd. Print message and fail.
|
||||
if [ -n "$unformatted" ]; then
|
||||
# Some files are not gofmt'd.
|
||||
echo >&2 "Go files must be formatted with gofmt. Please run:"
|
||||
for fn in $unformatted; do
|
||||
echo >&2 " gofmt -w $PWD/$fn"
|
||||
done
|
||||
fi
|
||||
fi
|
||||
echo
|
||||
|
||||
echo >&2 "Go files must be formatted with gofmt. Please run:"
|
||||
for fn in $unformatted; do
|
||||
echo >&2 " gofmt -w $PWD/$fn"
|
||||
done
|
||||
|
||||
echo "EXIT 1"
|
||||
exit 1
|
||||
|
||||
+18
-21
@@ -63,9 +63,22 @@ func (s *Selection) Text() string {
|
||||
var buf bytes.Buffer
|
||||
|
||||
// Slightly optimized vs calling Each: no single selection object created
|
||||
for _, n := range s.Nodes {
|
||||
buf.WriteString(getNodeText(n))
|
||||
var f func(*html.Node)
|
||||
f = func(n *html.Node) {
|
||||
if n.Type == html.TextNode {
|
||||
// Keep newlines and spaces, like jQuery
|
||||
buf.WriteString(n.Data)
|
||||
}
|
||||
if n.FirstChild != nil {
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
f(c)
|
||||
}
|
||||
}
|
||||
}
|
||||
for _, n := range s.Nodes {
|
||||
f(n)
|
||||
}
|
||||
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
@@ -112,7 +125,7 @@ func (s *Selection) AddClass(class ...string) *Selection {
|
||||
for _, n := range s.Nodes {
|
||||
curClasses, attr := getClassesAndAttr(n, true)
|
||||
for _, newClass := range tcls {
|
||||
if strings.Index(curClasses, " "+newClass+" ") == -1 {
|
||||
if !strings.Contains(curClasses, " "+newClass+" ") {
|
||||
curClasses += newClass + " "
|
||||
}
|
||||
}
|
||||
@@ -129,7 +142,7 @@ func (s *Selection) HasClass(class string) bool {
|
||||
class = " " + class + " "
|
||||
for _, n := range s.Nodes {
|
||||
classes, _ := getClassesAndAttr(n, false)
|
||||
if strings.Index(classes, class) > -1 {
|
||||
if strings.Contains(classes, class) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -179,7 +192,7 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
|
||||
for _, n := range s.Nodes {
|
||||
classes, attr := getClassesAndAttr(n, true)
|
||||
for _, tcl := range tcls {
|
||||
if strings.Index(classes, " "+tcl+" ") != -1 {
|
||||
if strings.Contains(classes, " "+tcl+" ") {
|
||||
classes = strings.Replace(classes, " "+tcl+" ", " ", -1)
|
||||
} else {
|
||||
classes += tcl + " "
|
||||
@@ -192,22 +205,6 @@ func (s *Selection) ToggleClass(class ...string) *Selection {
|
||||
return s
|
||||
}
|
||||
|
||||
// Get the specified node's text content.
|
||||
func getNodeText(node *html.Node) string {
|
||||
if node.Type == html.TextNode {
|
||||
// Keep newlines and spaces, like jQuery
|
||||
return node.Data
|
||||
} else if node.FirstChild != nil {
|
||||
var buf bytes.Buffer
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
buf.WriteString(getNodeText(c))
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func getAttributePtr(attrName string, n *html.Node) *html.Attribute {
|
||||
if n == nil {
|
||||
return nil
|
||||
|
||||
@@ -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
|
||||
|
||||
+8
-3
@@ -143,11 +143,15 @@ func (s *Selection) ClosestMatcher(m Matcher) *Selection {
|
||||
// ClosestNodes gets the first element that matches one of the nodes by testing the
|
||||
// element itself and traversing up through its ancestors in the DOM tree.
|
||||
func (s *Selection) ClosestNodes(nodes ...*html.Node) *Selection {
|
||||
set := make(map[*html.Node]bool)
|
||||
for _, n := range nodes {
|
||||
set[n] = true
|
||||
}
|
||||
return pushStack(s, mapNodes(s.Nodes, func(i int, n *html.Node) []*html.Node {
|
||||
// For each node in the selection, test the node itself, then each parent
|
||||
// until a match is found.
|
||||
for ; n != nil; n = n.Parent {
|
||||
if isInSlice(nodes, n) {
|
||||
if set[n] {
|
||||
return []*html.Node{n}
|
||||
}
|
||||
}
|
||||
@@ -684,10 +688,11 @@ func getParentNodes(nodes []*html.Node) []*html.Node {
|
||||
// Returns an array of nodes mapped by calling the callback function once for
|
||||
// each node in the source nodes.
|
||||
func mapNodes(nodes []*html.Node, f func(int, *html.Node) []*html.Node) (result []*html.Node) {
|
||||
set := make(map[*html.Node]bool)
|
||||
for i, n := range nodes {
|
||||
if vals := f(i, n); len(vals) > 0 {
|
||||
result = appendWithoutDuplicates(result, vals)
|
||||
result = appendWithoutDuplicates(result, vals, set)
|
||||
}
|
||||
}
|
||||
return
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -26,6 +26,16 @@ func TestFindInvalid(t *testing.T) {
|
||||
assertLength(t, sel.Nodes, 0)
|
||||
}
|
||||
|
||||
func TestFindBig(t *testing.T) {
|
||||
doc := DocW()
|
||||
sel := doc.Find("li")
|
||||
assertLength(t, sel.Nodes, 373)
|
||||
sel2 := doc.Find("span")
|
||||
assertLength(t, sel2.Nodes, 448)
|
||||
sel3 := sel.FindSelection(sel2)
|
||||
assertLength(t, sel3.Nodes, 248)
|
||||
}
|
||||
|
||||
func TestChainedFind(t *testing.T) {
|
||||
sel := Doc().Find("div.hero-unit").Find(".row-fluid")
|
||||
assertLength(t, sel.Nodes, 4)
|
||||
|
||||
@@ -31,6 +31,10 @@ func NewDocumentFromNode(root *html.Node) *Document {
|
||||
// NewDocument is a Document constructor that takes a string URL as argument.
|
||||
// It loads the specified document, parses it, and stores the root Document
|
||||
// node, ready to be manipulated.
|
||||
//
|
||||
// Deprecated: Use the net/http standard library package to make the request
|
||||
// and validate the response before calling goquery.NewDocumentFromReader
|
||||
// with the response's body.
|
||||
func NewDocument(url string) (*Document, error) {
|
||||
// Load the URL
|
||||
res, e := http.Get(url)
|
||||
@@ -40,10 +44,10 @@ func NewDocument(url string) (*Document, error) {
|
||||
return NewDocumentFromResponse(res)
|
||||
}
|
||||
|
||||
// NewDocumentFromReader returns a Document from a generic reader.
|
||||
// NewDocumentFromReader returns a Document from an io.Reader.
|
||||
// It returns an error as second value if the reader's data cannot be parsed
|
||||
// as html. It does *not* check if the reader is also an io.Closer, so the
|
||||
// provided reader is never closed by this call, it is the responsibility
|
||||
// as html. It does not check if the reader is also an io.Closer, the
|
||||
// provided reader is never closed by this call. It is the responsibility
|
||||
// of the caller to close it if required.
|
||||
func NewDocumentFromReader(r io.Reader) (*Document, error) {
|
||||
root, e := html.Parse(r)
|
||||
@@ -56,6 +60,8 @@ func NewDocumentFromReader(r io.Reader) (*Document, error) {
|
||||
// NewDocumentFromResponse is another Document constructor that takes an http response as argument.
|
||||
// It loads the specified response's document, parses it, and stores the root Document
|
||||
// node, ready to be manipulated. The response's body is closed on return.
|
||||
//
|
||||
// Deprecated: Use goquery.NewDocumentFromReader with the response's body.
|
||||
func NewDocumentFromResponse(res *http.Response) (*Document, error) {
|
||||
if res == nil {
|
||||
return nil, errors.New("Response is nil")
|
||||
|
||||
+14
-22
@@ -23,45 +23,42 @@ func Doc() *Document {
|
||||
}
|
||||
return doc
|
||||
}
|
||||
func DocClone() *Document {
|
||||
return CloneDocument(Doc())
|
||||
}
|
||||
|
||||
func Doc2() *Document {
|
||||
if doc2 == nil {
|
||||
doc2 = loadDoc("page2.html")
|
||||
}
|
||||
return doc2
|
||||
}
|
||||
|
||||
func Doc2Clone() *Document {
|
||||
return CloneDocument(Doc2())
|
||||
}
|
||||
|
||||
func Doc3() *Document {
|
||||
if doc3 == nil {
|
||||
doc3 = loadDoc("page3.html")
|
||||
}
|
||||
return doc3
|
||||
}
|
||||
|
||||
func Doc3Clone() *Document {
|
||||
return CloneDocument(Doc3())
|
||||
}
|
||||
|
||||
func DocB() *Document {
|
||||
if docB == nil {
|
||||
docB = loadDoc("gotesting.html")
|
||||
}
|
||||
return docB
|
||||
}
|
||||
func DocBClone() *Document {
|
||||
return CloneDocument(DocB())
|
||||
}
|
||||
|
||||
func DocW() *Document {
|
||||
if docW == nil {
|
||||
docW = loadDoc("gowiki.html")
|
||||
}
|
||||
return docW
|
||||
}
|
||||
func DocWClone() *Document {
|
||||
return CloneDocument(DocW())
|
||||
}
|
||||
|
||||
func assertLength(t *testing.T, nodes []*html.Node, length int) {
|
||||
if len(nodes) != length {
|
||||
@@ -108,19 +105,6 @@ func printSel(t *testing.T, sel *Selection) {
|
||||
}
|
||||
}
|
||||
|
||||
func shortPrintSel(t *testing.T, sel *Selection) {
|
||||
sel.Each(func(i int, s *Selection) {
|
||||
h, _ := OuterHtml(s)
|
||||
if ix := strings.Index(h, "\n"); ix >= 0 {
|
||||
h = h[:ix]
|
||||
}
|
||||
if len(h) > 100 {
|
||||
h = h[:100]
|
||||
}
|
||||
fmt.Println(i, ">>> ", h)
|
||||
})
|
||||
}
|
||||
|
||||
func loadDoc(page string) *Document {
|
||||
var f *os.File
|
||||
var e error
|
||||
@@ -137,6 +121,14 @@ func loadDoc(page string) *Document {
|
||||
return NewDocumentFromNode(node)
|
||||
}
|
||||
|
||||
func loadString(t *testing.T, doc string) *Document {
|
||||
d, err := NewDocumentFromReader(strings.NewReader(doc))
|
||||
if err != nil {
|
||||
t.Error("Failed to parse test document")
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
func TestNewDocument(t *testing.T) {
|
||||
if f, e := os.Open("./testdata/page.html"); e != nil {
|
||||
t.Error(e.Error())
|
||||
|
||||
+43
-14
@@ -6,6 +6,11 @@ import (
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
// used to determine if a set (map[*html.Node]bool) should be used
|
||||
// instead of iterating over a slice. The set uses more memory and
|
||||
// is slower than slice iteration for small N.
|
||||
const minNodesForSet = 1000
|
||||
|
||||
var nodeNames = []string{
|
||||
html.ErrorNode: "#error",
|
||||
html.TextNode: "#text",
|
||||
@@ -31,12 +36,22 @@ func NodeName(s *Selection) string {
|
||||
if s.Length() == 0 {
|
||||
return ""
|
||||
}
|
||||
switch n := s.Get(0); n.Type {
|
||||
return nodeName(s.Get(0))
|
||||
}
|
||||
|
||||
// nodeName returns the node name of the given html node.
|
||||
// See NodeName for additional details on behaviour.
|
||||
func nodeName(node *html.Node) string {
|
||||
if node == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch node.Type {
|
||||
case html.ElementNode, html.DoctypeNode:
|
||||
return n.Data
|
||||
return node.Data
|
||||
default:
|
||||
if n.Type >= 0 && int(n.Type) < len(nodeNames) {
|
||||
return nodeNames[n.Type]
|
||||
if node.Type >= 0 && int(node.Type) < len(nodeNames) {
|
||||
return nodeNames[node.Type]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@@ -62,13 +77,6 @@ func OuterHtml(s *Selection) (string, error) {
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func getChildren(n *html.Node) (result []*html.Node) {
|
||||
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||
result = append(result, c)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Loop through all container nodes to search for the target node.
|
||||
func sliceContains(container []*html.Node, contained *html.Node) bool {
|
||||
for _, n := range container {
|
||||
@@ -112,11 +120,32 @@ func indexInSlice(slice []*html.Node, node *html.Node) int {
|
||||
// Appends the new nodes to the target slice, making sure no duplicate is added.
|
||||
// There is no check to the original state of the target slice, so it may still
|
||||
// contain duplicates. The target slice is returned because append() may create
|
||||
// a new underlying array.
|
||||
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node) []*html.Node {
|
||||
// a new underlying array. If targetSet is nil, a local set is created with the
|
||||
// target if len(target) + len(nodes) is greater than minNodesForSet.
|
||||
func appendWithoutDuplicates(target []*html.Node, nodes []*html.Node, targetSet map[*html.Node]bool) []*html.Node {
|
||||
// if there are not that many nodes, don't use the map, faster to just use nested loops
|
||||
// (unless a non-nil targetSet is passed, in which case the caller knows better).
|
||||
if targetSet == nil && len(target)+len(nodes) < minNodesForSet {
|
||||
for _, n := range nodes {
|
||||
if !isInSlice(target, n) {
|
||||
target = append(target, n)
|
||||
}
|
||||
}
|
||||
return target
|
||||
}
|
||||
|
||||
// if a targetSet is passed, then assume it is reliable, otherwise create one
|
||||
// and initialize it with the current target contents.
|
||||
if targetSet == nil {
|
||||
targetSet = make(map[*html.Node]bool, len(target))
|
||||
for _, n := range target {
|
||||
targetSet[n] = true
|
||||
}
|
||||
}
|
||||
for _, n := range nodes {
|
||||
if !isInSlice(target, n) {
|
||||
if !targetSet[n] {
|
||||
target = append(target, n)
|
||||
targetSet[n] = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user