Table of contents

How do I use Colly with cloud services like AWS or Google Cloud?

Running Colly web scrapers on cloud platforms like AWS and Google Cloud provides scalability, cost-effectiveness, and managed infrastructure benefits. This guide covers deployment strategies, configuration considerations, and practical examples for running Colly applications in cloud environments.

AWS Deployment Options

AWS Lambda with Colly

AWS Lambda is ideal for lightweight, event-driven scraping tasks. Here's how to deploy a Colly scraper as a Lambda function:

package main

import (
    "context"
    "encoding/json"
    "log"
    "time"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/gocolly/colly/v2"
    "github.com/gocolly/colly/v2/debug"
)

type ScrapingRequest struct {
    URL    string            `json:"url"`
    Config map[string]string `json:"config"`
}

type ScrapingResponse struct {
    StatusCode int                    `json:"statusCode"`
    Body       string                 `json:"body"`
    Data       []map[string]string   `json:"data"`
}

func handleRequest(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    var req ScrapingRequest
    if err := json.Unmarshal([]byte(request.Body), &req); err != nil {
        return events.APIGatewayProxyResponse{
            StatusCode: 400,
            Body:       "Invalid request body",
        }, nil
    }

    // Create collector with Lambda-optimized settings
    c := colly.NewCollector(
        colly.Debugger(&debug.LogDebugger{}),
        colly.UserAgent("Lambda-Colly-Scraper/1.0"),
    )

    // Configure timeouts for Lambda execution limits
    c.SetRequestTimeout(30 * time.Second)

    var results []map[string]string

    c.OnHTML("your-selector", func(e *colly.HTMLElement) {
        result := map[string]string{
            "title": e.ChildText("h1"),
            "link":  e.Attr("href"),
        }
        results = append(results, result)
    })

    c.OnError(func(r *colly.Response, err error) {
        log.Printf("Error scraping %s: %v", r.Request.URL, err)
    })

    if err := c.Visit(req.URL); err != nil {
        return events.APIGatewayProxyResponse{
            StatusCode: 500,
            Body:       "Scraping failed",
        }, nil
    }

    response := ScrapingResponse{
        StatusCode: 200,
        Body:       "Success",
        Data:       results,
    }

    responseBody, _ := json.Marshal(response)
    return events.APIGatewayProxyResponse{
        StatusCode: 200,
        Body:       string(responseBody),
        Headers: map[string]string{
            "Content-Type": "application/json",
        },
    }, nil
}

func main() {
    lambda.Start(handleRequest)
}

AWS ECS/Fargate Deployment

For more complex scraping operations, use AWS ECS with Fargate:

# Dockerfile for ECS deployment
FROM golang:1.19-alpine AS builder

WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download

COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o scraper main.go

FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/

COPY --from=builder /app/scraper .

CMD ["./scraper"]
// ECS-optimized Colly scraper
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "log"
    "os"
    "time"

    "github.com/gocolly/colly/v2"
    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

func main() {
    // Get configuration from environment variables
    targetURL := os.Getenv("TARGET_URL")
    s3Bucket := os.Getenv("S3_BUCKET")

    // Create AWS session
    sess := session.Must(session.NewSession(&aws.Config{
        Region: aws.String("us-east-1"),
    }))
    s3Client := s3.New(sess)

    c := colly.NewCollector()

    // Configure for cloud environment
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 5, // Adjust based on ECS task resources
        Delay:       1 * time.Second,
    })

    c.OnHTML("article", func(e *colly.HTMLElement) {
        // Process and store data in S3
        data := map[string]string{
            "title":   e.ChildText("h1"),
            "content": e.ChildText("p"),
            "url":     e.Request.URL.String(),
        }

        // Store in S3
        storeInS3(s3Client, s3Bucket, data)
    })

    c.Visit(targetURL)
}

func storeInS3(s3Client *s3.S3, bucket string, data map[string]string) {
    jsonData, _ := json.Marshal(data)
    key := fmt.Sprintf("scraping-results/%d.json", time.Now().Unix())

    _, err := s3Client.PutObject(&s3.PutObjectInput{
        Bucket:      aws.String(bucket),
        Key:         aws.String(key),
        Body:        bytes.NewReader(jsonData),
        ContentType: aws.String("application/json"),
    })

    if err != nil {
        log.Printf("Failed to store data in S3: %v", err)
    }
}

Google Cloud Platform Integration

Google Cloud Functions

Deploy Colly scrapers as serverless functions on Google Cloud:

package cloudscraper

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"

    "github.com/gocolly/colly/v2"
)

type ScrapingRequest struct {
    URL      string `json:"url"`
    Selector string `json:"selector"`
}

func ScrapeWebsite(w http.ResponseWriter, r *http.Request) {
    var req ScrapingRequest
    if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
        http.Error(w, "Invalid request", http.StatusBadRequest)
        return
    }

    c := colly.NewCollector(
        colly.UserAgent("GCP-Colly-Scraper/1.0"),
    )

    var results []string

    c.OnHTML(req.Selector, func(e *colly.HTMLElement) {
        results = append(results, e.Text)
    })

    c.OnError(func(r *colly.Response, err error) {
        fmt.Printf("Error: %v\n", err)
    })

    if err := c.Visit(req.URL); err != nil {
        http.Error(w, "Scraping failed", http.StatusInternalServerError)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]interface{}{
        "results": results,
        "count":   len(results),
    })
}

Google Cloud Run

For containerized deployments with more resources:

# cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/docker'
  args: ['build', '-t', 'gcr.io/$PROJECT_ID/colly-scraper', '.']
- name: 'gcr.io/cloud-builders/docker'
  args: ['push', 'gcr.io/$PROJECT_ID/colly-scraper']
- name: 'gcr.io/cloud-builders/gcloud'
  args: [
    'run', 'deploy', 'colly-scraper',
    '--image', 'gcr.io/$PROJECT_ID/colly-scraper',
    '--region', 'us-central1',
    '--platform', 'managed',
    '--allow-unauthenticated'
  ]

Cloud-Specific Configurations

Memory and Timeout Optimization

package main

import (
    "time"
    "github.com/gocolly/colly/v2"
)

func createCloudOptimizedCollector() *colly.Collector {
    c := colly.NewCollector(
        colly.Async(true), // Enable async for better performance
    )

    // Limit concurrent requests based on cloud function limits
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: 2, // Conservative for serverless
        Delay:       500 * time.Millisecond,
    })

    // Set reasonable timeouts for cloud environments
    c.SetRequestTimeout(30 * time.Second)

    // Configure cache for repeated requests
    c.CacheDir = "/tmp/colly-cache"

    return c
}

Environment Variable Configuration

type CloudConfig struct {
    MaxConcurrency int           `env:"MAX_CONCURRENCY" envDefault:"5"`
    RequestDelay   time.Duration `env:"REQUEST_DELAY" envDefault:"1s"`
    UserAgent      string        `env:"USER_AGENT" envDefault:"Cloud-Colly/1.0"`
    ProxyURL       string        `env:"PROXY_URL"`
}

func loadConfig() CloudConfig {
    var cfg CloudConfig
    // Use a library like env to load configuration
    return cfg
}

Data Storage Integration

AWS S3 Integration

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "time"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/s3"
)

type ScrapedData struct {
    Title   string    `json:"title"`
    Content string    `json:"content"`
    URL     string    `json:"url"`
    Time    time.Time `json:"scraped_at"`
}

func storeResultsInS3(results []ScrapedData) error {
    sess := session.Must(session.NewSession())
    s3Client := s3.New(sess)

    data, err := json.Marshal(results)
    if err != nil {
        return err
    }

    key := fmt.Sprintf("scraping-results/%d.json", time.Now().Unix())

    _, err = s3Client.PutObject(&s3.PutObjectInput{
        Bucket:      aws.String("your-scraping-bucket"),
        Key:         aws.String(key),
        Body:        bytes.NewReader(data),
        ContentType: aws.String("application/json"),
    })

    return err
}

Google Cloud Storage Integration

package main

import (
    "context"
    "encoding/json"
    "fmt"
    "time"

    "cloud.google.com/go/storage"
)

func storeResultsInGCS(ctx context.Context, results []ScrapedData) error {
    client, err := storage.NewClient(ctx)
    if err != nil {
        return err
    }
    defer client.Close()

    bucket := client.Bucket("your-scraping-bucket")
    obj := bucket.Object(fmt.Sprintf("results-%d.json", time.Now().Unix()))

    w := obj.NewWriter(ctx)
    defer w.Close()

    return json.NewEncoder(w).Encode(results)
}

Monitoring and Logging

CloudWatch Integration (AWS)

package main

import (
    "log"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/cloudwatch"
)

func publishMetrics(scraped int, errors int) {
    sess := session.Must(session.NewSession())
    cw := cloudwatch.New(sess)

    _, err := cw.PutMetricData(&cloudwatch.PutMetricDataInput{
        Namespace: aws.String("Colly/Scraping"),
        MetricData: []*cloudwatch.MetricDatum{
            {
                MetricName: aws.String("ScrapedItems"),
                Value:      aws.Float64(float64(scraped)),
                Unit:       aws.String("Count"),
            },
            {
                MetricName: aws.String("Errors"),
                Value:      aws.Float64(float64(errors)),
                Unit:       aws.String("Count"),
            },
        },
    })

    if err != nil {
        log.Printf("Failed to publish metrics: %v", err)
    }
}

Structured Logging for Cloud Environments

package main

import (
    "encoding/json"
    "log"
    "time"
)

type LogEntry struct {
    Timestamp time.Time `json:"timestamp"`
    Level     string    `json:"level"`
    Message   string    `json:"message"`
    URL       string    `json:"url,omitempty"`
    Error     string    `json:"error,omitempty"`
}

func logStructured(level, message, url, errorMsg string) {
    entry := LogEntry{
        Timestamp: time.Now(),
        Level:     level,
        Message:   message,
        URL:       url,
        Error:     errorMsg,
    }

    jsonData, _ := json.Marshal(entry)
    log.Println(string(jsonData))
}

Deployment Automation

Terraform Configuration

# terraform/main.tf
resource "aws_lambda_function" "colly_scraper" {
  filename         = "colly-scraper.zip"
  function_name    = "colly-web-scraper"
  role            = aws_iam_role.lambda_role.arn
  handler         = "main"
  runtime         = "go1.x"
  timeout         = 60
  memory_size     = 512

  environment {
    variables = {
      S3_BUCKET = aws_s3_bucket.scraping_results.bucket
    }
  }
}

resource "aws_iam_role" "lambda_role" {
  name = "colly-lambda-role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_s3_bucket" "scraping_results" {
  bucket = "your-scraping-results-bucket"
}

GitHub Actions Deployment

# .github/workflows/deploy.yml
name: Deploy Colly Scraper

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2

    - name: Set up Go
      uses: actions/setup-go@v2
      with:
        go-version: 1.19

    - name: Build
      run: |
        GOOS=linux GOARCH=amd64 go build -o main main.go
        zip deployment.zip main

    - name: Deploy to AWS Lambda
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: us-east-1

    - run: |
        aws lambda update-function-code \
          --function-name colly-web-scraper \
          --zip-file fileb://deployment.zip

Best Practices for Cloud Deployment

1. Resource Management

  • Set appropriate memory limits based on scraping complexity
  • Configure timeouts to prevent hanging functions
  • Use connection pooling for database connections
  • Monitor resource usage and adjust accordingly

2. Error Handling and Retries

func setupErrorHandling(c *colly.Collector) {
    c.OnError(func(r *colly.Response, err error) {
        // Log errors to cloud logging service
        logStructured("error", "Scraping failed", r.Request.URL.String(), err.Error())

        // Implement retry logic for transient errors
        if isRetryableError(err) {
            scheduleRetry(r.Request.URL.String())
        }
    })
}

func isRetryableError(err error) bool {
    // Define logic to determine if an error is retryable
    return err.Error() != "404 Not Found"
}

func scheduleRetry(url string) {
    // Implementation depends on your cloud provider
    // Could use SQS, Cloud Tasks, etc.
}

3. Rate Limiting and Compliance

func setupRateLimiting(c *colly.Collector) {
    // Implement distributed rate limiting for cloud environments
    c.Limit(&colly.LimitRule{
        DomainGlob:  "*",
        Parallelism: getConfiguredParallelism(),
        Delay:       getConfiguredDelay(),
    })

    // Respect robots.txt
    c.OnRequest(func(r *colly.Request) {
        // Add custom headers for identification
        r.Headers.Set("X-Scraper-ID", "cloud-colly-v1")
    })
}

func getConfiguredParallelism() int {
    // Read from environment variables or configuration service
    return 3
}

func getConfiguredDelay() time.Duration {
    // Read from environment variables or configuration service
    return time.Second
}

Security Considerations

When deploying Colly scrapers to cloud platforms, ensure proper security measures:

IAM Policies and Roles

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject"
      ],
      "Resource": "arn:aws:s3:::your-scraping-bucket/*"
    },
    {
      "Effect": "Allow",
      "Action": [
        "cloudwatch:PutMetricData"
      ],
      "Resource": "*"
    }
  ]
}

Secure Configuration Management

package main

import (
    "os"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/ssm"
)

func getSecureConfig(paramName string) (string, error) {
    sess := session.Must(session.NewSession())
    ssmClient := ssm.New(sess)

    result, err := ssmClient.GetParameter(&ssm.GetParameterInput{
        Name:           aws.String(paramName),
        WithDecryption: aws.Bool(true),
    })

    if err != nil {
        return "", err
    }

    return *result.Parameter.Value, nil
}

Advanced Integration Patterns

Event-Driven Scraping

package main

import (
    "context"
    "encoding/json"

    "github.com/aws/aws-lambda-go/events"
    "github.com/aws/aws-lambda-go/lambda"
    "github.com/gocolly/colly/v2"
)

func handleSQSEvent(ctx context.Context, sqsEvent events.SQSEvent) error {
    for _, message := range sqsEvent.Records {
        var scrapeRequest struct {
            URL      string `json:"url"`
            Selector string `json:"selector"`
        }

        if err := json.Unmarshal([]byte(message.Body), &scrapeRequest); err != nil {
            continue
        }

        // Process scraping request
        c := colly.NewCollector()
        c.OnHTML(scrapeRequest.Selector, func(e *colly.HTMLElement) {
            // Process scraped data
        })

        c.Visit(scrapeRequest.URL)
    }

    return nil
}

For more advanced browser automation scenarios that complement Colly's capabilities, consider how to handle browser sessions in Puppeteer for JavaScript-heavy sites, or explore how to use Puppeteer with Docker for containerized deployments.

Cloud deployment of Colly scrapers provides excellent scalability and cost-effectiveness for web scraping operations. By following these patterns and best practices, you can build robust, maintainable scraping solutions that leverage the full power of cloud infrastructure while maintaining security, performance, and compliance standards.

Try WebScraping.AI for Your Web Scraping Needs

Looking for a powerful web scraping solution? WebScraping.AI provides an LLM-powered API that combines Chromium JavaScript rendering with rotating proxies for reliable data extraction.

Key Features:

  • AI-powered extraction: Ask questions about web pages or extract structured data fields
  • JavaScript rendering: Full Chromium browser support for dynamic content
  • Rotating proxies: Datacenter and residential proxies from multiple countries
  • Easy integration: Simple REST API with SDKs for Python, Ruby, PHP, and more
  • Reliable & scalable: Built for developers who need consistent results

Getting Started:

Get page content with AI analysis:

curl "https://api.webscraping.ai/ai/question?url=https://example.com&question=What is the main topic?&api_key=YOUR_API_KEY"

Extract structured data:

curl "https://api.webscraping.ai/ai/fields?url=https://example.com&fields[title]=Page title&fields[price]=Product price&api_key=YOUR_API_KEY"

Try in request builder

Related Questions

Get Started Now

WebScraping.AI provides rotating proxies, Chromium rendering and built-in HTML parser for web scraping
Icon