GoScrapy Documentation

repository·main·Indexed 18 days ago

https://github.com/tech-engine/goscrapy

A high-performance web scraping framework for Go inspired by Python's Scrapy. It features a structured architecture with Spiders, an Engine, Scheduler, Worker Pool, and Middlewares. GoScrapy includes a CLI for project scaffolding, a signal-driven lifecycle for decoupled component communication, and Gosm for declarative data mapping using CSS, XPath, and JSON paths. It supports concurrent request execution and configurable export pipelines for formats like CSV and JSON.

Tokens
4.8K
Snippets
12
Records
20
Agent score
63%

What's inside GoScrapy

  1. Understand the GoScrapy Telemetry Architecture

    main

    GoScrapy uses a decoupled, three-layer telemetry architecture designed to collect high-performance metrics without blocking the crawler's execution path (the 'hot-path'). The system separates the high-speed recording of metrics from the periodic broadcasting of those metrics.

    The Three Layers:

    1. Collection Layer (Non-Blocking): Engine components like the Scheduler, Worker Pool, Pipeline Manager, and Stats Middleware maintain their own local metrics using sync/atomic counters. This prevents a central bottleneck during high-speed scraping.
    2. Aggregation Layer (Periodic): The TelemetryHub acts as an orchestrator. It uses a background loop with a Go time.Ticker to periodically poll registered components.
    3. Exhibition Layer (Broadcast): Observers (such as a TUI dashboard or loggers) consume the aggregated GlobalSnapshot to visualize or export data.

    Telemetry Flow:

    • Collection Phase: Components update internal atomic counters asynchronously during execution.
    • Broadcast Phase: At a set interval (e.g., every 500ms), the TelemetryHub calls Snapshot() on all components to collect ComponentSnapshot data, builds a GlobalSnapshot, and sends it to observers via OnSnapshot(GlobalSnapshot).
  2. How GoScrapy architecture works

    main

    GoScrapy follows a structured, concurrent data flow inspired by Python's Scrapy. The main components and their interactions are:

    1. Spider: Defines the extraction logic and yields requests and items.
    2. Engine: The central orchestrator that manages the lifecycle and coordinates between the Spider, Scheduler, and Worker Pool.
    3. Scheduler: Manages the queue of requests to be processed.
    4. Worker Pool: Executes requests concurrently.
    5. Middlewares: Intercept requests and responses (e.g., for proxy rotation, header manipulation, or TLS settings).
    6. HTTP Adapter: Performs the actual network fetching.
    7. Pipeline Manager & Pipelines: Handles the processing and export of scraped items (e.g., to CSV, JSON, MongoDB, etc.).
    8. Signal Bus: An event-driven system that allows components to communicate via decoupled signals.
  3. How the Signal-Driven Lifecycle works

    main

    Since v0.26.0, GoScrapy uses a signal-based architecture to decouple components. Instead of direct method calls, the framework emits signals that components subscribe to. This allows for non-intrusive extensions, such as a TUI Dashboard, to monitor the crawler.

    Key Lifecycle Events

    • SpiderOpened: Emitted when the engine starts. It automatically triggers the Open(ctx) method on your spider if implemented.
    • SpiderIdle: Emitted when the engine detects no active requests or pending items. This is the signal used for graceful shutdowns.
    • SpiderClosed: Emitted when the engine has completed all work.
    • ItemScraped/Dropped: Emitted by the Pipeline Manager to notify observers about the progress of items.
  4. Understand the GoScrapy Core Architecture

    main

    GoScrapy follows a decoupled, event-driven architecture inspired by Scrapy. The system is orchestrated by an Engine that coordinates several specialized components through a Signal Bus.

    Core Components

    • Spider: Contains your custom scraping logic (requests and parsing). Spiders are automatically discovered via reflection.
    • Engine: The central orchestrator that uses the Signal Bus for event-driven coordination.
    • Scheduler: Manages the priority queue of pending requests.
    • Worker Pool: Executes requests concurrently using a dynamic pool of workers.
    • Middlewares: Pluggable hooks used to modify requests or responses (e.g., for retries, cookie management, or statistics).
    • HTTP Adapter: The network layer responsible for fetching data (supports standard HTTP and TLS spoofing).
    • Pipeline Manager: Handles the processing and exporting of items yielded by the spider.
  5. Create a new GoScrapy project

    main

    Use the startproject command to scaffold a new scraping project. This command initializes a new Go module and generates the necessary project structure (e.g., main.go, spider.go, record.go, etc.). You will be prompted to run go mod tidy to resolve dependencies automatically.

    goscrapy startproject books_to_scrape
  6. Implement a Spider with Open, Parse, and Close methods

    main

    A Spider is the core component responsible for parsing logic. It implements the gos.ICoreSpider[*T] interface. The engine uses reflection to auto-discover and call the following lifecycle methods:

    1. Open(ctx context.Context): Called during engine startup. Use this to initiate your first requests using s.Request(ctx).
    2. parse(ctx context.Context, resp core.IResponseReader): Your custom parsing logic. Use resp.Bytes() or other reader methods to access content, and s.Yield(data) to send parsed records to the configured pipelines.
    3. Close(ctx context.Context): Called during engine shutdown for cleanup tasks.
    package myspider
    
    import (
    	"context"
    	"encoding/json"
    	"github.com/tech-engine/goscrapy/pkg/core"
    )
    
    // open is auto-called by goscrapy during engine startup
    func (s *Spider) Open(ctx context.Context) {
    	req := s.Request(ctx).Url("https://httpbin.org/get")
    	s.Parse(req, s.parse)
    }
    
    func (s *Spider) parse(ctx context.Context, resp core.IResponseReader) {
    	s.Logger().Infof("status: %d", resp.StatusCode())
    
    	var data Record
    	if err := json.Unmarshal(resp.Bytes(), &data); err != nil {
    		s.Logger().Errorf("failed to unmarshal record: %v", err)
    		return
    	}
    
    	// Yield sends the data securely to your configured pipelines
    	s.Yield(&data)
    }
    
    // close is auto-called by goscrapy during engine shutdown
    func (s *Spider) Close(ctx context.Context) {
    }
  7. Install the GoScrapy CLI

    main

    Install the GoScrapy command-line interface using go install. This provides both the goscrapy command and the gos alias for scaffolding and project management.

    Requirements:

    • Go 1.26 or higher
    go install github.com/tech-engine/goscrapy/cmd/...@latest
  8. Configure export pipelines in settings.go

    main

    GoScrapy uses a centralized settings.go file (automatically generated by the CLI) to manage middlewares and export pipelines. You can define pipelines by creating instances of specific pipeline types (e.g., csv.New[*T]) and adding them to the PIPELINES slice of type []engine.IPipeline[*T].

    package myspider
    
    import (
    	"github.com/tech-engine/goscrapy/pkg/engine"
    	"github.com/tech-engine/goscrapy/pkg/builtin/pipelines/csv"
    )
    
    // Prepare CSV export pipeline
    var export2CSV = csv.New[*Record](csv.Options{
    	Filename: "itstimeitsnowornever.csv",
    })
    
    // Export to CSV instantly
    var PIPELINES = []engine.IPipeline[*Record]{
    	export2CSV,
    }
  9. Implement Spider lifecycle methods for Auto-Discovery

    main

    GoScrapy uses reflection to automatically connect your spider's methods to the internal signal bus during RegisterSpider. To take advantage of this, implement the following methods in your spider struct:

    • Open(context.Context): Called when the spider starts.
    • Close(context.Context): Called when the spider finishes.
    • Idle(context.Context): Called when the engine detects no active work.
    • Error(context.Context, error): Called when an error occurs.
  10. Configure logging via GOS_LOG_LEVEL

    main

    GoScrapy has a built-in logging system. You can control the verbosity of the framework output by setting the GOS_LOG_LEVEL environment variable.

    Supported levels:

    • DEBUG: Detailed execution trace.
    • INFO: Basic startup/shutdown info (Default).
    • WARN: Warnings and retry notifications.
    • ERROR: Fatal errors.
    • NONE: Completely disable framework logging.

    For custom logging implementations, you can use the .WithLogger() method during application setup with an object implementing the core.ILogger interface.

  11. Use signals to hook into the scraping lifecycle

    main

    GoScrapy uses a central, strongly-typed signal bus. You can subscribe to events using a fluent builder pattern on the application instance. This allows you to monitor the engine, track item progress, or handle errors without coupling your logic to the core components.

    Supported Signals:

    CategorySignalTriggered when...
    EngineEngineStartedThe engine has finished initialization and is starting.
    EngineStoppedThe engine has finished all work and completed its shutdown.
    SpiderSpiderOpenedA spider is registered and ready to begin (auto-calls Open method).
    SpiderClosedA spider has finished all its tasks (auto-calls Close method).
    SpiderIdleA spider has no active requests or pending items (auto-calls Idle method).
    SpiderErrorA spider encounters an unhandled error (auto-calls Error method).
    ItemItemScrapedAn item has successfully passed through all configured pipelines.
    ItemDroppedAn item was explicitly dropped by a pipeline using engine.ErrDropItem.
    ItemErrorA pipeline returned a non-nil error while processing an item.
    RequestRequestScheduledA new request has been added to the scheduler.
    RequestDroppedA request was dropped due to a full queue or other limitations.
    RequestErrorA request failed during execution (e.g., network timeout).
    ResponseReceivedA response has been received from the downloader and is about to be parsed.
    app, _ := gos.New[*MyRecord]()
     
    app.OnEngineStarted(func(ctx context.Context) {
        log.Println("engine started")
    }).
    OnItemScraped(func(ctx context.Context, item *MyRecord) {
        log.Printf("item scraped: %s", item.Title)
    }).
    OnSpiderError(func(ctx context.Context, err error) {
        log.Printf("spider error: %v", err)
    })