golangの日記

Go言語を中心にプログラミングについてのブログ

Go言語(golang)のAgouti(Selenium WebDriver)の使い方

golang.png


Go言語でSelenium WebDriverを使うサードパーティパッケージAgoutiの使い方。
Webブラウザを自動化し、作成したウェブサイトのテストやウェブクローラーに使います。
ライブラリのAgoutiの他、Google Chromeを使う場合は、chromedriver が FireFoxを使う場合は、geckodriverが必要になるので、
ダウンロード、解凍して、パスの通った場所に配置するか capabilities の binary にパスを指定します。


AgoutiのGitHubページ

Agoutiの godocページ

Seleniumのドキュメントページ


Chromeのドライバーとドキュメント


FireFoxのドライバーとドキュメント





目次



パッケージのダウンロード

$ go get github.com/sclevine/agouti


基本的な使い方

example.com にアクセスしてタイトルを表示

package main

import (
    "fmt"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    driver := agouti.ChromeDriver()
    defer driver.Stop()

    if err := driver.Start(); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://example.com")
    fmt.Println(page.Title())
}



ヘッドレスモードで起動(ブラウザを非表示する)

chromeのオプション(引数)に --headless を指定します。

ヘッドレス Chrome ことはじめ https://developers.google.com/web/updates/2017/04/headless-chrome

package main

import (
    "fmt"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    options := agouti.ChromeOptions(
        "args", []string{
            "--headless",
            "--disable-gpu", // 暫定的に必要らしいです。
        })

    driver := agouti.ChromeDriver(options)
    defer driver.Stop()
    driver.Start()

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://example.com")
    fmt.Println(page.Title())
}



ブラウザのプロフィールを指定する

何も指定しない場合は、一時的な新しいプロフィールが使われるので、
--user-data-dir でパスを指定すれば、そのプロファイル(パスになければ作成されます)が使用されます。

package main

import (
    "fmt"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    options := agouti.ChromeOptions(
        "args", []string{
            "--user-data-dir=/path/to/profile",
        })

    driver := agouti.ChromeDriver(options)
    defer driver.Stop()
    driver.Start()

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://example.com")
    fmt.Println(page.Title())
}



拡張機能を使う

GoogleChromeの拡張機能を使う。ただし、ヘッドレスでは組み込めないので、
予めプロファイルを作成し、そこに拡張機能をインストールする必要があります。

package main

import (
    "fmt"
    "io/ioutil"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    crxBytes, _ := ioutil.ReadFile("/path/to/extension.crx")

    options := []agouti.Option{
        agouti.ChromeOptions(
            "args", []string{
                "--user-data-dir=/path/to/profile",
            }),
        agouti.ChromeOptions("extensions", [][]byte{crxBytes}),
    }

    driver := agouti.ChromeDriver(options...)
    defer driver.Stop()
    driver.Start()

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://example.com")
    fmt.Println(page.Title())
}



JavaScript を実行する

package main

import (
    "fmt"
    "net/http"

    "github.com/sclevine/agouti"
)

func main() {
    var options []agouti.Option

    client := new(http.Client)
    // httpクライアントを設定する(設定していない場合は、内部的にhttp.DefaultClientが使われる)
    options = append(options, agouti.HTTPClient(client))

    // なんのタイムアウトなのかわかりません。
    options = append(options, agouti.Timeout(190)) // seconds

    // SSL証明書が無効な場合は拒否する(デフォルトではすべて受け入れる)
    options = append(options, agouti.RejectInvalidSSL)

    capabilities := agouti.Capabilities{
        "args": []string{
            "--disable-gpu",
            "--headless",
        },
    }
    options = append(options, agouti.Desired(capabilities))

    driverPath := "/usr/local/bin/chromedriver"

    command := []string{driverPath, "--port={{.Port}}"}
    driver := agouti.NewWebDriver("http://{{.Address}}", command, options...)

    driver.Start()
    defer driver.Stop()

    // page ごとにオプションを変更する場合は以下のように NewPage に指定する
    page, err := driver.NewPage(options...)
    if err != nil {
        fmt.Println(err)
        return
    }

    // ポップアップの無効化
    if err := page.CancelPopup(); err != nil {
        fmt.Println(err)
    }

    // ページロードのタイムアウト(秒)
    if err := page.SetPageLoad(90); err != nil {
        fmt.Println(err)
    }

    // すぐに取得できない要素を待つ(秒)
    if err := page.SetImplicitWait(10); err != nil {
        fmt.Println(err)
    }

    if err := page.Navigate("https://example.com"); err != nil {
        fmt.Println(err)
        return
    }

    // JavaScriptを実行する
    javascript := `document.body.innerHTML = element; return "hello"`

    // スクリプト内で使う変数
    variables := map[string]interface{}{
        "element": interface{}("<h1>hello</h1>"),
    }

    // return で返ってくる値を受け取る
    var result string

    if err = page.RunScript(javascript, variables, &result); err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(result) // hello
    }
}



Google-Chrome のオプション

ChromeOptions の一覧は以下に載ってます。 https://sites.google.com/a/chromium.org/chromedriver/capabilities

package main

import (
    "fmt"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    driver := agouti.ChromeDriver(
        agouti.Browser("chrome"),
        agouti.Debug,
        agouti.ChromeOptions("prefs", map[string]interface{}{
            "download.directory_upgrade":              true,
            "download.default_directory":              "/home/xxx/Downloads", // ダウンロードするディレクトリ
            "download.prompt_for_download":            false,
            "plugins.always_open_pdf_externally":      true,
            "plugins.plugins_disabled":                "Chrome PDF Viewer",
            "profile.default_content_settings.popups": 0,
        }),
        agouti.ChromeOptions("args", []string{
            "--disable-gpu",
            "--headless",
            "--start-maximized", // ブラウザのサイズを指定する
        }),
    )

    if err := driver.Start(); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        os.Exit(1)
    }
    defer driver.Stop()

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://example.com")
    fmt.Println(page.Title())
}



FireFox のオプション

Socks5プロキシを使って接続する

package main

import (
    "fmt"
    "os"

    "github.com/sclevine/agouti"
)

func main() {
    options := []agouti.Option{
        agouti.Browser("firefox"),
    }

    capabilities := agouti.Capabilities{
        "moz:firefoxOptions": map[string]interface{}{
            "args": []string{
                "--headless",
            },
            "prefs": map[string]interface{}{
                "network.proxy.type":       1,
                "network.proxy.socks":      "127.0.0.1",
                "network.proxy.socks_port": 9050,
            },
        },
    }

    driver := agouti.GeckoDriver(append(options, agouti.Desired(capabilities))...)

    if err := driver.Start(); err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        os.Exit(1)
    }
    defer driver.Stop()

    page, err := driver.NewPage()
    if err != nil {
        fmt.Fprintf(os.Stderr, "%s\n", err)
        return
    }

    page.Navigate("https://check.torproject.org/")

    fmt.Println(page.Title())
}