Selenium WebDriver 直接通过浏览器自动化的本地接口来调用浏览器,以达到模拟浏览器行为的操作,如点击、选择、鼠标移动等。下面是记录个人使用golang 驱动的记录。
下载Selenium 驱动
Selenium 官网 https://www.seleniumhq.org 33 在那可以下载对应浏览器的驱动。你可以下载一个jar 包 Selenium Standalone Server
这个比较方便,可以运行在多个不同平台,但需要计算机有java 运行环境。
也可以根据自己的需求下载特定浏览器的驱动,如The Internet Explorer Driver Server
、Mozilla GeckoDriver
、Google Chrome Driver
等,这都是二进制文件,可以单独运行,不依赖java 环境。
选择 golang 驱动
这个库是目前比较完善的golang Selenium 驱动 https://github.com/tebeka/selenium 38,但官方没有IeDriverService
,这里我做一个补充,在service.go
里面添加一个函数:
func NewIeDriverService(path string, port int, opts ...ServiceOption) (*Service, error) {
cmd := exec.Command(path, "--port="+strconv.Itoa(port))
s, err := newService(cmd, port, opts...)
if err != nil {
return nil, err
}
if err := s.start(port); err != nil {
return nil, err
}
return s, nil
}
这样就可以在外部调用:
service, err := selenium.NewIeDriverService("IEDriverServer.exe", port, opts...)
if err != nil {
panic(err)
}
使用动态端口
因为启动 DriverService
需要占用一个端口,防止指定端口已经被占用,可以用下面的函数来动态获取一个可用端口。
func pickUnusedPort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
port := l.Addr().(*net.TCPAddr).Port
if err := l.Close(); err != nil {
return 0, err
}
return port, nil
}
完整例子
下面是在官方例子上做了一点修改,使用ieDriver
的例子:
package main
import (
"fmt"
"github.com/tebeka/selenium"
"net"
"os"
"time"
)
func pickUnusedPort() (int, error) {
addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:0")
if err != nil {
return 0, err
}
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return 0, err
}
port := l.Addr().(*net.TCPAddr).Port
if err := l.Close(); err != nil {
return 0, err
}
return port, nil
}
func main() {
port, err := pickUnusedPort()
fmt.Println("port", port)
opts := []selenium.ServiceOption{
selenium.Output(os.Stderr),
}
selenium.SetDebug(false)
service, err := selenium.NewIeDriverService("IEDriverServer.exe", port, opts...)
if err != nil {
panic(err)
}
defer service.Stop()
fmt.Println("here 1")
// 起新线程在新标签页打开窗口
go func() {
caps := selenium.Capabilities{"browserName": "internet explorer"}
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d", port))
if err != nil {
panic(err)
}
defer wd.Quit()
if err := wd.Get("https://www.baidu.com"); err != nil {
panic(err)
}
time.Sleep(time.Second * 10)
// 在窗口调用js 脚本
wd.ExecuteScript(`window.open("https://www.qq.com", "_blank");`, nil)
time.Sleep(time.Second * 20)
}()
// 打开一个在一个标签页里打开一个窗口
caps := selenium.Capabilities{"browserName": "internet explorer"}
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d", port))
if err != nil {
panic(err)
}
defer wd.Quit()
// Navigate to the simple playground interface.
if err := wd.Get("http://play.golang.org/?simple=1"); err != nil {
panic(err)
}
// Get a reference to the text box containing code.
elem, err := wd.FindElement(selenium.ByCSSSelector, "#code")
if err != nil {
panic(err)
}
// Remove the boilerplate code already in the text box.
if err := elem.Clear(); err != nil {
panic(err)
}
// Enter some new code in text box.
err = elem.SendKeys(`
package main
import "fmt"
func main() {
fmt.Println("Hello WebDriver!\n")
}
`)
if err != nil {
panic(err)
}
// Click the run button.
btn, err := wd.FindElement(selenium.ByCSSSelector, "#run")
if err != nil {
panic(err)
}
if err := btn.Click(); err != nil {
panic(err)
}
// Wait for the program to finish running and get the output.
outputDiv, err := wd.FindElement(selenium.ByCSSSelector, "#output")
if err != nil {
panic(err)
}
var output string
for {
output, err = outputDiv.Text()
if err != nil {
panic(err)
}
if output != "Waiting for remote server..." {
break
}
time.Sleep(time.Second * 1)
}
fmt.Println("waiting....")
time.Sleep(time.Second * 80)
// Example Output:
// Hello WebDriver!
//
// Program exited.
}
注意问题
如果用 Selenium Standalone Server
启动 NewRemote
时,路径不同,应设为:
wd, err := selenium.NewRemote(caps, fmt.Sprintf("http://localhost:%d/wd/hub", port))