init
This commit is contained in:
commit
f9082aa509
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
.DS_Store
|
26
LICENSE
Normal file
26
LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
MIT NON-AI License
|
||||
|
||||
Copyright (c) 2024, Frank Mayer
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of the software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions.
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
In addition, the following restrictions apply:
|
||||
|
||||
1. The Software and any modifications made to it may not be used for the purpose of training or improving machine learning algorithms,
|
||||
including but not limited to artificial intelligence, natural language processing, or data mining. This condition applies to any derivatives,
|
||||
modifications, or updates based on the Software code. Any usage of the Software in an AI-training dataset is considered a breach of this License.
|
||||
|
||||
2. The Software may not be included in any dataset used for training or improving machine learning algorithms,
|
||||
including but not limited to artificial intelligence, natural language processing, or data mining.
|
||||
|
||||
3. Any person or organization found to be in violation of these restrictions will be subject to legal action and may be held liable
|
||||
for any damages resulting from such use.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
5
go.mod
Normal file
5
go.mod
Normal file
@ -0,0 +1,5 @@
|
||||
module github.com/tsukinoko-kun/lwb
|
||||
|
||||
go 1.23.2
|
||||
|
||||
require golang.org/x/net v0.32.0 // indirect
|
2
go.sum
Normal file
2
go.sum
Normal file
@ -0,0 +1,2 @@
|
||||
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
|
||||
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
|
107
main.go
Normal file
107
main.go
Normal file
@ -0,0 +1,107 @@
|
||||
package lwb
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/tsukinoko-kun/lwb/util"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
type (
|
||||
Browser struct {
|
||||
url string
|
||||
userAgent string
|
||||
document *html.Node
|
||||
mut sync.RWMutex
|
||||
}
|
||||
|
||||
Element struct {
|
||||
node *html.Node
|
||||
}
|
||||
)
|
||||
|
||||
func NewBrowser(userAgent string) *Browser {
|
||||
b := &Browser{
|
||||
userAgent: userAgent,
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *Browser) Get(url string) error {
|
||||
b.mut.Lock()
|
||||
defer b.mut.Unlock()
|
||||
|
||||
b.url = url
|
||||
|
||||
rest, err := http.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rest.Body.Close()
|
||||
|
||||
b.document, err = html.Parse(rest.Body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Browser) GetElementById(id string) *Element {
|
||||
b.mut.RLock()
|
||||
defer b.mut.RUnlock()
|
||||
|
||||
var nodes util.Stack[*html.Node] = []*html.Node{b.document}
|
||||
for !nodes.Empty() {
|
||||
node := nodes.Pop()
|
||||
for _, a := range node.Attr {
|
||||
if strings.ToLower(a.Key) != "id" {
|
||||
continue
|
||||
}
|
||||
if a.Val != id {
|
||||
break
|
||||
}
|
||||
return &Element{node: node}
|
||||
}
|
||||
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
nodes.Push(c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Browser) GetElementsByClassName(class string) []*Element {
|
||||
b.mut.RLock()
|
||||
defer b.mut.RUnlock()
|
||||
|
||||
var elements []*Element
|
||||
var nodes util.Stack[*html.Node] = []*html.Node{b.document}
|
||||
for !nodes.Empty() {
|
||||
node := nodes.Pop()
|
||||
attr_loop:
|
||||
for _, a := range node.Attr {
|
||||
if strings.ToLower(a.Key) != "class" {
|
||||
continue
|
||||
}
|
||||
for _, c := range classNames(a.Val) {
|
||||
if c == class {
|
||||
elements = append(elements, &Element{node: node})
|
||||
break attr_loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
||||
nodes.Push(c)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func classNames(class string) []string {
|
||||
return strings.Split(class, " ")
|
||||
}
|
21
util/stack.go
Normal file
21
util/stack.go
Normal file
@ -0,0 +1,21 @@
|
||||
package util
|
||||
|
||||
type Stack[T any] []T
|
||||
|
||||
func (s *Stack[T]) Push(v T) {
|
||||
*s = append(*s, v)
|
||||
}
|
||||
|
||||
func (s *Stack[T]) Pop() T {
|
||||
v := (*s)[len(*s)-1]
|
||||
*s = (*s)[:len(*s)-1]
|
||||
return v
|
||||
}
|
||||
|
||||
func (s Stack[T]) Peek() T {
|
||||
return s[len(s)-1]
|
||||
}
|
||||
|
||||
func (s Stack[T]) Empty() bool {
|
||||
return len(s) == 0
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user