Skip to content
On this page

Web scraping with Go (Golang) means using net/http or the Colly framework to fetch pages and goquery to extract data with jQuery-like selectors. Go is a great fit for scraping at scale: it compiles to a single fast binary and its goroutines make concurrent fetching simple and cheap. The common stack is net/http + goquery for simple jobs, Colly for full crawlers, and chromedp for JavaScript-rendered pages.

Quick facts

HTML parsinggoquery v1.12.x — jQuery-style CSS selectors
Crawl frameworkColly v2.2.x — collectors, callbacks, built-in concurrency
JavaScript pageschromedp (drives Chrome via CDP) or go-rod
ConcurrencyGoroutines + channels — cheap parallel fetching
Module pathsgithub.com/gocolly/colly/v2, github.com/PuerkitoBio/goquery

Your first Go scraper with net/http + goquery

The simplest Go scraper uses the standard library's net/http to fetch a page and goquery to parse it. Install goquery with go get github.com/PuerkitoBio/goquery.

package main

import (
	"fmt"
	"log"
	"net/http"

	"github.com/PuerkitoBio/goquery"
)

func main() {
	res, err := http.Get("https://books.toscrape.com/")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	doc, err := goquery.NewDocumentFromReader(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	doc.Find("article.product_pod").Each(func(i int, s *goquery.Selection) {
		title, _ := s.Find("h3 a").Attr("title")
		price := s.Find(".price_color").Text()
		fmt.Printf("%s | %s\n", title, price)
	})
}

goquery mirrors jQuery: Find() takes a CSS selector, Each() iterates matches, Text() reads text, and Attr() returns an attribute plus an "exists" boolean. It is the most idiomatic way to parse HTML in Go.

Crawling with Colly

For anything beyond a single page, Colly is the framework of choice. It gives you a collector with event callbacks, automatic link-following, and built-in concurrency and rate-limiting. Install: go get github.com/gocolly/colly/v2.

package main

import (
	"fmt"

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

func main() {
	c := colly.NewCollector(
		colly.AllowedDomains("books.toscrape.com"),
		colly.UserAgent("Mozilla/5.0 (Windows NT 10.0; Win64; x64)"),
	)

	// Extract each product.
	c.OnHTML("article.product_pod", func(e *colly.HTMLElement) {
		title := e.ChildAttr("h3 a", "title")
		price := e.ChildText(".price_color")
		fmt.Printf("%s | %s\n", title, price)
	})

	// Follow the "next" pagination link.
	c.OnHTML("li.next > a", func(e *colly.HTMLElement) {
		e.Request.Visit(e.Request.AbsoluteURL(e.Attr("href")))
	})

	c.Visit("https://books.toscrape.com/")
}

Colly's callback model — OnHTML, OnRequest, OnError — keeps crawlers readable as they grow. Enable parallelism with colly.Async(true) and a LimitRule to control concurrency and delay per domain.

Concurrent fetching with goroutines

Go's superpower for scraping is cheap concurrency. To fetch many URLs in parallel, launch goroutines and bound them with a semaphore channel so you do not overwhelm the target:

func fetchAll(urls []string) {
	sem := make(chan struct{}, 10) // max 10 in flight
	var wg sync.WaitGroup

	for _, u := range urls {
		wg.Add(1)
		go func(url string) {
			defer wg.Done()
			sem <- struct{}{}        // acquire
			defer func() { <-sem }() // release

			res, err := http.Get(url)
			if err != nil {
				return
			}
			defer res.Body.Close()
			// ...parse res.Body with goquery...
		}(u)
	}
	wg.Wait()
}

This pattern scrapes hundreds of pages concurrently while keeping a hard cap on simultaneous requests. Colly offers the same via its async mode and limit rules if you prefer the framework to manage it.

Which Go scraping library should you use?

LibraryTypeRuns JS?Best for
net/http + goqueryFetch + parseNoSimple static scraping
CollyCrawl frameworkNoMulti-page crawls, concurrency built in
chromedpBrowser automationYesJavaScript-rendered pages
go-rodBrowser automationYesModern browser control with fine-grained options

Use net/http + goquery for quick jobs, Colly for real crawlers, and chromedp or go-rod only when the page needs JavaScript.

The hard part: handling anti-bot blocking

Go is fast, but raw speed does not help when a site's anti-bot defenses reject the request. A net/http request has a TLS and header signature anti-bot vendors flag immediately, and even chromedp leaks headless signals. You cannot parse a Cloudflare or DataDome challenge page.

The fix — residential proxies, a real browser TLS fingerprint, and CAPTCHA challenges — is a lot to maintain in Go. A scraping API handles it server-side; your Go code POSTs the URL and parses the returned HTML with goquery:

Code example

go
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
)

func main() {
	payload, _ := json.Marshal(map[string]string{
		"cmd": "request.get",
		"url": "https://example.com/protected",
	})

	resp, err := http.Post(
		"https://api.your-scraping-provider.com/v1?key=YOUR_API_KEY",
		"application/json",
		bytes.NewReader(payload),
	)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	var result map[string]interface{}
	json.Unmarshal(body, &result)

	// Fully rendered, unblocked HTML -- parse it with goquery as usual.
	html := result["solution"].(map[string]interface{})["response"].(string)
	fmt.Println(html[:200])
}

Next in Scraping in every language · 5 of 8

The most widely deployed server language on the web.

Web Scraping With PHP: A Complete 2026 Guide

Related terms

Concept map

Concept map

How Web Scraping With Go (Golang): A Complete 2026 Guide connects

The terms most directly tied to this one. Hover a node to see its neighbours, click to preview, drag to rearrange.

0 terms · 0 connections
You are here · Web Scraping by Language
Building map…

Frequently asked questions

What is the best library for web scraping with Go?

For simple jobs, the standard net/http plus goquery (jQuery-style CSS selectors) is the idiomatic choice. For real crawlers, Colly adds collectors, automatic link-following, concurrency, and rate-limiting. For JavaScript-rendered pages, chromedp or go-rod drive a real Chrome browser. Most Go scrapers start with goquery and adopt Colly as the crawl grows.

Can Go scrape JavaScript-rendered pages?

Yes — goquery and Colly only see server HTML, so for JavaScript-rendered content use chromedp (which controls Chrome over the DevTools Protocol) or go-rod. Both run a real browser. As a faster alternative, inspect the Network tab, find the JSON API the page calls, and request it directly with net/http.

Is Go good for web scraping at scale?

Very. Go compiles to a fast single binary and its goroutines make concurrent fetching cheap and simple, so it handles high-throughput crawling well with modest resource use. Colly’s built-in async mode and limit rules make large, polite crawls straightforward. It is one of the best languages for performance-critical scraping.

How do I avoid blocks when scraping with Go?

Set a realistic User-Agent, throttle and rotate residential proxies, and cap concurrency per domain. Against Cloudflare, DataDome, or Akamai you also need a browser-grade TLS fingerprint , which is significant work in Go. Many teams route hard targets through a scraping API that manages proxies, fingerprinting, and challenges server-side.

Last updated: 2026-06-08