mirror of
https://github.com/tiennm99/miti99bot.git
synced 2026-10-04 12:13:20 +00:00
The monkeyd-crawler repository moved into tiennm99/mttools, so recursive clones of the old submodule URL fail and block every deploy. The crawler, PDF renderer, and export flow now live under internal/modules/monkeyd, trimmed to the bot's use: a fixed phone page, the bundled font only, and the font size as the single export option.
96 lines
2.2 KiB
Go
96 lines
2.2 KiB
Go
package crawler
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
// attr returns the value of the named attribute, or "" when absent.
|
|
func attr(n *html.Node, name string) string {
|
|
for _, a := range n.Attr {
|
|
if a.Key == name {
|
|
return a.Val
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// hasClass reports whether the node carries the given class token.
|
|
func hasClass(n *html.Node, class string) bool {
|
|
for _, tok := range strings.Fields(attr(n, "class")) {
|
|
if tok == class {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// findNode returns the first node in document order satisfying match.
|
|
func findNode(root *html.Node, match func(*html.Node) bool) *html.Node {
|
|
if match(root) {
|
|
return root
|
|
}
|
|
for c := root.FirstChild; c != nil; c = c.NextSibling {
|
|
if found := findNode(c, match); found != nil {
|
|
return found
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// findAllNodes returns every node satisfying match, in document order.
|
|
func findAllNodes(root *html.Node, match func(*html.Node) bool) []*html.Node {
|
|
var out []*html.Node
|
|
var walk func(*html.Node)
|
|
walk = func(n *html.Node) {
|
|
if match(n) {
|
|
out = append(out, n)
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
walk(c)
|
|
}
|
|
}
|
|
walk(root)
|
|
return out
|
|
}
|
|
|
|
// elementByID finds an element by its id attribute.
|
|
func elementByID(root *html.Node, id string) *html.Node {
|
|
return findNode(root, func(n *html.Node) bool {
|
|
return n.Type == html.ElementNode && attr(n, "id") == id
|
|
})
|
|
}
|
|
|
|
// elementByTag finds the first element with the given tag name.
|
|
func elementByTag(root *html.Node, tag string) *html.Node {
|
|
return findNode(root, func(n *html.Node) bool {
|
|
return n.Type == html.ElementNode && n.Data == tag
|
|
})
|
|
}
|
|
|
|
// nodeText collects the descendant text of a node with whitespace collapsed.
|
|
func nodeText(n *html.Node) string {
|
|
if n == nil {
|
|
return ""
|
|
}
|
|
var b strings.Builder
|
|
var walk func(*html.Node)
|
|
walk = func(n *html.Node) {
|
|
if n.Type == html.TextNode {
|
|
b.WriteString(n.Data)
|
|
}
|
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
|
walk(c)
|
|
}
|
|
}
|
|
walk(n)
|
|
return collapseSpaces(b.String())
|
|
}
|
|
|
|
// collapseSpaces trims the string and reduces every whitespace run, including
|
|
// the non-breaking spaces the site emits as , to a single space.
|
|
func collapseSpaces(s string) string {
|
|
return strings.Join(strings.Fields(s), " ")
|
|
}
|