您现在的位置是:首页 > 文章详情

Golang使用selenium操作Chrome

日期:2020-05-28点击:620

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中的实现,比如自动化测试

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

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对象

原文链接:https://blog.51cto.com/9406836/2499426
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章