last modified August 24, 2023
Go chromedp tutorial shows how to automate browsers in Golang with chromedp.
The chromedp is a Go library which provides a high-level API to control Chromium over the DevTools Protocol. It allows to use a browser in a headless mode (the default mode), which works without the UI. This is great for scripting.
$ go version go version go1.18.1 linux/amd64
We use Go version 1.18.
Get outer HTML
chromedp.OuterHTMLpackage main
import (
"context"
"fmt"
"log"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
url := "http://webcode.me"
var data string
if err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.OuterHTML("html", &data, chromedp.ByQuery),
); err != nil {
log.Fatal(err)
}
fmt.Println(data)
}
The example retrieves the home page of webcode.me.
ctx, cancel := chromedp.NewContext(context.Background()) defer cancel()
chromedp.NewContextif err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.OuterHTML("html", &data, chromedp.ByQuery),
); err != nil {
log.Fatal(err)
}
RunhtmlGet title
chromedp.Titlepackage main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"strings"
"github.com/chromedp/chromedp"
)
func writeHTML(content string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
io.WriteString(w, strings.TrimSpace(content))
})
}
func main() {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ts := httptest.NewServer(writeHTML(`
<head>
<title>Home page</title>
</head>
<body>
<p>Hello there!</a>
</body>
`))
defer ts.Close()
var title string
if err := chromedp.Run(ctx,
chromedp.Navigate(ts.URL),
chromedp.Title(&title),
); err != nil {
log.Fatal(err)
}
fmt.Println(title)
}
In the example, we create our built-in web server that sends a tiny web page. We retrieve its title.
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/html")
io.WriteString(w, strings.TrimSpace(content))
})
text/htmlts := httptest.NewServer(writeHTML(`
<head>
<title>Home page</title>
</head>
<body>
<p>Hello there!</a>
</body>
`))
httptest.NewServerdefer ts.Close()
At the end of the program, the test server is closed.
if err := chromedp.Run(ctx,
chromedp.Navigate(ts.URL),
chromedp.Title(&title),
); err != nil {
log.Fatal(err)
}
chromedp.Title$ go run title.go Home page
Setting timeouts
We can experience deadlocks with our tasks. To prevent them, we can set up timeouts.
package main
import (
"context"
"fmt"
"log"
"strings"
"time"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(context.Background())
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 5*time.Second)
defer cancel()
url := "http://webcode.me"
var res string
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.Text("body", &res, chromedp.NodeVisible),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(strings.TrimSpace(res))
}
bodyctx, cancel = context.WithTimeout(ctx, 5*time.Second) defer cancel()
We set a timeout of 5 seconds.
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.Text("body", &res, chromedp.NodeVisible),
)
bodychromedp.TextClick action
chromedp.Clickpackage main
import (
"context"
"log"
"time"
"github.com/chromedp/chromedp"
"github.com/chromedp/chromedp/device"
)
func main() {
ctx, cancel := chromedp.NewContext(
context.Background(),
)
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 15*time.Second)
defer cancel()
url := "http://webcode.me/click.html"
var ua string
err := chromedp.Run(ctx,
chromedp.Emulate(device.IPhone11),
chromedp.Navigate(url),
chromedp.Click("button", chromedp.NodeVisible),
chromedp.Text("#output", &ua),
)
if err != nil {
log.Fatal(err)
}
log.Printf("User agent: %s\n", ua)
}
In the example, we click on a button of a web page. The web page shows the client's user agent in the output div.
err := chromedp.Run(ctx,
chromedp.Emulate(device.IPhone11),
chromedp.Navigate(url),
chromedp.Click("button", chromedp.NodeVisible),
chromedp.Text("#output", &ua),
)
chromedp.EmulateCreate screenshot
chromedp.Screenshotchromedp.FullScreenshotpackage main
import (
"context"
"fmt"
"io/ioutil"
"log"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(
context.Background(),
)
defer cancel()
url := "http://webcode.me"
var buf []byte
if err := chromedp.Run(ctx, ElementScreenshot(url, "body", &buf)); err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile("body.png", buf, 0o644); err != nil {
log.Fatal(err)
}
if err := chromedp.Run(ctx, FullScreenshot(url, 90, &buf)); err != nil {
log.Fatal(err)
}
if err := ioutil.WriteFile("full.png", buf, 0o644); err != nil {
log.Fatal(err)
}
fmt.Println("screenshots created")
}
func ElementScreenshot(url, sel string, res *[]byte) chromedp.Tasks {
return chromedp.Tasks{
chromedp.Navigate(url),
chromedp.Screenshot(sel, res, chromedp.NodeVisible),
}
}
func FullScreenshot(url string, quality int, res *[]byte) chromedp.Tasks {
return chromedp.Tasks{
chromedp.Navigate(url),
chromedp.FullScreenshot(res, quality),
}
}
ioutil.WriteFileSubmit form
chromedp.SendKeyschromedp.Clickchromedp.Submitpackage main
import (
"context"
"fmt"
"log"
"time"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(
context.Background(),
)
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 6*time.Second)
defer cancel()
url := "http://webcode.me/submit/"
var res string
err := chromedp.Run(ctx,
chromedp.Navigate(url),
chromedp.SendKeys("input[name=name]", "Lucia"),
chromedp.SendKeys("input[name=message]", "Hello!"),
// chromedp.Click("button", chromedp.NodeVisible),
chromedp.Submit("input[name=name]"),
chromedp.Text("*", &res),
)
if err != nil {
log.Fatal(err)
}
fmt.Println(res)
}
The example fills in a form and receives a message.
chromedp.SendKeys("input[name=name]", "Lucia"),
chromedp.SendKeys("input[name=message]", "Hello!"),
We set two strings to the specified input tags.
// chromedp.Click("button", chromedp.NodeVisible),
chromedp.Submit("input[name=name]"),
chromedp.Clickchromedp.SubmitIn this article we have automated browsers in Go with chromedp.
Author
My name is Jan Bodnar and I am a passionate programmer with many years of programming experience. I have been writing programming articles since 2007. So far, I have written over 1400 articles and 8 e-books. I have over eight years of experience in teaching programming.