Documentation
¶
Overview ¶
Package browser is a pure-Go, dependency-light headless browser for scraping and automation.
It is a Go answer to the Zig Lightpanda browser, but deliberately built only from portable, pure-Go parts so it cross-compiles and runs everywhere Go does, including Android/Termux and musl systems:
- HTML5 parsing + serialization: golang.org/x/net/html
- CSS selectors: github.com/andybalholm/cascadia
- JavaScript: github.com/dop251/goja (an ECMAScript engine in pure Go)
- Network: shade/requests, so pages are fetched with real browser TLS/JA3 and HTTP/2 fingerprints.
The DOM is the x/net/html node tree itself. That keeps the port small and correct: the same nodes that were parsed are the ones scripts mutate.
Index ¶
- Constants
- Variables
- func Attr(n *html.Node, key string) string
- func UserAgent() string
- type Browser
- type ConsoleEntry
- type Link
- type MicrodataItem
- type Options
- type Page
- func (p *Page) CallOn(decl string, this goja.Value, args []goja.Value) (goja.Value, error)
- func (p *Page) Check(sel string, on bool) error
- func (p *Page) Click(sel string) error
- func (p *Page) ClickNode(n *html.Node) error
- func (p *Page) ClickPoint(x, y float64) error
- func (p *Page) Close()
- func (p *Page) Console() []ConsoleEntry
- func (p *Page) Document() *html.Node
- func (p *Page) ElementFromPoint(x, y float64) *html.Node
- func (p *Page) ElementRect(n *html.Node) Rect
- func (p *Page) Eval(script string) (goja.Value, error)
- func (p *Page) Fill(sel, value string) error
- func (p *Page) Focus(sel string) error
- func (p *Page) FrameFor(el *html.Node) *Page
- func (p *Page) Frames() []*Page
- func (p *Page) GetElementByID(idv string) *html.Node
- func (p *Page) HTML() string
- func (p *Page) InitScripts() []string
- func (p *Page) Links() []Link
- func (p *Page) Load(rawURL string) error
- func (p *Page) Log(level, message string)
- func (p *Page) Markdown() string
- func (p *Page) Microdata() []MicrodataItem
- func (p *Page) NodeValue(n *html.Node) goja.Value
- func (p *Page) OuterHTMLOf(n *html.Node) string
- func (p *Page) PDF(opts ScreenshotOptions) ([]byte, error)
- func (p *Page) Press(key string) error
- func (p *Page) Query(sel string) *html.Node
- func (p *Page) QueryAll(sel string) []*html.Node
- func (p *Page) QuerySelectorAllFrom(n *html.Node, sel string) []*html.Node
- func (p *Page) QuerySelectorFrom(n *html.Node, sel string) *html.Node
- func (p *Page) QueryXPath(expr string) []*html.Node
- func (p *Page) QueryXPathFirst(expr string, from *html.Node) *html.Node
- func (p *Page) QueryXPathFrom(expr string, from *html.Node) []*html.Node
- func (p *Page) ReadyState() string
- func (p *Page) RenderOutline(opts ScreenshotOptions, limit int) ([]string, error)
- func (p *Page) Response() *requests.Response
- func (p *Page) Runtime() *goja.Runtime
- func (p *Page) Screenshot(opts ScreenshotOptions) ([]byte, error)
- func (p *Page) Scroll(sel string) error
- func (p *Page) Select(sel, value string) error
- func (p *Page) SetContent(source, url string) error
- func (p *Page) SetInitScripts(scripts []string)
- func (p *Page) SetUserAgent(ua string)
- func (p *Page) SetViewportWidth(w float64)
- func (p *Page) ShadowRoots() []*shadowRoot
- func (p *Page) StructuredData() []any
- func (p *Page) StyleSheets() []StyleSheet
- func (p *Page) StyleSheetsAt(width float64) []StyleSheet
- func (p *Page) Text() string
- func (p *Page) TextOf(n *html.Node) string
- func (p *Page) Title() string
- func (p *Page) Type(sel, text string) error
- func (p *Page) UserAgent() string
- func (p *Page) WaitForNetworkIdle(quiet, timeout time.Duration)
- func (p *Page) WaitForScript(script string, timeout time.Duration) error
- func (p *Page) WaitForSelector(sel string, timeout time.Duration) *html.Node
- func (p *Page) WaitForTime(d time.Duration)
- func (p *Page) XPath(expr string) (XPathResult, error)
- type Rect
- type Request
- type Response
- type ScreenshotOptions
- type StyleSheet
- type XPathResult
Constants ¶
const ( Chrome100 = impersonate.Chrome100 Chrome101 = impersonate.Chrome101 Chrome104 = impersonate.Chrome104 Chrome107 = impersonate.Chrome107 Chrome110 = impersonate.Chrome110 Chrome116 = impersonate.Chrome116 Chrome119 = impersonate.Chrome119 Chrome120 = impersonate.Chrome120 Chrome123 = impersonate.Chrome123 Chrome124 = impersonate.Chrome124 Chrome131 = impersonate.Chrome131 Chrome131Android = impersonate.Chrome131Android Chrome133a = impersonate.Chrome133a Chrome136 = impersonate.Chrome136 Chrome142 = impersonate.Chrome142 Chrome145 = impersonate.Chrome145 Chrome146 = impersonate.Chrome146 Chrome150 = impersonate.Chrome150 Chrome99 = impersonate.Chrome99 Chrome99Android = impersonate.Chrome99Android Custom = impersonate.Custom Edge101 = impersonate.Edge101 Edge99 = impersonate.Edge99 Firefox133 = impersonate.Firefox133 Firefox135 = impersonate.Firefox135 Firefox144 = impersonate.Firefox144 Firefox147 = impersonate.Firefox147 OkHTTP4Android = impersonate.OkHTTP4Android Safari153 = impersonate.Safari153 Safari155 = impersonate.Safari155 Safari170 = impersonate.Safari170 Safari172Ios = impersonate.Safari172Ios Safari180 = impersonate.Safari180 Safari180Ios = impersonate.Safari180Ios Safari184 = impersonate.Safari184 Safari184Ios = impersonate.Safari184Ios Safari260 = impersonate.Safari260 Safari2601 = impersonate.Safari2601 Safari260Ios = impersonate.Safari260Ios Tor145 = impersonate.Tor145 DefaultChrome = impersonate.DefaultChrome DefaultEdge = impersonate.DefaultEdge DefaultSafari = impersonate.DefaultSafari DefaultSafariIOS = impersonate.DefaultSafariIOS DefaultSafariBeta = impersonate.DefaultSafariBeta DefaultSafariIOSBeta = impersonate.DefaultSafariIOSBeta DefaultChromeAndroid = impersonate.DefaultChromeAndroid DefaultFirefox = impersonate.DefaultFirefox DefaultTor = impersonate.DefaultTor )
Impersonation targets, re-exported from the impersonate package so that a caller who only drives the browser needs one import rather than two:
import . "github.com/HashShin/shade/browser"
p, err := Get("https://quotes.toscrape.com/js/", Chrome131)
They are aliases of the constants in impersonate/targets.go, not a second copy of the values. A raw string works too, since Get takes a plain string, but a misspelt constant does not compile where a misspelt string only fails when the page is loaded.
Variables ¶
var WaitUntilNames = []string{"load", "domcontentloaded", "networkidle0"}
WaitUntilNames are the accepted Options.WaitUntil values.
Functions ¶
Types ¶
type Browser ¶
type Browser struct {
// contains filtered or unexported fields
}
Browser is a reusable browsing context: it owns cookies and an impersonating HTTP session so multiple pages share state.
func (*Browser) Close ¶
func (b *Browser) Close()
Close releases the underlying HTTP session, unless it was handed in through Options.Session: that one belongs to its creator.
func (*Browser) Fork ¶
Fork returns a new Browser with the same options and its own cookie jar, robots cache and page set. Two forks can hold independent logins, which is what gives each MCP HTTP session its own cookies and memory. They still share nothing else, so a fork is cheap: no request is made until it loads a page.
type ConsoleEntry ¶
ConsoleEntry is a captured console.* message.
type MicrodataItem ¶
MicrodataItem is one item in a microdata graph: an element with itemscope and its itemtype and named properties.
type Options ¶
type Options struct {
// Impersonate selects the TLS/HTTP fingerprint target (see
// shade/impersonate). Empty means the requests default.
Impersonate string
// UserAgent overrides navigator.userAgent and the request UA. When empty
// the impersonation preset's UA is used.
UserAgent string
// Headers are added to every request.
Headers map[string]string
// Timeout per network request. Zero means no explicit timeout.
Timeout time.Duration
// MaxRedirects for subresource and document requests.
MaxRedirects int
// RunScripts controls JavaScript execution. Default true.
RunScripts *bool
// LoadImages is currently ignored (no image decoding); kept for API parity.
LoadImages bool
// JavaScriptTimeout bounds a single script evaluation. Default 10s.
JavaScriptTimeout time.Duration
// LoadTimeout bounds the whole script-loading phase (document scripts and
// external bundles). Default 30s. A slow or hanging subresource is dropped
// once the budget is spent, so a page always finishes loading.
LoadTimeout time.Duration
// TimerBudget bounds how long the loader waits for pending timers after
// DOMContentLoaded and again after load. Default 2s each. Lower it to
// finish sooner at the cost of missing content rendered by timers.
TimerBudget time.Duration
// Console receives console.* output. nil discards it.
Console func(level, message string)
// Insecure skips TLS verification when true.
Insecure bool
// Proxy routes every request (document, scripts, fetch, XHR) through the
// given proxy URL, for example "http://127.0.0.1:8080". Empty means a
// direct connection. It is passed straight to the transport, so socks5://
// and authenticated http:// proxies work.
Proxy string
// LoadOptions are passed to every request a page makes, the document and
// its subresources alike. They are how a fingerprint asked for on one
// request reaches the whole load, and they are applied per request, so a
// browser can share its session and still honour them.
LoadOptions []requests.Option
// Session is the requests session the browser fetches through, which is
// what lets a page and a plain request share cookies: it is how
// Request.Browser reuses the session its request was sent on. Nil creates
// one, which Close then closes; a session set here belongs to its creator
// and is left open. The options that shape the session itself -
// Impersonate, Timeout, Insecure, MaxRedirects and Proxy - apply only when
// the browser creates its own.
Session *requests.Session
// ObeyRobots makes the browser honour robots.txt: before any request it
// fetches and caches the origin's robots.txt and skips a disallowed URL,
// yielding a synthetic 403 that does not stop the load. Off by default,
// so the fast path pays nothing.
ObeyRobots bool
// Intercept, when set, is offered every request before it is sent (document,
// scripts, stylesheets, images, fonts, fetch and XHR). Returning nil lets
// the request continue; returning Block drops it; returning Fulfill answers
// it from the hook. The hook runs synchronously on the request path.
Intercept func(*Request) *Response
// CORS, when set, enforces cross-origin rules on fetch and XHR: a
// cross-origin request carries an Origin header and a preflight when it is
// not simple, and a response without a matching Access-Control-Allow-Origin
// is rejected. Off by default, matching the original's experimental flag,
// and off means fetch behaves as before.
CORS bool
// Adblock drops requests to well-known advertising and analytics hosts and
// paths, so a page renders without the noise and loads faster. Off by
// default. It runs after Intercept, so a hook can override it by
// fulfilling a request the blocker would have dropped.
Adblock bool
// WaitUntil decides how far a load goes before Open returns:
// "load" (the default) runs scripts, DOMContentLoaded, load and the timer
// budget; "domcontentloaded" stops after DOMContentLoaded; "networkidle0"
// additionally keeps draining until nothing is pending. An unknown value
// means "load". See wait.go.
WaitUntil string
// Debug logs page-load phases to stderr.
Debug bool
}
Options configure a Browser or a one-off Page.
type Page ¶
Page is a single loaded document plus its JavaScript environment.
func Get ¶
Get opens one page in a Browser of its own, so a single page takes one call rather than a Browser and an Open:
p, err := browser.Get("https://example.com/", impersonate.Chrome131)
if err != nil {
panic(err)
}
defer p.Close()
The target is optional; without it the default impersonation is used. Close the page when done, which closes the Browser behind it. Use New and Open instead when several pages should share cookies, or when more than the impersonation target has to be set.
func Open ¶
Open loads a request's URL in a browser of its own and returns the page object, so the same Request literal Browse takes can be read and driven: Browse answers with the rendered bytes, Open with the page they came from.
The page owns the browser, so Page.Close releases both. Only a GET of a URL has a browser path, which is the same limit Browse has.
func (*Page) CallOn ¶
CallOn calls a function declaration with `this` bound to obj and the given arguments, in the page's JavaScript context. It is the primitive CDP's Runtime.callFunctionOn builds on.
func (*Page) Click ¶
Click dispatches a full mouse press and release on the first element matching the selector, then a click, as a browser does, and follows the click's own meaning: a link navigates and a submit control submits its form.
func (*Page) ClickPoint ¶
ClickPoint clicks whatever element is at the page coordinates.
func (*Page) Close ¶
func (p *Page) Close()
Close releases the Browser this page was loaded in. A page from Get owns its Browser, so this is the whole cleanup a one-shot needs. A page from New shares its Browser with the other pages in it, so this closes those too; use Browser.Close directly when that lifetime is the clearer one.
func (*Page) Console ¶
func (p *Page) Console() []ConsoleEntry
Console returns captured console output.
func (*Page) ElementFromPoint ¶
ElementFromPoint returns the deepest element whose box contains the point, which is what document.elementFromPoint and a CDP hit test need. It reads the same cached layout as ElementRect.
func (*Page) ElementRect ¶
ElementRect returns the element's border box in CSS pixels. An element the layout did not place (a text node, or one inside display:none) returns the zero rectangle, as a browser's getBoundingClientRect returns for a detached node's parent chain.
func (*Page) Fill ¶
Fill sets a control's value in one step and fires input and change, the way a paste or a programmatic fill does.
func (*Page) GetElementByID ¶
GetElementByID returns the element with the given id, looking inside shadow roots as well.
func (*Page) InitScripts ¶
InitScripts returns a copy of the scripts installed with SetInitScripts.
func (*Page) Links ¶
Links returns every anchor with a resolvable href, including those inside child frames.
func (*Page) Markdown ¶
Markdown renders the current DOM as markdown, useful for feeding an LLM. A child frame's content is appended in document order, so framed content is not lost the way a plain text extraction of the top document would lose it.
func (*Page) Microdata ¶
func (p *Page) Microdata() []MicrodataItem
Microdata extracts itemscope elements and their itemprop values, which is the older alternative to JSON-LD (schema.org's itemscope/itemprop markup).
func (*Page) NodeValue ¶
NodeValue wraps a DOM node as a JavaScript value in the page's context, so a CDP DOM.resolveNode can hand it to a client.
func (*Page) OuterHTMLOf ¶
OuterHTMLOf serializes a node, including the element itself.
func (*Page) PDF ¶
func (p *Page) PDF(opts ScreenshotOptions) ([]byte, error)
PDF renders the page and returns a PDF document containing it. The options are the same as Screenshot's; Width, Scale and MaxHeight control the layout and the image, and NoImages turns off drawing the page's pictures.
func (*Page) Press ¶
Press dispatches a key press on the focused element, or the body when nothing is focused.
func (*Page) Query ¶
Query returns the first element matching a CSS selector. Query returns the first element matching a CSS selector. It reaches into shadow roots, which the DOM's own document.querySelector does not: for a scraper, content hidden behind a web component is content that must be reachable.
func (*Page) QueryAll ¶
QueryAll returns all elements matching a CSS selector, shadow roots included.
func (*Page) QuerySelectorAllFrom ¶
QuerySelectorAllFrom returns every descendant of n matching sel.
func (*Page) QuerySelectorFrom ¶
QuerySelectorFrom returns the first descendant of n matching sel.
func (*Page) QueryXPath ¶
QueryXPath evaluates an XPath expression against the document and returns the matched nodes. A compile error or a scalar result yields no nodes; use Page.XPath when the type of the result matters.
func (*Page) QueryXPathFirst ¶
QueryXPathFirst returns the first node matching expr, searching from `from` (the document when from is nil).
func (*Page) QueryXPathFrom ¶
QueryXPathFrom evaluates expr with `from` as the context node, so a relative expression such as ".//a" searches only that subtree.
func (*Page) ReadyState ¶
ReadyState returns the current document readyState.
func (*Page) RenderOutline ¶
func (p *Page) RenderOutline(opts ScreenshotOptions, limit int) ([]string, error)
RenderOutline lays the page out exactly as Screenshot would and returns one line per drawn block, without rasterizing anything. It is how a render is compared against a browser's geometry: Chromium answers with getBoundingClientRect (tools/cssdiff/geom) and this answers with the boxes this renderer used.
func (*Page) Runtime ¶
Runtime returns the page's JavaScript runtime, or nil when scripts are disabled. It backs CDP's Runtime domain.
func (*Page) Screenshot ¶
func (p *Page) Screenshot(opts ScreenshotOptions) ([]byte, error)
Screenshot renders the page to a PNG.
The page's CSS is applied first: <style> blocks and <link rel=stylesheet> sheets are fetched, parsed and cascaded (specificity, !important, source order, inheritance, simple media queries), including the user-agent defaults. The renderer then flows the document at the requested width: block flow with margins, padding and borders, flex rows, grid columns, tables, absolute positioning, images and backgrounds, and draws the text runs that result.
It is still a document renderer, not a web renderer: there are no floats, no multi-column layout, and no transforms or animations. Layout, stylesheet loading, font parsing and rasterization all happen inside this call, so loading pages is unaffected.
func (*Page) Scroll ¶
Scroll dispatches a scroll event on the element; a full-page render has no scrolling viewport, so this only informs the page.
func (*Page) Select ¶
Select chooses an <option> by value (or by label when no value matches) and fires change.
func (*Page) SetContent ¶
SetContent loads HTML from a string, running scripts unless disabled.
func (*Page) SetInitScripts ¶
SetInitScripts installs scripts evaluated on every document load before the page's own scripts run. The slice is copied, so the caller keeps ownership.
func (*Page) SetUserAgent ¶
SetUserAgent overrides navigator.userAgent and the User-Agent sent on subsequent requests. It is the hook behind CDP Network.setUserAgentOverride. An empty value is ignored so a misconfigured driver cannot blank the UA.
func (*Page) SetViewportWidth ¶
SetViewportWidth sets the width media queries and the layout resolve against, which is what a CDP Emulation.setDeviceMetricsOverride changes.
func (*Page) ShadowRoots ¶
func (p *Page) ShadowRoots() []*shadowRoot
ShadowRoots returns every attached shadow root in document order, so a caller can inspect the trees extraction walked through.
func (*Page) StructuredData ¶
StructuredData returns every JSON-LD object the document declares, in document order, including those in child frames. A block that fails to parse is skipped rather than failing the whole call.
func (*Page) StyleSheets ¶
func (p *Page) StyleSheets() []StyleSheet
StyleSheets lists the stylesheets the page itself declares, in document order, fetching the linked ones the first time it is called. Nothing is injected, and nothing the page declares is left out: a sheet that failed or was skipped appears with Err set, so "the site's CSS did not load" can be told apart from "the page has no CSS to load".
func (*Page) StyleSheetsAt ¶
func (p *Page) StyleSheetsAt(width float64) []StyleSheet
StyleSheetsAt is StyleSheets counted at a given layout width, so a report can name the same width a screenshot renders at. The counts differ because @media is evaluated while parsing.
func (*Page) Text ¶
Text returns the visible text of the rendered DOM, skipping script and style content, with child frame text appended so framed content is not lost.
func (*Page) TextOf ¶
TextOf returns the visible text of a node's subtree, which is how a matched XPath or selector node is turned into a string.
func (*Page) Type ¶
Type focuses the element and types text into it, firing the key and input events a user's keystrokes would.
func (*Page) WaitForNetworkIdle ¶
WaitForNetworkIdle drains the page until nothing has been pending for the quiet period, or the timeout expires. It is --wait-until networkidle0. The timeout is not an error: a page holding a socket open legitimately never goes idle, and the caller still wants whatever it rendered.
func (*Page) WaitForScript ¶
WaitForScript evaluates script repeatedly until it returns a truthy value, which is how a page signals that it has finished rendering. It is --wait-script. The timeout bounds the wait; an error from the script is returned rather than retried, because a broken expression never becomes true.
func (*Page) WaitForSelector ¶
WaitForSelector polls until a selector matches or the timeout expires.
func (*Page) WaitForTime ¶
WaitForTime advances the page by at least d, running timers as their turn comes. It is --wait-ms: a page whose content appears on a setTimeout needs the clock moved, and sleeping alone would not run the callback.
type Rect ¶
type Rect struct {
X, Y, Width, Height float64
}
Rect is an element's border box in CSS pixels, measured from the page's top-left, the same rectangle getBoundingClientRect reports.
type Request ¶
type Request struct {
URL string
Method string
ResourceType string // document, script, stylesheet, image, font, fetch, xhr, other
Headers map[string]string
}
Request is offered to an Intercept hook before it is sent. Its fields are a copy, so a hook may modify the returned request through the pointer it receives but cannot change the page's own state.
type Response ¶
type Response struct {
Status int
Headers map[string]string
Body []byte
// contains filtered or unexported fields
}
Response is what an Intercept hook returns: nil to continue to the network, Block to drop the request, or Fulfill to answer without the network.
type ScreenshotOptions ¶
type ScreenshotOptions struct {
// Width is the layout width in CSS px. Default 1280, clamped to 320..4096.
Width int
// Scale multiplies the output size (2 gives a 2x image). Default 1.
Scale float64
// FontScale multiplies every rendered font size, and the line height that
// goes with it, the way a browser's text-only zoom does. Boxes, padding
// and margins keep the page's own sizes, and getComputedStyle still
// reports them, so this changes only how large the text is drawn.
// Default 1, the page's own sizes.
FontScale float64
// MaxHeight bounds the output height in CSS px, so a very long page cannot
// produce a huge image. Default 20000. The content is cropped.
MaxHeight int
// NoImages turns off drawing the page's pictures, which is the only part
// of a render that fetches anything beyond the page's own stylesheets.
// Images are on by default, bounded by MaxImages and MaxImageBytes.
NoImages bool
// MaxImages caps how many images are fetched for one render. Default 48.
MaxImages int
// MaxImageBytes caps the total image bytes kept for drawing. Default 16MB.
MaxImageBytes int
}
ScreenshotOptions configures Page.Screenshot.
type StyleSheet ¶
type StyleSheet struct {
// Href is the absolute URL, empty for an inline <style> element.
Href string
// Media is the media attribute, or the <style> element's.
Media string
// Inline is true for a <style> element.
Inline bool
// Bytes is the size of the CSS text.
Bytes int
// Rules is the number of rules parsed from it. Media queries are evaluated
// while parsing, so this number is only meaningful with Width.
Rules int
// Fonts is the number of @font-face rules in the sheet. They are not style
// rules, so a font provider's sheet can be all fonts and no rules.
Fonts int
// Width is the layout width the rules were counted at.
Width float64
// Err is why the sheet is not applied, empty when it is.
Err string
}
StyleSheet describes one stylesheet the document declares. It is the Go answer to document.styleSheets, and the way to check that a page's own CSS was loaded rather than guessed at.
Source Files
¶
- act_actions.go
- act_wait.go
- browser.go
- css_om.go
- css_parse.go
- css_values.go
- css_webfont.go
- dom_frames.go
- dom_shadow.go
- dom_tree.go
- dom_xml.go
- extract_structured.go
- extract_text.go
- extract_xpath.go
- font_system.go
- js_abortsignal.go
- js_customelements.go
- js_dom.go
- js_env.go
- js_extras.go
- js_hostfunc.go
- js_iface.go
- js_indexeddb.go
- js_module.go
- js_net.go
- js_observers.go
- js_web.go
- js_webapis.go
- js_websocket.go
- js_worker.go
- net_adblock.go
- net_content_type.go
- net_cors.go
- net_fingerprint.go
- net_intercept.go
- net_robots.go
- open.go
- page.go
- render_canvas.go
- render_geometry.go
- render_image.go
- render_pdf.go
- render_screenshot.go
- render_svg.go
- render_text.go
- render_widget.go
- requests.go
- targets.go