位元詩人 [Selenium] 程式設計教學:如何使用 Golang 操作瀏覽器

Facebook Twitter LinkedIn LINE Skype EverNote GMail Yahoo Email

前言

雖然 Selenium 並未提供給 Go (golang) 使用的官方 binding,透過 tebeka/selenium 這類社群套件,同樣可以用來操作 Selenium (或 WebDriver)。目前 tebeka/selenium 的範例偏少,大概只能看官方 API 來學如何使用。本文以兩個 Hello World 等級範例,分別來看如何透過 Selenium (或 WebDriver) 操作瀏覽器。

使用 Go (golang) 最大的好處,在於可以保護程式碼;因 Go (golang) 是編譯語言,無法直接透過反組譯得到原始碼。相對來說,使用先前介紹的 Java 來寫 Selenium 網路爬蟲沒有保護原始碼的功能,透過 Java 的 IDE 可以輕易地還原至原本的程式碼。不過,tebeka/selenium 這類社群套件的文件相對缺乏,只能看少數的範例和 API 文件慢慢去學該套件的使用方式。

tebeka/selenium 的作者預設的使用平台是 GNU/Linux 等類 Unix 系統 (參考此 issue);不過,經筆者實測,其實在 Windows 上也可以執行,只是要去掉類 Unix 系統特有的功能,像是 framebuffer。理論上,Selenium 可以支援所有的主流瀏覽器,但 tebeka/selenium 目前僅支援 Chrome 和 Firefox,故我們的範例會分別操作這兩個瀏覽器。

如何使用 Go (Golang) 透過 WebDriver 操作 Chrome

在本範例中,我們不透過 Selenium,而直接透過 Chrome Driver 操作 Chrome 瀏覽器:

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/tebeka/selenium"
)

const (
    port = 8080
)

func main() {
    opts := []selenium.ServiceOption{
        // Enable fake XWindow session.
        // selenium.StartFrameBuffer(),
        selenium.Output(os.Stderr), // Output debug information to STDERR
    }

    // Enable debug info.
    // selenium.SetDebug(true)
    service, err := selenium.NewChromeDriverService("chromedriver", port, opts...)
    if err != nil {
        panic(err)
    }
    defer service.Stop()

    caps := selenium.Capabilities{"browserName": "chrome"}
    wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://127.0.0.1:%d/wd/hub", port))
    if err != nil {
        panic(err)
    }
    defer wd.Quit()

    wd.Get("https://tw.yahoo.com")

    time.Sleep(5 * time.Second)
}

一開始要先建立服務 (service),這個服務會在背景執行。接著,會建立連線 (remote),一併建立 wd (webdriver) 物件,之後實際的動作會透過 wd 物件來執行。由於我們這個範例僅是展示如何開啟瀏覽器,故我們只簡單拜訪一下 Yahoo 台灣網站,沒有做其他動作。

如果開啟 framebuffer,這隻程式會藉由 xvfb 軟體開啟虛擬螢幕,這時候 Chrome 會在虛擬螢幕中執行,不會看到 Chrome 的畫面。這個方式比用 headless 模式更好,因為 headless 模式會被一些網站偵測出來,但虛擬螢幕視為真實的桌面環境,不算 headless 模式。但 framebuffer 是類 Unix 系統限定的功能,在 Windows 上要記得關掉。

如何使用 Go (Golang) 透過 Selenium 操作 Firefox

在操控 Firefox 時,除了需要 GeckoDriver 外,還要 Selenium 伺服器 (JAR 檔),故系統上要有 Java 環境。範例程式碼如下:

package main

import (
    "fmt"
    "os"
    "time"

    "github.com/tebeka/selenium"
)

const (
    seleniumPath    = "selenium-server-standalone-3.141.59.jar"
    geckoDriverPath = "geckodriver"
    port            = 8080
)

func main() {
    opts := []selenium.ServiceOption{
        // Enable fake XWindow session.
        // selenium.StartFrameBuffer(),
        selenium.GeckoDriver(geckoDriverPath),
        selenium.Output(os.Stderr), // Output debug information to STDERR
    }

    // Enable debug info.
    // selenium.SetDebug(true)
    service, err := selenium.NewSeleniumService(seleniumPath, port, opts...)
    if err != nil {
        panic(err)
    }
    defer service.Stop()

    caps := selenium.Capabilities{"browserName": "firefox"}
    wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://127.0.0.1:%d/wd/hub", port))
    if err != nil {
        panic(err)
    }
    defer wd.Quit()

    wd.Get("https://tw.yahoo.com")

    time.Sleep(5 * time.Second)
}

為什麼需要額外的 Selenium 伺服器呢?據 tebeka/selenium 開發者述,因為 GeckoDriver 的迭代較快,直接操作 GeckoDriver 易因 API 更動造成錯誤,故透過 Selenium 伺服器間接使用 GeckoDriver 操控 Firefox。

除了使用的瀏覽器相異,這兩隻程式的行為基本上是相同的,讀者可以自行閱讀程式碼。

結語

這兩隻程式只是連線到 Yahoo 台灣網站,並沒有進行複雜的網頁操作。我們這兩隻程式的目的在於確認開發環境是否正常運作,相當於 Hello World 程式。經筆者實測,除了 framebuffer 外,這些程式在 Windows 或類 Unix 系統皆可順利運行。日後有機會,或許我們會再用 tebeka/selenium 套件展示一些更複雜的應用。

關於作者

身為資訊領域碩士,位元詩人 (ByteBard) 認為開發應用程式的目的是為社會帶來價值。如果在這個過程中該軟體能成為永續經營的項目,那就是開發者和使用者雙贏的局面。

位元詩人喜歡用開源技術來解決各式各樣的問題,但必要時對專有技術也不排斥。閒暇之餘,位元詩人將所學寫成文章,放在這個網站上和大家分享。