package lwb import ( "net/http" "net/http/cookiejar" "strings" "sync" "git.frankmayer.dev/tsukinoko-kun/lwb/util" "golang.org/x/net/html" ) type Browser struct { url string userAgent string http *http.Client document *html.Node cookies *cookiejar.Jar mut *sync.RWMutex } func NewBrowser(userAgent string) (*Browser, error) { cj, err := cookiejar.New(nil) if err != nil { return nil, err } hc := &http.Client{ Jar: cj, } b := &Browser{ url: "about:blank", userAgent: userAgent, http: hc, document: nil, cookies: cj, mut: &sync.RWMutex{}, } return b, nil } func (b *Browser) Get(url string) error { b.mut.Lock() defer b.mut.Unlock() b.url = url resp, err := b.http.Get(url) if err != nil { return err } defer resp.Body.Close() // Parse the HTML document b.document, err = html.Parse(resp.Body) if err != nil { return err } return nil } func (b *Browser) FindElementByContent(textContent string) *Element { b.mut.RLock() defer b.mut.RUnlock() textContentTrimed := strings.TrimSpace(textContent) var nodes util.Stack[*html.Node] = []*html.Node{b.document} for !nodes.Empty() { node := nodes.Pop() if strings.TrimSpace(node.Data) == textContentTrimed { return &Element{node: node, browser: b} } for c := node.FirstChild; c != nil; c = c.NextSibling { nodes.Push(c) } } 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, browser: b} } 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, browser: b}) 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, " ") }