mirror of
https://github.com/PuerkitoBio/goquery.git
synced 2026-09-24 19:30:50 +00:00
Compare commits
32
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
291e2cf4e3 | ||
|
|
7019296f03 | ||
|
|
eec8e87fed | ||
|
|
957a864443 | ||
|
|
34d91dbc67 | ||
|
|
0984c44bae | ||
|
|
fbfb78c352 | ||
|
|
e91ee4d790 | ||
|
|
90131ce208 | ||
|
|
3e5761b538 | ||
|
|
6802fc5555 | ||
|
|
466380eec7 | ||
|
|
0f5ed94072 | ||
|
|
3f0ba08339 | ||
|
|
629afd96a7 | ||
|
|
f1f5362a06 | ||
|
|
dd1e02fa62 | ||
|
|
f9e713566d | ||
|
|
fd5dc59310 | ||
|
|
b752fff338 | ||
|
|
8bd5b7d432 | ||
|
|
bf8e18a449 | ||
|
|
400486e492 | ||
|
|
1bc4a06d02 | ||
|
|
bc419c1e56 | ||
|
|
b076e25085 | ||
|
|
22d8cfd154 | ||
|
|
56c8ec7392 | ||
|
|
3ce74287b9 | ||
|
|
153f1ea15a | ||
|
|
2095230494 | ||
|
|
ce7330a252 |
@@ -5,3 +5,8 @@ updates:
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
# Enable version updates for GitHub action workflows
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
|
||||
@@ -8,18 +8,18 @@ jobs:
|
||||
test:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x, 1.22.x]
|
||||
go-version: [1.23.x]
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v3
|
||||
uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Test
|
||||
run: go test ./... -v -cover
|
||||
|
||||
@@ -40,6 +40,9 @@ Please note that starting with version `v1.9.0` of goquery, Go 1.18+ is required
|
||||
|
||||
**Note that goquery's API is now stable, and will not break.**
|
||||
|
||||
* **2024-09-06 (v1.9.3)** : Update `go.mod` dependencies.
|
||||
* **2024-04-29 (v1.9.2)** : Update `go.mod` dependencies.
|
||||
* **2024-02-29 (v1.9.1)** : Improve allocation and performance of the `Map` function and `Selection.Map` method, better document the cascadia differences (thanks [@jwilsson](https://github.com/jwilsson)).
|
||||
* **2024-02-22 (v1.9.0)** : Add a generic `Map` function, **goquery now requires Go version 1.18+** (thanks [@Fesaa](https://github.com/Fesaa)).
|
||||
* **2023-02-18 (v1.8.1)** : Update `go.mod` dependencies, update CI workflow.
|
||||
* **2021-10-25 (v1.8.0)** : Add `Render` function to render a `Selection` to an `io.Writer` (thanks [@anthonygedeon](https://github.com/anthonygedeon)).
|
||||
@@ -89,7 +92,7 @@ Utility functions that are not in jQuery but are useful in Go are implemented as
|
||||
|
||||
The complete [package reference documentation can be found here][doc].
|
||||
|
||||
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
|
||||
Please note that Cascadia's selectors do not necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details. Also, the selectors work more like the DOM's `querySelectorAll`, than jQuery's matchers - they have no concept of contextual matching (for some concrete examples of what that means, see [this ticket](https://github.com/andybalholm/cascadia/issues/61)). In practice, it doesn't matter very often but it's something worth mentioning. Invalid selector strings compile to a `Matcher` that fails to match any node. Behaviour of the various functions that take a selector string as argument follows from that fact, e.g. (where `~` is an invalid selector string):
|
||||
|
||||
* `Find("~")` returns an empty selection because the selector string doesn't match anything.
|
||||
* `Add("~")` returns a new selection that holds the same nodes as the original selection, because it didn't add any node (selector string didn't match anything).
|
||||
@@ -159,6 +162,7 @@ func main() {
|
||||
- [goskyr](https://github.com/jakopako/goskyr), an easily configurable command-line scraper written in Go.
|
||||
- [goGetJS](https://github.com/davemolk/goGetJS), a tool for extracting, searching, and saving JavaScript files (with optional headless browser).
|
||||
- [fitter](https://github.com/PxyUp/fitter), a tool for selecting values from JSON, XML, HTML and XPath formatted pages.
|
||||
- [seltabl](github.com/conneroisu/seltabl), an orm-like package and supporting language server for extracting values from HTML
|
||||
|
||||
## Support
|
||||
|
||||
@@ -181,15 +185,15 @@ There are a number of ways you can support the project:
|
||||
|
||||
The [BSD 3-Clause license][bsd], the same as the [Go language][golic]. Cascadia's license is [here][caslic].
|
||||
|
||||
[jquery]: http://jquery.com/
|
||||
[go]: http://golang.org/
|
||||
[jquery]: https://jquery.com/
|
||||
[go]: https://go.dev/
|
||||
[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
|
||||
[bsd]: https://opensource.org/licenses/BSD-3-Clause
|
||||
[golic]: https://go.dev/LICENSE
|
||||
[caslic]: https://github.com/andybalholm/cascadia/blob/master/LICENSE
|
||||
[doc]: https://pkg.go.dev/github.com/PuerkitoBio/goquery
|
||||
[index]: http://api.jquery.com/index/
|
||||
[index]: https://api.jquery.com/index/
|
||||
[gonet]: https://github.com/golang/net/
|
||||
[html]: https://pkg.go.dev/golang.org/x/net/html
|
||||
[wiki]: https://github.com/PuerkitoBio/goquery/wiki/Tips-and-tricks
|
||||
|
||||
@@ -25,6 +25,48 @@ func BenchmarkEach(b *testing.B) {
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEachIter(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("td")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for range sel.EachIter() {
|
||||
tmp++
|
||||
}
|
||||
if n == 0 {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
if n != 59 {
|
||||
b.Fatalf("want 59, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEachIterWithBreak(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
b.StopTimer()
|
||||
sel := DocW().Find("td")
|
||||
b.StartTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
tmp = 0
|
||||
for range sel.EachIter() {
|
||||
tmp++
|
||||
if tmp >= 10 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if n == 0 {
|
||||
n = tmp
|
||||
}
|
||||
}
|
||||
if n != 10 {
|
||||
b.Fatalf("want 10, got %d", n)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMap(b *testing.B) {
|
||||
var tmp, n int
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ func (s *Selection) AddBackFiltered(selector string) *Selection {
|
||||
}
|
||||
|
||||
// AddBackMatcher reduces the previous set of elements on the stack to those that match
|
||||
// the mateher, and adds them to the curernt set.
|
||||
// the matcher, 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) AddBackMatcher(m Matcher) *Selection {
|
||||
|
||||
@@ -2,7 +2,7 @@ module github.com/PuerkitoBio/goquery
|
||||
|
||||
require (
|
||||
github.com/andybalholm/cascadia v1.3.2
|
||||
golang.org/x/net v0.21.0
|
||||
golang.org/x/net v0.29.0
|
||||
)
|
||||
|
||||
go 1.18
|
||||
go 1.23
|
||||
|
||||
@@ -10,8 +10,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
||||
+17
-1
@@ -1,5 +1,7 @@
|
||||
package goquery
|
||||
|
||||
import "iter"
|
||||
|
||||
// Each iterates over a Selection object, executing a function for each
|
||||
// matched element. It returns the current Selection object. The function
|
||||
// f is called for each element in the selection with the index of the
|
||||
@@ -12,6 +14,18 @@ func (s *Selection) Each(f func(int, *Selection)) *Selection {
|
||||
return s
|
||||
}
|
||||
|
||||
// EachIter returns an iterator that yields the Selection object in order.
|
||||
// The implementation is similar to Each, but it returns an iterator instead.
|
||||
func (s *Selection) EachIter() iter.Seq2[int, *Selection] {
|
||||
return func(yield func(int, *Selection) bool) {
|
||||
for i, n := range s.Nodes {
|
||||
if !yield(i, newSingleSelection(n, s.document)) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EachWithBreak iterates over a Selection object, executing a function for each
|
||||
// matched element. It is identical to Each except that it is possible to break
|
||||
// out of the loop by returning false in the callback function. It returns the
|
||||
@@ -37,8 +51,10 @@ func (s *Selection) Map(f func(int, *Selection) string) (result []string) {
|
||||
// Map is the generic version of Selection.Map, allowing any type to be
|
||||
// returned.
|
||||
func Map[E any](s *Selection, f func(int, *Selection) E) (result []E) {
|
||||
result = make([]E, len(s.Nodes))
|
||||
|
||||
for i, n := range s.Nodes {
|
||||
result = append(result, f(i, newSingleSelection(n, s.document)))
|
||||
result[i] = f(i, newSingleSelection(n, s.document))
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -105,3 +105,39 @@ func TestGenericMap(t *testing.T) {
|
||||
t.Errorf("Expected Map array result to have a length of 3, found %v.", len(vals))
|
||||
}
|
||||
}
|
||||
|
||||
func TestEachIter(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := Doc().Find(".hero-unit .row-fluid")
|
||||
|
||||
for i, s := range sel.EachIter() {
|
||||
cnt++
|
||||
t.Logf("At index %v, node %v", i, s.Nodes[0].Data)
|
||||
}
|
||||
|
||||
sel = sel.Find("a")
|
||||
|
||||
if cnt != 4 {
|
||||
t.Errorf("Expected Each() to call function 4 times, got %v times.", cnt)
|
||||
}
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
func TestEachIterWithBreak(t *testing.T) {
|
||||
var cnt int
|
||||
|
||||
sel := Doc().Find(".hero-unit .row-fluid")
|
||||
for i, s := range sel.EachIter() {
|
||||
cnt++
|
||||
t.Logf("At index %v, node %v", i, s.Nodes[0].Data)
|
||||
break
|
||||
}
|
||||
|
||||
sel = sel.Find("a")
|
||||
|
||||
if cnt != 1 {
|
||||
t.Errorf("Expected Each() to call function 1 time, got %v times.", cnt)
|
||||
}
|
||||
assertLength(t, sel.Nodes, 6)
|
||||
}
|
||||
|
||||
@@ -20,6 +20,12 @@ const (
|
||||
// Find gets the descendants of each element in the current set of matched
|
||||
// elements, filtered by a selector. It returns a new Selection object
|
||||
// containing these matched elements.
|
||||
//
|
||||
// Note that as for all methods accepting a selector string, the selector is
|
||||
// compiled and applied by the cascadia package and inherits its behavior and
|
||||
// constraints regarding supported selectors. See the note on cascadia in
|
||||
// the goquery documentation here:
|
||||
// https://github.com/PuerkitoBio/goquery?tab=readme-ov-file#api
|
||||
func (s *Selection) Find(selector string) *Selection {
|
||||
return pushStack(s, findWithMatcher(s.Nodes, compileMatcher(selector)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user