Documentation
¶
Overview ¶
Package crawler implements gocrawl's concurrent crawl engine. It fetches pages within a configurable scope, captures redirect chains and robots.txt data, and produces a Result that analyzers consume. The engine has no knowledge of specific SEO/SEA checks.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
Engine crawls a website concurrently within the configured scope.
type Fetcher ¶
Fetcher retrieves a single URL and returns a populated Page. Implementations include the raw HTTPFetcher and the (stubbed) headless renderer.
type HTTPFetcher ¶
type HTTPFetcher struct {
// contains filtered or unexported fields
}
HTTPFetcher fetches pages over HTTP(S). It follows redirects manually so the full redirect chain is recorded on the resulting Page.
func NewHTTPFetcher ¶
func NewHTTPFetcher(opts Options) *HTTPFetcher
NewHTTPFetcher builds a fetcher from the given options. When opts.Proxies is non-empty the client routes each request through a proxy chosen by opts.ProxyRotation; otherwise Go's default proxy behavior (environment variables) applies. The User-Agent header is chosen per request from opts.UserAgents / opts.UserAgent via opts.UserAgentRotation.
type Link ¶
type Link struct {
URL string `json:"url"` // absolute, normalized
Anchor string `json:"anchor"` // visible text
Rel string `json:"rel"`
Nofollow bool `json:"nofollow"`
External bool `json:"external"` // points outside the seed host
}
Link is an outbound link discovered on a page.
type Options ¶
type Options struct {
MaxDepth int
MaxPages int
Concurrency int
RatePerSecond float64
UserAgent string
// UserAgents is an optional pool of User-Agent strings to rotate across. When non-empty it
// supersedes UserAgent; UserAgentRotation picks one per request.
UserAgents []string
UserAgentRotation RotationStrategy
// Proxies is an optional pool of proxy URLs (http, https, or socks5) to route requests
// through. When non-empty, ProxyRotation picks one per request. Empty leaves Go's default
// proxy behavior (honoring HTTP_PROXY/HTTPS_PROXY/NO_PROXY) in place.
Proxies []*url.URL
ProxyRotation RotationStrategy
Include []*regexp.Regexp
Exclude []*regexp.Regexp
RespectRobots bool
AllowSubdomains bool
FollowExternal bool
FollowNofollow bool
// StripQuery drops the query string when normalizing URLs, so URLs that differ only by
// their query collapse to one and are crawled once.
StripQuery bool
Timeout time.Duration
MaxBodyBytes int64
MaxRedirects int
// Verbose logs each fetch and every rate-limit change to stderr while crawling.
Verbose bool
// AdaptiveDelay automatically slows the crawl (halving requests-per-second, honoring any
// Retry-After header) when the server responds with HTTP 429 or 503.
AdaptiveDelay bool
}
Options controls crawl scope and politeness.
func DefaultOptions ¶
func DefaultOptions() Options
DefaultOptions returns conservative, polite defaults.
type Page ¶
type Page struct {
RequestedURL string `json:"requested_url"`
FinalURL string `json:"final_url"`
StatusCode int `json:"status_code"`
Header http.Header `json:"-"`
ContentType string `json:"content_type"`
Body []byte `json:"-"`
RawBody []byte `json:"-"` // pre-JS HTML captured during headless render; nil in raw mode
Doc *goquery.Document `json:"-"` // nil if not HTML or parse failed
Redirects []Redirect `json:"redirects,omitempty"`
Links []Link `json:"-"`
Depth int `json:"depth"`
Referrer string `json:"referrer,omitempty"`
Duration time.Duration `json:"duration_ms"`
FetchedAt time.Time `json:"fetched_at"`
Err string `json:"error,omitempty"`
Render *RenderResult `json:"render,omitempty"`
}
Page is the unit passed from the engine to analyzers.
type RenderResult ¶
type RenderResult struct {
Implemented bool `json:"implemented"`
Note string `json:"note,omitempty"`
LCP float64 `json:"lcp_ms,omitempty"`
FCP float64 `json:"fcp_ms,omitempty"`
CLS float64 `json:"cls,omitempty"`
TBT float64 `json:"tbt_ms,omitempty"`
TTFB float64 `json:"ttfb_ms,omitempty"`
// DataLayerPresent reports whether window.dataLayer was an array after the page rendered.
DataLayerPresent bool `json:"data_layer_present,omitempty"`
// DataLayer is the post-render snapshot of window.dataLayer, each element the raw JSON of
// one entry (a GTM event push or a gtag() arguments object). It is deliberately not
// serialized into reports: it can carry PII, so the datalayer analyzer emits sanitized
// findings from it instead. Nil in raw mode or when the page has no dataLayer.
DataLayer []json.RawMessage `json:"-"`
// Requests holds outbound request URLs observed during render, bounded to avoid unbounded
// growth. The datalayer analyzer uses it to confirm analytics/marketing tags actually
// fired a network beacon. Not serialized.
Requests []string `json:"-"`
}
RenderResult holds headless-rendering output, including lab-mode Core Web Vitals. Implemented is true when chromedp produced real measurements; false when rendering fell back to a raw fetch (Note carries the reason). Metric times are milliseconds; CLS is the unitless layout-shift score. Zero means not collected.
INP is a field-only metric and is not collected in lab mode. TBT (Total Blocking Time) is reported as a lab-mode proxy for responsiveness, matching Lighthouse.
type Result ¶
type Result struct {
Seed string `json:"seed"`
Pages []*Page `json:"pages"`
Robots map[string]*RobotsData `json:"robots,omitempty"`
StartedAt time.Time `json:"started_at"`
Finished time.Time `json:"finished_at"`
Opts Options `json:"-"`
// ThrottleEvents counts how many times adaptive delay reduced the crawl rate after HTTP
// 429/503 responses. FinalRate is the requests-per-second in effect when the crawl ended;
// it is meaningful only when ThrottleEvents > 0.
ThrottleEvents int `json:"throttle_events,omitempty"`
FinalRate float64 `json:"final_rate,omitempty"`
// contains filtered or unexported fields
}
Result is the complete output of a crawl, consumed by analyzers.
type RobotsData ¶
type RobotsData struct {
Host string `json:"host"`
Found bool `json:"found"`
Status int `json:"status"`
Sitemaps []string `json:"sitemaps,omitempty"`
// contains filtered or unexported fields
}
RobotsData is the parsed robots.txt for a single host.
func (*RobotsData) TestAgent ¶
func (r *RobotsData) TestAgent(path, userAgent string) bool
TestAgent reports whether the given path is allowed for userAgent.
type RotationStrategy ¶
type RotationStrategy int
RotationStrategy selects how a pool (of proxies or User-Agent strings) picks an entry for a given request. It is shared by the proxy pool and the User-Agent pool so both rotate with identical semantics.
const ( // RotateRoundRobin cycles through the pool in order, one entry per request. It is the // default once a pool has more than one entry. RotateRoundRobin RotationStrategy = iota // RotateRandom picks a uniformly random entry per request. RotateRandom // RotateStickyHost maps each target host to a fixed entry (by hash), so every request to a // given host uses the same proxy/User-Agent. For proxies this avoids a server seeing a // single session arrive from several IPs. RotateStickyHost // RotateOff disables rotation: the first entry is always used. A pool with a single entry // behaves this way regardless of strategy. RotateOff )
func ParseRotation ¶
func ParseRotation(s string) (RotationStrategy, error)
ParseRotation converts a config string into a RotationStrategy. An empty string defaults to round-robin, so simply supplying a multi-entry pool rotates without extra configuration.
type UAPool ¶
type UAPool struct {
// contains filtered or unexported fields
}
UAPool selects a User-Agent string per request. It is built from Options and shared by the raw HTTP fetcher and the headless renderer so both rotate identically. A nil pool, or one with no agents, yields the empty string (callers then leave the header unset).
func NewUAPool ¶
NewUAPool builds a User-Agent pool from opts. When UserAgents is set it is the pool; otherwise the single UserAgent (if any) is the only entry.
func (*UAPool) Default ¶
Default returns the pool's first agent without advancing rotation state. The headless renderer uses it as the browser-level User-Agent; per-navigation rotation is layered on top.