Java-Selenium自动化教程(学了不亏)

Posted 胡安民

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-Selenium自动化教程(学了不亏)相关的知识,希望对你有一定的参考价值。

selenium

文章目录

Selenium 是什么?

Selenium 是一组软件工具集,每一个都有不同的方法来支持测试自动化。大多数使用 Selenium 的QA工程师只关注一两个最能满足他们的项目需求的工具上。然而,学习所有的工具你将有更多选择来解决不同类型的测试自动化问题。这一整套工具具备丰富的测试功能,很好的契合了测试各种类型的网站应用的需要。这些操作非常灵活,有多种选择来定位 UI 元素,同时将预期的测试结果和实际的行为进行比较。Selenium 最关键的特性是支持在多浏览器平台上进行测试。Chrome和Firefox相继推出了无头浏览器模式,由于这些大厂的加入phantomjs的用户量渐渐减少,新版的selenium已经不再支持phantomjs

selenium - java这方面几乎很少,大部分都不全而且很乱,导致后来人没法学习, 这就是生态的重要性, 那么我就来刨根问底将Java Selenium 的东西都整理出来, 其实Java爬虫很方便的多看源码其实啥都告你了

小提示: 在java中selenium和python的selenium一样,所以可以参考python的文档,前提你需要能看懂python代码 ^_^

下载驱动

Chromedriver下载地址:
http://chromedriver.storage.googleapis.com/index.html
http://npm.taobao.org/mirrors/chromedriver/
两个地址都可以下载,根据自己的chrome浏览器的版本选择下载即可(一定要和自己游览器版本一致,否则没法使用)
查看浏览器版本 ↓


没找到103.0.5060.114那么下载你当前游览器版本的临近最新版就行


下载解压后把exe文件保存好,用于之后程序中调用

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
<!--            目前来说就3.141.59这个版本好使高版本会有问题-->
            <version>3.141.59</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>22.0</version>
        </dependency>

参数大全

https://peter.sh/experiments/chromium-command-line-switches/

工具类(简化复杂操作)

  • 最优初Selenium 始化
  • 代理
  • 显示等待
  • 强制等待
  • 隐式默认等待
  • 截图
  • 支持多线程并发
package com.reptile;

import com.file.ReadWriteFileUtils;
import lombok.SneakyThrows;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 简要描述
 *
 * @Author: huanmin
 * @Date: 2022/7/18 17:27
 * @Version: 1.0
 * @Description: 文件作用详细描述....
 */
public class ChromeDriverUtil 
    //文件版本,防止多线程缓存文件和用户文件共享,导致创建错误
    private  static  AtomicInteger fileSerial=new AtomicInteger(0);
    private ChromeDriver driver;

    public ChromeDriverUtil(String path, boolean pd, boolean img) 
        init(path, pd, img);
    

    @SneakyThrows
    private void init(String path, boolean pd, boolean img) 
        System.setProperty("webdriver.chrome.driver", path);
        ChromeOptions options = new ChromeOptions();
        if (!pd) 
            options.addArguments("--headless"); //无浏览器模式
        
        options.addArguments("--disable-gpu"); // 谷歌文档提到需要加上这个属性来规避bug
        options.addArguments("--disable-software-rasterizer"); //禁用3D软件光栅化器
        options.addArguments("--no-sandbox");// 为了让linux root用户也能执行
        // 优化参数
        options.addArguments("--disable-dev-shm-usage"); //解决在某些VM环境中,/dev/shm分区太小,导致Chrome失败或崩溃
        if (img) 
            options.addArguments("blink-settings=imagesEnabled=false"); //禁止加图片,如果爬取图片的话,这个不能禁用
            options.addArguments("--disable-images");
        

        String tmpdir = System.getProperty("java.io.tmpdir");
        String dir = tmpdir + File.separator + "chrome_file_data_cache"+File.separator+fileSerial.incrementAndGet();
        File file1 = new File(dir+File.separator + "data");
        if(file1.exists())
            file1.mkdirs();
        
        File file2 = new File(dir+File.separator + "cache");
        if(file2.exists())
            file1.mkdirs();
        

        options.addArguments("--user-data-dir=" + file1.getAbsolutePath()); //解决打开页面出现data;空白页面情况,因为没有缓存目录
        options.addArguments("--disk-cache-dir=" + file2.getAbsolutePath()); //指定Cache路径
        options.addArguments("--incognito") ; //无痕模式
        options.addArguments("--disable-plugins"); //禁用插件,加快速度
        options.addArguments("--disable-extensions"); //禁用扩展
        options.addArguments("--disable-popup-blocking"); //关闭弹窗拦截
        options.addArguments("--ignore-certificate-errors"); //  禁现窗口最大化
        options.addArguments("--allow-running-insecure-content");  //关闭https提示 32位
        options.addArguments("--disable-infobars");  //禁用浏览器正在被自动化程序控制的提示  ,但是高版本不生效

        if (!pd) 
            //无浏览器模式-最大化窗口  ,防止有些元素被隐藏
            int screenWidth = ((int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().width);
            int screenHeight = ((int) java.awt.Toolkit.getDefaultToolkit().getScreenSize().height);
            options.addArguments("window-size=" + screenWidth + "," + screenHeight);
        
        //随机设置请求头
        options.addArguments("--user-agent=" + UserAgent.getUserAgentWindows());
        proxy(options, false); //设置代理 ,true 开启代理
        driver = new ChromeDriver(options);//实例化
        if (pd) 
            driver.manage().window().maximize(); //界面的方式, 最大化窗口, 防止有些元素被隐藏,无界面就不要使用了
        
        //当我们去定位页面元素时,如果元素没有找到,不会立即抛出异常,而是周期性地(通常为 0.5s)去重新寻找,直到该元素找到或者超过最大等待时间才结束 ,超时后就报错NoTouchElementException
        //当我们使用implicitly_wait()时,如果想要定位的元素已经找到,但是它的内容(如文本内容,属性等)没有加载出来,此时隐式等待无效,仍会直接抛出NoSuchElementException异常,这也是为什么我们很多时候仍需要使用time.sleep()的原因。
        driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);

    

    //无头模式,不加载图片
    public static ChromeDriverUtil buildHide(String path) 
        return new ChromeDriverUtil(path, false, true);
    

    //无头模式,加载图片
    public static ChromeDriverUtil buildHideImg(String path) 
        return new ChromeDriverUtil(path, false, false);
    

    //显示游览器 ,全功能
    public static ChromeDriverUtil build(String path) 
        return new ChromeDriverUtil(path, true, false);
    

    public ChromeDriver getDriver() 
        return driver;
    

    //强制等待 代码在执行到某个位置时强制等待一段时间
    @SneakyThrows
    public void sleep(long ms) 
        Thread.sleep(ms);
    

    // 显示等待,是为了解决隐式等待遗留的问题,比如元素显示了,但是内部的文本没有显示出来,可能文本是通过ajax异步的会比较慢
    public WebElement wait(int seconds, ExpectedCondition<WebElement> expectedCondition) 
        WebDriverWait webDriverWait = new WebDriverWait(driver, seconds);
        //返回null或者false,等待500毫秒继续尝试,直到过期
        WebElement until = webDriverWait.until(expectedCondition);

        return until;
    

    //自行扩展, 从接口中读取,或者从文件中读取都行
    private void proxy(ChromeOptions options, boolean pd) 
        if (pd) 
            String prox = "101.200.127.149:" + 3129;
            Proxy p = new Proxy();
            p.setHttpProxy(prox);//http
//        p.setFtpProxy(prox); //ftp
//        p.setSslProxy(prox);//ssl
//        p.setSocksProxy(prox); //SOCKS
//        p.setSocksUsername("");
//        p.setSocksPassword("");

            options.setProxy(p);
        
    

    //截图
    public void screenshotPNG(TakesScreenshot takesScreenshot, File file) 
        byte[] screenshotAs1 = takesScreenshot.getScreenshotAs(OutputType.BYTES);
        ReadWriteFileUtils.writeByte(screenshotAs1, file);
        try (
                FileOutputStream fos1 = new FileOutputStream(file);
                BufferedOutputStream fos = new BufferedOutputStream(fos1);
        ) 
            fos.write(screenshotAs1, 0, screenshotAs1.length); // 写入数据
         catch (Exception e) 
            e.printStackTrace();
        
    





上面代码UserAgent.getUserAgentWindows() 可以参考Java-JSONP(爬虫) 这个里面都有

常用方法

请求

Navigation navigate()

driver.get(url); 请求一个页面,不支持前进和后退切换
driver.navigate().to(url); 和get类似,支持前进和后退切换
driver.navigate().back(); 退到上一个页面 ,前提必须前进了一个页面才能回退
driver.navigate().forward(); 指前进到下一个页面 ,前提是必须后退后才能前进
driver.navigate().refresh(); 刷新当前页面

定位标签

ChromeDriver 全部选择器都有, WebElement只有两个通用选择器

WebElement findElement(By by); 通用搜索,第一个 , By里包含常用的各种搜索
List<WebElement> findElements(By by); 通用搜索,多个,By里包含常用的各种搜索
WebElement findElementById(String using) 查询指定id的标签
WebElement findElementByLinkText(String using) 查询a标签内容是using的 ,第一个
List<WebElement> findElementsByLinkText(String using) 查询a标签内容是using的 ,多个
WebElement findElementByPartialLinkText(String using) 查询a标签内容是using的 , 模糊匹配 ,第一个
List findElementsByPartialLinkText(String using) 查询a标签内容是using的 , 模糊匹配 ,多个
WebElement findElementByTagName(String using) 查询标签名称 ,第一个
List<WebElement> findElementsByTagName(String using) 查询标签名称,多个
WebElement findElementByName(String using) 查询标签属性name,第一个
List<WebElement> findElementsByName(String using) 查询标签属性name,多个
WebElement findElementByClassName(String using) 查询标签数据class, 第一个
List findElementsByClassName(String using) 查询标签数据class, 多个
WebElement findElementByCssSelector(String using) 使用css选择器 , 第一个
List findElementsByCssSelector(String using) 使用css选择器 , 多个
WebElement findElementByXPath(String using) 使用 XPath 选择器 , 第一个
List findElementsByXPath(String using) 使用 XPath 选择器 , 多个

获取内容

String getPageSource() 获取页面html
String getTitle() 获取页面标题
String getText() 获取此元素(包括子元素)的可见(即未被CSS隐藏)文本。
String getTagName(); 获取此元素的标签名
String getAttribute(String name); 获取元素指定属性的值
Point getLocation(); 获取当前元素,基于页面左上角的为准
Dimension getSize(); 渲染元素的宽度和高度是多少?
Rectangle getRect(); 渲染元素的位置和大小
String getCssValue(String propertyName); 获取指定元素的CSS属性的值
String getCurrentUrl(); 获取表示浏览器正在查看的当前URL的字符串。

判断

boolean isEnabled(); 输入元素当前是否已启用?对于输入元素之外的所有元素这通常都将返回true ,如果元素已启用,则为True,否则为false。
boolean isSelected(); 确定是否选择了此元素。此操作仅适用于输入元素,如复选框、选择中的选项和单选按钮 ,如果当前选择或选中了元素,则为True,否则为false。
boolean isDisplayed(); 元素是否显示

行为

void clear(); 如果该元素是文本输入元素,则会清除该值
void submit(); 提交from表单
void click(); 单击此元素 ,单击元素有一些先决条件。元素必须可见,并且其高度和宽度必须大于0。
void sendKeys(CharSequence… keysToSend); 使用此方法模拟在元素中键入,可以设置其值。

注意:

有些网站的页面是使用动态加载js的,这也就会导致html页面出来了,但是js还没执行完毕,相关事件还没绑定到具体的元素上,那么虽然选择器能找到元素,但是进行事件操作就是不好使的情况 ,我们可以这样解决在加载页面时候前进行强制等待几秒等待全部加载完毕后在进行后续操作

有些网站的指定元素的事件不是在页面加载的时候加载,而是当鼠标悬浮上指定的元素后在动态绑定的, 我们可以控制鼠标悬浮上去停一下,之后在操作这个元素

有些网站的部分元素的事件是根据可视化窗口来动态加载js的, 所以我们操作不在可视化窗口内的元素, 就需要滑动滚动条让元素显示在可视化窗口内部才行

窗口

driver.manage().window()

在某些时候,有些网站在执行的时候可能会打开另外一个窗口或者多个窗口,这个时候,如果我们想要回到原先的窗口获取其他指定的窗口,应该怎么办呢?WebDriver.TargetLocator targetLocator = driver.switchTo(); 可以通过TargetLocator来切换窗口

String getWindowHandle(); 返回当前窗口句柄,通过将其传递给switchTo(),进行切换窗口
driver.switchTo().window(windowHandle); 切换到指定句柄的窗口
WebDriver frame(int index); 切换窗口 ,从0开始 ,一旦切换完成,后续调用都会对该窗口进行。
WebDriver frame(String nameOrId); 切换窗口 nameOrId–帧窗口的名称、元素的id或(从零开始的)索引
WebDriver frame(WebElement frameElement); 使用先前定位的WebElement选择框架。
WebDriver parentFrame(); 将焦点更改为父窗口
WebDriver defaultContent(); 选择页面上的第一个框架,或者当页面包含iFrame时选择主文档。
WebElement activeElement(); 换到当前的文档中具有焦点的元素,如果无法检测到,则切换到正文元素则为body元素。
Alert alert(); 切换到此特定驱动程序实例的当前活动模式对话框。

键盘和鼠标

一、键盘事件

ctrl+a : driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “a”);
ctrl+x: driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “x”);
ctrl+c: driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “c”);
ctrl+v: driver.findElement(By.id(“kw”)).sendKeys(Keys.CONTROL, “v”);
F键操作: driver.findElement(By.id(“kw”)).sendKeys(Keys.F5);
TAB键: driver.findElement(By.id(“kw”)).sendKeys(Keys.TAB);
回车键: driver.findElement(By.id(“kw”)).sendKeys(Keys.ENTER);
空格键: driver.findElement(By.id(“kw”)).sendKeys(Keys.SPACE);
还有其他键盘的操作,在这里只列举常用的键位。

二、鼠标事件

  • Actions actions = new Actions(driver);
  • perform() 执行动作

右键点击enement的元素
actions.contextClick(element).perform();

左键单击 enement元素
actions.clickAndHold(element).perform();

鼠标左键双击 enement元素
actions.doubleClick(element).perform();

鼠标悬停enement元素
actions.moveToElement(element).perform(); //中间
actions.moveToElement(element,x,y).perform(); //指定位置

将鼠标从其当前位置(或0,0) 移动鼠标 ,如果提供的坐标在视口之外(鼠标将在浏览器窗口之外结束),则会滚动视口以匹配。
actions.moveToElement(x,y).perform();

拖动元素, 在源元素的位置执行点击并保持,移动到目标元素的位置,然后释放鼠标
Actions dragAndDrop(WebElement source, WebElement target)
Actions dragAndDropBy(WebElement source, int xOffset, int yOffset) 拖动到指定位置

结束

driver.close(); 关闭当前窗口,如果它是当前打开的最后一个窗口,则退出浏览器。
driver.quit(); 退出此驱动程序,关闭每个相关窗口。

注意: 在操作完毕后必须调用quit() 进行释放资源,否则驱动将长存在内存中不会被释放掉,通过任务管理器就能看到一大堆的chromedriver.exe

js执行

某些时候,我们可能通过getText()的方式获取标签的文本值并不会生效 ,但是我们可以通过写js语句来解决大部分问题。 执行js语句Object executeScript(String script, Object... args); 该方法可以供我们执行js语句,script代表我们的js语句,args代表传给script的值,接受参数使用arguments[0]…[1]…[2].依次来接受。示例如下:
假设我们想要获取某个标签的文本值

第一种方式:driver.executeScript("return document.getElementById('blogClick').innerText ;")

第二种方式:

WebElement blogClick = driver.findElementById("blogClick");
driver.executeScript("return arguments[0].innerText;",blogClick);

结果需要返回值的话那么需要指定return

  • 对于HTML元素返回WebElement
  • 对于十进制,返回双精度
  • 对于非十进制数返回Long,
  • 对于布尔值返回布尔值
  • 对于所有其他情况,返回一个字符串
  • 对于数组返回一个列表 ,支持嵌套列表。
  • 对于对象,返回一个map ,
    遵循上述规则。 除非值为null或没有返回值,否则返回null 如果参数不符合这些条件,将引发异常。

表单的常用操作

选择下拉框元素

Select select = new Select(driver.findElementById("select")); 
//通过索引选择 
select.selectByIndex(1);
//通过value值获取 
select.selectByValue("zhangsan")
//通过文本值获取
select.selectByVisibleText("张三");

单选和复选

driver.findElementById("radio").click(); //单选按钮

复选框其实和单选按钮一样,都是定位元素,点击元素,在选择元素之前,我们可以通过isSelected()来判断元素是否被选择,isEnabled()来判断元素是否被禁用。

表单提交

WebElement form = driver.findElementById("form");
//只能用于表单提交
form.submit();

在某些时候,有些网站在执行的时候可能会打开另外一个窗口,这个时候,如果我们想要回到原先的窗口,应该怎么办呢?

//获取窗口的句柄 
String windowHandle = driver.getWindowHandle(); 
//另外一个窗口执行... 
//另外一个窗口执行结束后,我们可以通过switchTo()去返回到原先窗口 
driver.switchTo().window(windowHandle);

其他操作

移动滚动条

左右上下滑动指定元素的滚动条
driver.executeScript("document.getElementById('arguments[0]').scrollTop=arguments[1];","agreementMain",1200);
driver.executeScript("document.getElementById('arguments[0]').scrollLeft=arguments[1];","agreementMain",1200);

左右上下滑动window窗体的滚动条
driver.executeScript("window.scrollTo(arguments[0],arguments[1]);",0,1200);

网页正文全文宽: document.body.scrollWidth
网页正文全文高: document.body.scrollHeight

获取元素距当前可视区域顶部的距离

var box=document.getElementById(‘box’); // 获取元素
alert(box.getBoundingClientRect().top); // 元素上边距离页面上边的距离
alert(box.getBoundingClientRect().right); // 元素右边距离页面左边的距离
alert(box.getBoundingClientRect().bottom); // 元素下边距离页面上边的距离
alert(box.getBoundingClientRect().left); // 元素左边距离页面左边的距离

有些网站的元素他的事件相关js,是根据可视化窗口来,当元素在可视化窗口内,就会调用对应的js绑定事件,那么我们可以这样

		
        //找到元素,以渲染完毕
        WebElement elementa = driver.findElement(By.cssSelector(".timeline-toggle-btn .timeline-icon-toggle-down"));
        // 移动滚动条将,对应的元素显示,在可视化窗口中
        driver.executeScript("window.scrollTo(0,arguments[0].getBoundingClientRect().top-100)",elementa);
        //展开下拉列表
        elementa.click();

有些列表内的元素是懒加载,如果我们直接将滚动条滑动到底部,元素是不会加载出来的,触发懒加载需要时间的,那么我们需要模拟人为滑动滚动条的方式来触发列表中所有元素让他们进行加载

        //获取需要滚动的距离
        Long o =(Long) driver.executeScript("let elementsByClassNameElement = document.querySelector(arguments[0]);\\n" +
                        " return elementsByClassNameElement.offsetHeight+elementsByClassNameElement.offsetTop;",
                ".timeline-box.clearfix");
        //获取当前滚动条位置
        Long o1 =
        
                

       新手初入数字货币交易领域,就像刘姥姥刚入大观园一样,眼花缭乱,对一切未知充满好奇与新鲜,但新手应意识到数字交易领域可不是大观园,玩区块链数字货币,就要做好应对风险的准备。IMtoken钱包和交易所功能一致,是数字货币交易储存工具,相比交易所,IMtoken钱包相对比较安全些,一方面在设计该款钱包时,团队就将安全性放在研发首位,另一方面对于用户而言,有了IMtoken钱包,就相当于把钱攥在自己手里。

       IMtoken钱包是款免费的、密码掌握在自己手中的、被盗风险极低、储存货币多元的数字钱包,想要在区块链领域大有作为的新手们,有必要了解这款钱包的下载(官网www.mytoken.im)、创建、备份、删除、恢复、提现等基础内容。

  1、IMtoken钱包创建

  在下载IMtoken钱包之前,新手有必要做钱包风险测评,除了官网,我们还可以直接从Win7系统之家 http://www.winwin7.com/azapp/27036.html这种正规渠道下载,这些渠道有口皆碑,很多用户对其青睐有加,原因简单粗暴,即安全不附带垃圾软件。如果投资者用的是苹果手机,在下载之前,需要获得一个境外的苹果id。

  IMtoken下载后,我们就可以在软件上创建属于自己的IMtoken钱包。IMtoken官方团队设计了指导性较强的界面,因此用户可以按图索骥,一步步操作,创建成功。在此还要提醒大家,一定要注意密码保存工作,不要抱有侥幸心理,因为钱包没有找回密码和重置功能。

  虽然IMtoken钱包密码不能重新找回,但整个钱包有恢复机制,这意味着当钱包软件出现问题后,或更换手机,后期我们还可以通过重新下载来恢复钱包。该种功能就是备份。

  2、如何备份IMtoken钱包

  备份方式有多种,助记词、私钥、Keysotre。

  这三种备份方式之间的关系是:助记词=私钥=Keysotre+密码。

  所谓私钥,是除了你,别人都没有的钥匙,该钥匙可以打开你的Imtoken钱包。

  我们以备份助记词为例。

      在钱包页面上,备份助记词选项赫然在列,点击进入密码页面,填写确认后,你的钱包助记词会显现出来,助记词看起来很多且是英文,一副让人看不懂的样子,不要紧,按照顺序,将单词逐一记下,在此提醒大家,不要为了图省事,手动截图,否则后期会哭。自信谨慎的人会做好多个备份。在记下后,钱包会再次让用户手动输入助记词,以再次确定备份做到位。

  小tips:若要将助记词记载到电脑上,记得要断网!这一点很关键,直接关系到助记词的安全性,当然还要记得加密,在双重保证下,你的钱包才不会被黑客盯上。

  做好备份,输入助记词后,点击确认。此后你的钱包恢复及密码找回、提现转账等都要靠这个助记词了。

  IMtoken钱包有很多内容都值得新手去了解学习,常学常新,只有对其了如指掌,才能在数字交易领域游刃有余。

以上是关于Java-Selenium自动化教程(学了不亏)的主要内容,如果未能解决你的问题,请参考以下文章

Imtoken钱包使用教程:新手学了不后悔系列!

java-selenium处理弹窗问题

java8停止更新了,还有必要学吗

坑爹的数据结构和算法,怎么才能学了不忘?(万字总结)

VS code搭建 C 和 C++ 环境的完整图文教程!赶紧收藏,这波不亏!

java-selenium 框架例子