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.OuterHTML
package 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.NewContext
if err := chromedp.Run(ctx, chromedp.Navigate(url), chromedp.OuterHTML("html", &data, chromedp.ByQuery), ); err != nil { log.Fatal(err) }
Runhtml
Get title
chromedp.Title
package 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/html
ts := httptest.NewServer(writeHTML(` <head> <title>Home page</title> </head> <body> <p>Hello there!</a> </body> `))
httptest.NewServer
defer 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)) }
body
ctx, 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.Text
Click action
chromedp.Click
package 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.Emulate
Create screenshot
chromedp.Screenshotchromedp.FullScreenshot
package 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.WriteFile
Submit form
chromedp.SendKeyschromedp.Clickchromedp.Submit
package 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.Submit
In 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.