golang Selenium WebDriver 使用记录
收藏

Selenium WebDriver 直接通过浏览器自动化的本地接口来调用浏览器,以达到模拟浏览器行为的操作,如点击、选择、鼠标移动等。下面是记录个人使用golang 驱动的记录。

下载Selenium 驱动

Selenium 官网 https://www.seleniumhq.org 33 在那可以下载对应浏览器的驱动。你可以下载一个jar 包 Selenium Standalone Server 这个比较方便,可以运行在多个不同平台,但需要计算机有java 运行环境。

也可以根据自己的需求下载特定浏览器的驱动,如The Internet Explorer Driver ServerMozilla GeckoDriverGoogle Chrome Driver 等,这都是二进制文件,可以单独运行,不依赖java 环境。

Selenium 浏览器驱动

选择 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))
Relative Articles

    公众号
    关注公众号订阅更多技术干货!