Golang使用selenium操作Chrome


Golang使用selenium操作Chrome

1.需求

解决自动化登录的问题,顺便可以解决爬虫问题。

2.基本概念

selenium:  Selenium 是一个用于 Web 应用程序测试的工具,Selenium 测试直接自动运行在浏览器中,就像真正的用户在手工操作一样。

    webdriver:   chromeDriver是谷歌为网站开发人员提供的自动化测试工具。

    selenium和webdriver其实原来是两个不同的开源项目,后来selenium2就把selenium1(RC)和webdriver合并到一起,还是用selenium的名字,但是实现方式和协议基本沿用的是webdriver的。可以看做一样。

    **简单来说,需要通过chromedriver调用chrome,进行模拟浏览器操作。**

3.安装

  • 下载chromedriver。chrome和chromedriver 要版本对应,chromedriver版本下载,放到相应目录。

  • 下载golang代码包。selenium的golang代码包

    go get -t -d github.com/tebeka/selenium
  • 注意点: 本人Win10,调用chrome报错。主要涉及3个点: 1.将chrome添加至环境变量path中,可以通过cmd直接运行chrome.exe确定是否运行成功;2.修改程序的权限,让执行账户获取所有权,避免权限提升 3.如果提示没有找到google-chrome时,拷贝一份chrome.exe重命名为google.chrome.exe。

4.源码分析

4.1 selenium执行流程分析
经过我的理解和思考,我认为selenium 主要运行模式如下(个人理解,仅供参考)。

image.png

程序需要调用selenium库执行相应的函数, 后台调用chrome浏览器,然后将操作元素的请求给下方的浏览器驱动。浏览器驱动再转发这个请求给浏览器。最后将结果返回。
4.2 源码文件分析selenium-golangdoc
因为selenium源码包不是很大,同时因为是chrome进行实战,所以我将源码进行删减然后进行添加注释。godoc包应该没有写完整,比如webdriver,webelement只讲了接口,并没有将实现细节,我们可以根据selenium操作进行脑补。可以参考python中selenium中的实现,比如[自动化测试](http://www.python3.vip/tut/auto/selenium/01/)。

    先继续画个图进行包的讲解的吧。

image.png

源码简略分析:
// 第一类 杂项

//删除会话
func DeleteSession(urlPrefix, id string) error

//开启关闭debug调试
func SetDebug(debug bool)

//设置代理
func (c Capabilities) AddProxy(p Proxy)
//设置日志级别
func (c Capabilities) SetLogLevel(typ log.Type, level log.Level)




// 第二类 seleium后台服务

//服务实例的可选项
type ServiceOption func(*Service) error

//添加chrome路径信息,返回的是serviceOption类型
func ChromeDriver(path string) ServiceOption


//服务的结构体,包含隐藏类型
type Service struct {
    // contains filtered or unexported fields
}

//启动chrome浏览器的服务器,返回service类型指针
func NewChromeDriverService(path string, port int, opts ...ServiceOption) (*Service, error)

//关闭服务,记得defer关闭
func (s *Service) Stop() error




// 第三类 chrome操作相关

//设置浏览器兼容性,map类型,比如chrome浏览器兼容性。
//caps := selenium.Capabilities{"browserName": "chrome"}
type Capabilities map[string]interface{}

//通过调用函数添加chrome兼容性
func (c Capabilities) AddChrome(f chrome.Capabilities)

//启动webdriver实例
func NewRemote(capabilities Capabilities, urlPrefix string) (WebDriver, error)

//通过WebDriver接口可以看出具体页面的实现的方法,是接口,接口里面是实现的方法。
type WebDriver interface {
    //返回服务器环境的版本信息
    // Status returns various pieces of information about the server environment.
    Status() (*Status, error)

    //创建新的session
    // NewSession starts a new session and returns the session ID.
    NewSession() (string, error)

    //创建新的session(已废弃)
    // SessionId returns the current session ID
    // Deprecated: This identifier is not Go-style correct. Use SessionID
    // instead.
    SessionId() string

    //获取新的ssionid
    // SessionID returns the current session ID.
    SessionID() string

    //切换session
    // SwitchSession switches to the given session ID.
    SwitchSession(sessionID string) error

    //返回兼容性
    // Capabilities returns the current session's capabilities.
    Capabilities() (Capabilities, error)

    //设置异步脚本执行时间
    // SetAsyncScriptTimeout sets the amount of time that asynchronous scripts
    // are permitted to run before they are aborted. The timeout will be rounded
    // to nearest millisecond.
    SetAsyncScriptTimeout(timeout time.Duration) error

    //设置等待搜索元素的时间,目的: 如果页面结果返回较慢,就需要等待页面内容完整返回,然后再进行页面元素操作。
    // SetImplicitWaitTimeout sets the amount of time the driver should wait when
    // searching for elements. The timeout will be rounded to nearest millisecond.
    SetImplicitWaitTimeout(timeout time.Duration) error

    //设置等待页面的时间
    // SetPageLoadTimeout sets the amount of time the driver should wait when
    // loading a page. The timeout will be rounded to nearest millisecond.
    SetPageLoadTimeout(timeout time.Duration) error

    //设置会话退出
    // Quit ends the current session. The browser instance will be closed.
    Quit() error

    //获取现在窗口句柄,一串序号,打开一个窗口一个句柄
    // CurrentWindowHandle returns the ID of current window handle.
    CurrentWindowHandle() (string, error)

    //获取现在所有打开窗口句柄,获取所有窗口句柄
    // WindowHandles returns the IDs of current open windows.
    WindowHandles() ([]string, error)

    //返回当前页面连接的URL
    // CurrentURL returns the browser's current URL.
    CurrentURL() (string, error)

    //获取当前页面的标题
    // Title returns the current page's title.
    Title() (string, error)

    //返回当前页面的所有内容
    // PageSource returns the current page's source.
    PageSource() (string, error)

    //关闭现在的窗口
    // Close closes the current window.
    Close() error

    //切换frame,frame里面内嵌一个完整html,如果操作里面的内容需要进入iframe中。switchframe(nil),返回到顶层
    // SwitchFrame switches to the given frame. The frame parameter can be the
    // frame's ID as a string, its WebElement instance as returned by
    // GetElement, or nil to switch to the current top-level browsing context.
    SwitchFrame(frame interface{}) error

    切换windows到指定窗口
    // SwitchWindow switches the context to the specified window.
    SwitchWindow(name string) error

    //关闭窗口
    // CloseWindow closes the specified window.
    CloseWindow(name string) error

    //设置最大化窗口
    // MaximizeWindow maximizes a window. If the name is empty, the current
    // window will be maximized.
    MaximizeWindow(name string) error

    //设置窗口尺寸
    // ResizeWindow changes the dimensions of a window. If the name is empty, the
    // current window will be maximized.
    ResizeWindow(name string, width, height int) error

    //通过url导航至相应界面。主要选项,就是打开url地址。
    // Get navigates the browser to the provided URL.
    Get(url string) error

    //向前翻
    // Forward moves forward in history.
    Forward() error

    //向后翻
    // Back moves backward in history.
    Back() error

    //刷新
    // Refresh refreshes the page.
    Refresh() error

    //查找定位一个html元素。
    // FindElement finds exactly one element in the current page's DOM.
    FindElement(by, value string) (WebElement, error)

    //查找定位多个的html元素
    // FindElement finds potentially many elements in the current page's DOM.
    FindElements(by, value string) ([]WebElement, error)

    //获取当前焦点元素
    // ActiveElement returns the currently active element on the page.
    ActiveElement() (WebElement, error)

    //解码元素响应
    // DecodeElement decodes a single element response.
    DecodeElement([]byte) (WebElement, error)

    //解码多个元素响应
    // DecodeElements decodes a multi-element response.
    DecodeElements([]byte) ([]WebElement, error)

    //获取所有cookie
    // GetCookies returns all of the cookies in the browser's jar.
    GetCookies() ([]Cookie, error)

    //获取指定cookie
    // GetCookie returns the named cookie in the jar, if present. This method is
    // only implemented for Firefox.
    GetCookie(name string) (Cookie, error)

    //添加cookie到jar
    // AddCookie adds a cookie to the browser's jar.
    AddCookie(cookie *Cookie) error

    //删除所有cookie
    // DeleteAllCookies deletes all of the cookies in the browser's jar.    
    DeleteAllCookies() error

    //删除指定cookie
    // DeleteCookie deletes a cookie to the browser's jar.
    DeleteCookie(name string) error

    //敲击鼠标按钮
    // Click clicks a mouse button. The button should be one of RightButton,
    // MiddleButton or LeftButton.
    Click(button int) error

    //双击鼠标按钮
    // DoubleClick clicks the left mouse button twice.
    DoubleClick() error

    //按下鼠标
    // ButtonDown causes the left mouse button to be held down.
    ButtonDown() error

    //抬起鼠标
    // ButtonUp causes the left mouse button to be released.
    ButtonUp() error

    //发送更改到活动元素(已丢弃)
    // SendModifier sends the modifier key to the active element. The modifier
    // can be one of ShiftKey, ControlKey, AltKey, MetaKey.
    //
    // Deprecated: Use KeyDown or KeyUp instead.
    SendModifier(modifier string, isDown bool) error

    //将按键顺序序列发送到活动元素
    // KeyDown sends a sequence of keystrokes to the active element. This method
    // is similar to SendKeys but without the implicit termination. Modifiers are
    // not released at the end of each call.
    KeyDown(keys string) error


    //释放发送的元素
    // KeyUp indicates that a previous keystroke sent by KeyDown should be
    // release
    KeyUp(keys string) error

    //拍摄快照
    // Screenshot takes a screenshot of the browser window.
    Screenshot() ([]byte, error)

    //日志抓取
    // Log fetches the logs. Log types must be previously configured in the
    // capabilities.
    //
    // NOTE: will return an error (not implemented) on IE11 or Edge drivers.
    Log(typ log.Type) ([]log.Message, error)

    //解除警报
    // DismissAlert dismisses current alert.
    DismissAlert() error

    //接受警报
    // AcceptAlert accepts the current alert.
    AcceptAlert() error

    //返回现在警报内容
    // AlertText returns the current alert text.
    AlertText() (string, error)

    //发送警报内容
    // SetAlertText sets the current alert text.
    SetAlertText(text string) error

    //执行脚本
    // ExecuteScript executes a script.
    ExecuteScript(script string, args []interface{}) (interface{}, error)

    //异步执行脚本
    // ExecuteScriptAsync asynchronously executes a script.
    ExecuteScriptAsync(script string, args []interface{}) (interface{}, error)

    //执行源脚本
    // ExecuteScriptRaw executes a script but does not perform JSON decoding.
    ExecuteScriptRaw(script string, args []interface{}) ([]byte, error)

    //异步执行源脚本
    // ExecuteScriptAsyncRaw asynchronously executes a script but does not
    // perform JSON decoding.
    ExecuteScriptAsyncRaw(script string, args []interface{}) ([]byte, error)

    //等待条件为真
    // WaitWithTimeoutAndInterval waits for the condition to evaluate to true.
    WaitWithTimeoutAndInterval(condition Condition, timeout, interval time.Duration) error

    //等待时间
    // WaitWithTimeout works like WaitWithTimeoutAndInterval, but with default polling interval.
    WaitWithTimeout(condition Condition, timeout time.Duration) error

    //等待
    //Wait works like WaitWithTimeoutAndInterval, but using the default timeout and polling interval.
    Wait(condition Condition) error
}

//对相关元素进行后续执行,接口类型,里面是实现方法
type WebElement interface {
    // click选中的元素
    // Click clicks on the element.
    Click() error

    //发送数据到选中元素
    // SendKeys types into the element.
    SendKeys(keys string) error

    //提交按钮
    // Submit submits the button.
    Submit() error

    //清空按钮
    // Clear clears the element.
    Clear() error

    //移动元素到相应的坐标
    // MoveTo moves the mouse to relative coordinates from center of element, If
    // the element is not visible, it will be scrolled into view.
    MoveTo(xOffset, yOffset int) error

    // 查找子元素
    // FindElement finds a child element.
    FindElement(by, value string) (WebElement, error)

    //查找多个子元素
    // FindElement finds multiple children elements.
    FindElements(by, value string) ([]WebElement, error)

    //返回标签名称
    // TagName returns the element's name.
    TagName() (string, error)

    //返回元素内容
    // Text returns the text of the element.
    Text() (string, error)

    //元素被选中返回真
    // IsSelected returns true if element is selected.
    IsSelected() (bool, error)

    //如果元素启用返回真
    // IsEnabled returns true if the element is enabled.
    IsEnabled() (bool, error)

    //如果元素显示返回真
    // IsDisplayed returns true if the element is displayed.
    IsDisplayed() (bool, error)
    //获取元素的名称
    // GetAttribute returns the named attribute of the element.
    GetAttribute(name string) (string, error)

    //范围元素的位置
    // Location returns the element's location.
    Location() (*Point, error)

    //滚动后返回元素的位置
    // LocationInView returns the element's location once it has been scrolled
    // into view.
    LocationInView() (*Point, error)

    //返回元素的大小
    // Size returns the element's size.
    Size() (*Size, error)

    //返回css优先级
    // CSSProperty returns the value of the specified CSS property of the
    // element.
    CSSProperty(name string) (string, error)

    //返回属性滚动的快照
    // Screenshot takes a screenshot of the attribute scroll'ing if necessary.
    Screenshot(scroll bool) ([]byte, error)
}
4.3 说明

5.基础操作

操作之前,先分享一个快速获取CSS Selector和xpath的方法。

    大家用chrome浏览器访问网页,按F12后,点击调试页左上角Elements箭头,然后鼠标移动到目的位置,即可显示页面对应的HTML 元素。

    右键选中的元素,选择copy,此时可以根据直接选择ID,CLASS,CSS Selector或Xpath的地址.

image.png

参考别人的案例,写了几个小案例,仅供参考。亮代码吧。

  • 打开百度,自动搜索。

    package main
    
    import (
        "fmt"
        "github.com/tebeka/selenium"
        "time"
    )
    
    const (
        //设置常量 分别设置chromedriver.exe的地址和本地调用端口
        seleniumPath = `H:\webdriver\chromedriver.exe`
        port         = 9515
    )
    
    func main() {
        //1.开启selenium服务
        //设置selium服务的选项,设置为空。根据需要设置。
        ops := []selenium.ServiceOption{}
        service, err := selenium.NewChromeDriverService(seleniumPath, port, ops...)
        if err != nil {
            fmt.Printf("Error starting the ChromeDriver server: %v", err)
        }
        //延迟关闭服务
        defer service.Stop()
    
        //2.调用浏览器
        //设置浏览器兼容性,我们设置浏览器名称为chrome
        caps := selenium.Capabilities{
            "browserName": "chrome",
        }
        //调用浏览器urlPrefix: 测试参考:DefaultURLPrefix = "http://127.0.0.1:4444/wd/hub"
        wd, err := selenium.NewRemote(caps, "http://127.0.0.1:9515/wd/hub")
        if err != nil {
            panic(err)
        }
        //延迟退出chrome
        defer wd.Quit()
    
        //3.对页面元素进行操作
        //获取百度页面
        if err := wd.Get("https://www.baidu.com/"); err != nil {
            panic(err)
        }
        //找到百度输入框id
        we, err := wd.FindElement(selenium.ByID, "kw")
        if err != nil {
            panic(err)
        }
        //向输入框发送“”
        err = we.SendKeys("天下第一")
        if err != nil {
            panic(err)
        }
    
        //找到百度提交按钮id
        we, err = wd.FindElement(selenium.ByID, "su")
        if err != nil {
            panic(err)
        }
        //点击提交
        err = we.Click()
        if err != nil {
            panic(err)
        }
    
        //睡眠20秒后退出
        time.Sleep(20 * time.Second)
    }
  • 内嵌iframe切换。

    package main
    
    import (
        "fmt"
        "github.com/tebeka/selenium"
        "time"
    )
    
    const (
        //设置常量 分别设置chromedriver.exe的地址和本地调用端口
        seleniumPath = `H:\webdriver\chromedriver.exe`
        port         = 9515
    )
    
    func main() {
        //1.开启selenium服务
        //设置selium服务的选项,设置为空。根据需要设置。
        ops := []selenium.ServiceOption{}
        service, err := selenium.NewChromeDriverService(seleniumPath, port, ops...)
        if err != nil {
            fmt.Printf("Error starting the ChromeDriver server: %v", err)
        }
        //延迟关闭服务
        defer service.Stop()
    
        //2.调用浏览器
        //设置浏览器兼容性,我们设置浏览器名称为chrome
        caps := selenium.Capabilities{
            "browserName": "chrome",
        }
        //调用浏览器urlPrefix: 测试参考:DefaultURLPrefix = "http://127.0.0.1:4444/wd/hub"
        wd, err := selenium.NewRemote(caps, "http://127.0.0.1:9515/wd/hub")
        if err != nil {
            panic(err)
        }
        //延迟退出chrome
        defer wd.Quit()
    
        //3.对页面元素进行操作
        //获取测试网页
        if err := wd.Get("http://cdn1.python3.vip/files/selenium/sample2.html"); err != nil {
            panic(err)
        }
    
        //4.切换到相应的frame上去
        //wd.SwitchFrame(可以id或者frame获取的webelement),我们使用二种方式分别实现。
    
        //4.1 通过frame的id查找 此时id=frame1
        /*
            err = wd.SwitchFrame("frame1")
            if err != nil {
                panic(err)
            }
    
            // 此时定位到iframe的html中,再像使用bycssselector即可
            // 因为animal包含多个对象,我们使用findelements
            wes, err := wd.FindElements(selenium.ByCSSSelector, ".animal")
            if err != nil {
                panic(err)
            }
    
            //循环获取每个元素的信息
            for _,we := range  wes {
                text, err := we.Text()
    
                if err != nil {
                    panic(err)
                }
                fmt.Println(text)
            }
        */
    
        //4.2 frame获取的webelement,通过切换webelement实现。
        // 找到ifname的webelement对象
        element, err := wd.FindElement(selenium.ByCSSSelector, "#frame1")
        // 不同的获取element方式
        //element, err := wd.FindElement(selenium.ByCSSSelector, `iframe[name="innerFrame"]`)
        if err != nil {
            panic(err)
        }
        //切换到iframe中
        err = wd.SwitchFrame(element)
        if err != nil {
            panic(err)
        }
    
        // 此时定位到iframe的html中,再像使用bycssselector即可
        // 因为animal包含多个对象,我们使用findelements
        wes, err := wd.FindElements(selenium.ByCSSSelector, ".animal")
        if err != nil {
            panic(err)
        }
    
        //循环获取每个元素的信息
        for _, we := range wes {
            text, err := we.Text()
            if err != nil {
                panic(err)
            }
            fmt.Println(text)
        }
    
        //5.切换回顶层frame,因为切换中frame中是不能操作外层值元素的,所以我们要切换出来
        //frame=nil是切换回顶层frame
        err = wd.SwitchFrame(nil)
        if err != nil {
            panic(err)
        }
        //根据class name选择元素
        we, err := wd.FindElement(selenium.ByCSSSelector, ".baiyueheiyu")
        if err != nil {
            panic(err)
        }
        //查看顶层元素的内容
        fmt.Println(we.Text())
    
        //睡眠20秒后退出
        time.Sleep(20 * time.Second)
    }
  • 多windows切换。

    package main
    
    import (
        "fmt"
        "github.com/tebeka/selenium"
        "strings"
        "time"
    )
    
    const (
        //设置常量 分别设置chromedriver.exe的地址和本地调用端口
        seleniumPath = `H:\webdriver\chromedriver.exe`
        port         = 9515
    )
    
    func main() {
        //1.开启selenium服务
        //设置selenium服务的选项,设置为空。根据需要设置。
        ops := []selenium.ServiceOption{}
        service, err := selenium.NewChromeDriverService(seleniumPath, port, ops...)
        if err != nil {
            fmt.Printf("Error starting the ChromeDriver server: %v", err)
        }
        //延迟关闭服务
        defer service.Stop()
    
        //2.调用浏览器实例
        //设置浏览器兼容性,我们设置浏览器名称为chrome
        caps := selenium.Capabilities{
            "browserName": "chrome",
        }
        //调用浏览器urlPrefix: 测试参考:DefaultURLPrefix = "http://127.0.0.1:4444/wd/hub"
        wd, err := selenium.NewRemote(caps, "http://127.0.0.1:9515/wd/hub")
        if err != nil {
            panic(err)
        }
        //延迟退出chrome
        defer wd.Quit()
    
        //3.打开多页面chrome实例
        //目前就想到两种方式可以打开,
        //第一种就是页面中有url连接,通过click()方式打开
        //第二种方式就是通过脚本方式打开。wd.ExecuteScript
        if err := wd.Get("http://cdn1.python3.vip/files/selenium/sample3.html"); err != nil {
            panic(err)
        }
    
        //第一种方式,找到页面中的url地址,进行页面跳转
        we, err := wd.FindElement(selenium.ByTagName, "a")
        if err != nil {
            panic(err)
        }
        we.Click()
    
        //第二种方式,通过运行通用的js脚本打开新窗口,因为我们暂时不需要操作获取的结果,所有不获取返回值。
        wd.ExecuteScript(`window.open("https://www.qq.com", "_blank");`, nil)
        wd.ExecuteScript(`window.open("https://www.runoob.com/jsref/obj-window.html", "_blank");`, nil)
    
        //这一行是发送警报信息,写这一行的目的,主要是看当前主窗口是哪一个
        wd.ExecuteScript(`window.alert(location.href);`, nil)
    
        //查看当前窗口的handle值
        handle, err := wd.CurrentWindowHandle()
        if err != nil {
            panic(err)
        }
        fmt.Println(handle)
        fmt.Println("--------------------------")
    
        //查看所有网页的handle值
        handles, err := wd.WindowHandles()
        if err != nil {
            panic(err)
        }
        for _, handle := range handles {
            fmt.Println(handle)
        }
        fmt.Println("--------------------------")
    
        //4.跳转到指定的网页
        //我们虽然打开了多个页面,但是我们当前的handle值,还是第一个页面的,我们要想办法搞定它。
        //记得保存当前主页面的handle值
        //mainhandle := handle
    
        //通过判断条件进行相应的网页
        //获取所有handle值
        handles, err = wd.WindowHandles()
        if err != nil {
            panic(err)
        }
    
        //遍历所有handle值,通过url找到目标页面,判断相等时,break出来,就是停到相应的页面了。
        for _, handle := range handles {
            wd.SwitchWindow(handle)
            url, _ := wd.CurrentURL()
            if strings.Contains(url, "qq.com") {
                break
            }
        }
        //查看此页面的handle
        handle, err = wd.CurrentWindowHandle()
        if err != nil {
            panic(err)
        }
        fmt.Println(handle)
        //这一行是发送警报信息,写这一行的目的,主要是看当前主窗口是哪一个
        wd.ExecuteScript(`window.alert(location.href);`, nil)
    
        //切换回第一个页面
        //wd.SwitchWindow(mainhandle)
    
        //睡眠20秒后退出
        time.Sleep(20 * time.Second)
    }
  • 单选,多选框操作。

    ``` package main

    import (

    "fmt"
    "github.com/tebeka/selenium"
    "time"

    )

    const (

    //设置常量 分别设置chromedriver.exe的地址和本地调用端口
    seleniumPath = `H:\webdriver\chromedriver.exe`
    port         = 9515

    )

    func main() {

    //1.开启selenium服务
    //设置selenium服务的选项,设置为空。根据需要设置。
    ops := []selenium.ServiceOption{}
    service, err := selenium.NewChromeDriverService(seleniumPath, port, ops...)
    if err != nil {
        fmt.Printf("Error starting the ChromeDriver server: %v", err)
    }
    //延迟关闭服务
    defer service.Stop()
    
    //2.调用浏览器实例
    //设置浏览器兼容性,我们设置浏览器名称为chrome
    caps := selenium.Capabilities{
        "browserName": "chrome",
    }
    //调用浏览器urlPrefix: 测试参考:DefaultURLPrefix = "http://127.0.0.1:4444/wd/hub"
    wd, err := selenium.NewRemote(caps, "http://127.0.0.1:9515/wd/hub")
    if err != nil {
        panic(err)
    }
    //延迟退出chrome
    defer wd.Quit()
// 3单选radio,多选checkbox,select框操作(功能待完善,https://github.com/tebeka/selenium/issues/141)
    if err := wd.Get("http://cdn1.python3.vip/files/selenium/test2.html"); err != nil {
        panic(err)
    }
    //3.1操作单选radio
    we, err := wd.FindElement(selenium.ByCSSSelector, `#s_radio > input[type=radio]:nth-child(3)`)
    if err != nil {
        panic(err)
    }
    we.Click()


    //3.2操作多选checkbox
    //删除默认checkbox
    we, err = wd.FindElement(selenium.ByCSSSelector, `#s_checkbox > input[type=checkbox]:nth-child(5)`)
    if err != nil {
        panic(err)
    }
    we.Click()
    //选择选项
    we, err = wd.FindElement(selenium.ByCSSSelector, `#s_checkbox > input[type=checkbox]:nth-child(1)`)
    if err != nil {
        panic(err)
    }
    we.Click()
    we, err = wd.FindElement(selenium.ByCSSSelector, `#s_checkbox > input[type=checkbox]:nth-child(3)`)
    if err != nil {
        panic(err)
    }
    we.Click()

    //3.3 select多选
    //删除默认选项

    //选择默认项
    we, err = wd.FindElement(selenium.ByCSSSelector, `#ss_multi > option:nth-child(3)`)
    if err != nil {
        panic(err)
    }
    we.Click()

    we, err = wd.FindElement(selenium.ByCSSSelector, `#ss_multi > option:nth-child(2)`)
    if err != nil {
        panic(err)
    }
    we.Click()



    //睡眠20秒后退出
    time.Sleep(20 * time.Second)
}

```

结束语

个人理解,仅供参考。如有错误,欢迎指正。

    一直在路上,默默前行。

参考

selenium的golang代码包

selenium-golangdoc

自动化测试

golang driver使用记录

windows对象