Selenium简介
selenium 是一种用于Web应用程序自动化测试的工具,直接运行在浏览器中,模拟用户操作,它支持多种浏览器(Chrome,Firebox,IE等),还支持多种语言编写测试脚本(Java,Python,Go等)。
因为我要做的最终脚本可以在win和mac上运行,并且不需要安装特定语言的环境,所以这里采用Java进行脚本编写,使用java多个平台代码统一,并且只要把jre打包即可,即可在别的电脑上运行(虽然jre也挺大,但是起码不用像node,python安装环境)。
安装selenium
第一步肯定是安装selenium依赖,可以通过下载jar包,或者通过maven引入。
这里使用 maven 管理项目依赖,所以在pom.xml添加以下依赖:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
获取浏览器驱动
想用selenium驱动不同的浏览器需要不同的浏览器驱动,这里将会使用Chrome浏览器进行自动化测试,所以需要下载 ChromeDriver ,需要下载与目标浏览器相匹配的版本号的驱动器(不过Chrome浏览器一般都会自动更新,所以直接下载最新版的驱动器就是了)。
使用selenium打开浏览器
package org.example;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
WebDriver driver = new ChromeDriver();
try {
driver.get("https://www.baidu.com");
driver.findElement(By.id("kw")).sendKeys("selenium" + Keys.ENTER);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
这里驱动器打开了浏览器后输入网址 https://www.baidu.com 并且找到了 id 为 kw 的节点,输入 selenium 按下回车键,最后休眠两秒钟。
这里就使用到了前面下载的 ChromeDriver,你需要把下载的 ChromeDriver 加载到系统path里,这里的程序才可以正常运行。
使用指定的 ChromeDriver 启动程序
不过前面说了我最终的用户电脑上并不会装有一些特定的环境,所以我打包给他的东西应该包含程序启动必要的所有东西:
- JRE
- ChromeDriver
- 启动脚本
所以这里我把下载到的 chromedriver 文件放到项目的 resources 目录下,并且让应用程序使用指定的 chromedriver :
package org.example;
import org.openqa.selenium.By;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import java.io.File;
import java.net.URL;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
URL chromeDriverUrl = App.class.getClassLoader().getResource("chromedriver");
if (chromeDriverUrl == null) {
throw new RuntimeException("缺少Chrome驱动器");
}
ChromeDriverService service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(chromeDriverUrl.getPath()))
.build();
WebDriver driver = new ChromeDriver(service);
try {
driver.get("https://www.baidu.com");
driver.findElement(By.id("kw")).sendKeys("selenium" + Keys.ENTER);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
driver.quit();
}
}
}
如果正常的话,你现在运行程序应该可以看到程序自动打开百度,然后搜索 selenium 两秒之后就关闭浏览器。
控制浏览器大小,位置
package org.example;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import java.io.File;
import java.net.URL;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
URL chromeDriverUrl = App.class.getClassLoader().getResource("chromedriver");
if (chromeDriverUrl == null) {
throw new RuntimeException("缺少Chrome驱动器");
}
ChromeDriverService service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(chromeDriverUrl.getPath()))
.build();
WebDriver driver = new ChromeDriver(service);
try {
driver.get("https://www.baidu.com");
driver.manage().window().maximize();// 最大化
// driver.manage().window().fullscreen();// 全屏
// driver.manage().window().setSize(new Dimension(800, 600));// 自定义浏览器大小
// driver.manage().window().setPosition(new Point(0, 0));// 自定义浏览器左上角的位置
driver.findElement(By.id("kw")).sendKeys("selenium" + Keys.ENTER);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// driver.close();// 关闭当前窗口,如果当前窗口是最后一个窗口则,关闭浏览器
driver.quit();// 关闭浏览器
}
}
}
控制浏览页跳转
我们浏览网页时可能会进行回退操作,点错了按钮啥情况的,还有刷新页面,往前翻页等操作。selenium也提供了这一系列操作:
driver.navigate().back();// 返回上一个页面
driver.navigate().forward();// 往前一个页面
driver.navigate().refresh();// 刷新当前页面
driver.navigate().to("https://www.csdn.net");// 前往另一个网址
页面元素操作
自动化测试就是在模拟人对浏览器的操作,我们一般都会在页面上输入信息,点击某个按钮。
如果写过网页的就应该知道网页是由 HTML 编写的,里面都是一些 dom 元素,加上 css 样式,变成了我们所看到的样子。
比如我们打开百度,然后在搜索框输入要搜索的信息,最后点击搜索(或者按回车键),最后百度就会把我们输入信息的相关数据都在网页上显示出来。
这里如果用selenium来进行自动化操作,那么需要分成四个步骤:
- 打开
https://www.baidu.com - 找到输入框元素
- 在输入框元素中写入要查询的信息
- 找到百度一下这个按钮
- 点击按钮
打开指定网址
这个在上面的示例应该可以看到,就是通过 driver.get("https://www.baidu.com") 打开百度网址。
查找指定元素
这里我们可以先打开百度然后打开开发者工具(windows按 F12 ,mac 按 fn + F12)

先观察页面的元素,可以看到这个输入框元素 <input> 具有:
- class: s_ipt
- name: wd
- id: kw
然后我们还需要验证 class="s_ipt" 的元素是否只存在一个,毕竟一个 class 可以给很多元素用。

我们需要打开网页框架源代码,然后搜索 class="s_ipt

可以看到 class="s_ipt" 的元素只有一个,说明我们可以通过 class 获取到输入框这个元素。
你也可以观察另外的 name="wd" 和 id="kw" 是否也只有一个。
一般一个页面中 id 都是唯一的,正常开发者都是这么做的,不过以前写html的时候,好像同一个id用在两个元素上好像也是可以的,虽然报错了,但是页面还是能正常显示…所以就算是id也检查一下是否在这个页面上是唯一的。
代码示例
package org.example;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import java.io.File;
import java.net.URL;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
URL chromeDriverUrl = App.class.getClassLoader().getResource("chromedriver");
if (chromeDriverUrl == null) {
throw new RuntimeException("缺少Chrome驱动器");
}
ChromeDriverService service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(chromeDriverUrl.getPath()))
.build();
WebDriver driver = new ChromeDriver(service);
try {
// 1. 打开百度
driver.get("https://www.baidu.com");
// 2. 通过id找到输入框
WebElement inputElement = driver.findElement(By.id("kw"));
// 3. 在输入框中写入文字
inputElement.sendKeys("selenium");
// 4. 通过id找到'百度一下'按钮
WebElement searchButton = driver.findElement(By.id("su"));
// 5. 点击'百度一下'按钮
searchButton.click();
// 休眠两秒钟
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
driver.close();
}
}
}
如果你这里把 kw 写成了 km ,那么页面上不存在这个元素,此时程序就会报错:
Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"#km"}
查找元素的方式
通过id查找元素
上面的示例演示了通过 id 查找一个元素:By.id("kw")
通过class来查找元素
我们也可以通过 class 来查找输入框元素:By.className("s_ipt")
通过name来查找元素
我们也可以通过 name 来查找输入框元素:By.name("wd")
通过css选择器来查找元素
有时候我们会遇到比较复杂的页面,然后要查找的元素又没有唯一标识(可能有class但是有很多地方使用到这个class,没有id等情况)

例如我们要查找 https://mp.weixin.qq.com/ 的使用账号登录这个元素,我们可以看到这里只有 class="login__type__container__select-type" 但是通过网页源码查看

我们却看到了有三处类似,两个元素使用了这个class,说明我们无法使用这个 class 直接定位一个元素。
但是经过我们发现:这个元素的父级的class是唯一的 class="login__type__container login__type__container__scan" 所以我们可以通过css选择器来查找元素:By.cssSelector(".login__type__container.login__type__container__scan>.login__type__container__select-type")
抓取百度热榜简单示例

我们每次百度搜索时,都可以在右边看到一个百度热榜,我们就可以通过selenium来抓取百度最新的热门新闻了(虽然好像抓取这个东西用selenium有点大材小用了,不过入门嘛,意思一下)
一般用这个自动化可以抓取一些比较复杂的。比如可能还需要账号登录,然后去不同的页面抓取不同的数据,每个页面可能需要通过点击某个按钮才会显示出的一些数据,就是这种比较复杂的,用selenium会比较方便些。
package org.example;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.File;
import java.net.URL;
import java.util.List;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
URL chromeDriverUrl = App.class.getClassLoader().getResource("chromedriver");
if (chromeDriverUrl == null) {
throw new RuntimeException("缺少Chrome驱动器");
}
ChromeDriverService service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(chromeDriverUrl.getPath()))
.build();
WebDriver driver = new ChromeDriver(service);
// 等待操作最多等待10秒
WebDriverWait webDriverWait = new WebDriverWait(driver, 10);
try {
driver.get("https://www.baidu.com");
driver.findElement(By.name("wd")).sendKeys("selenium" + Keys.ENTER);
// 等待百度热榜数据出现(最多等待10秒)
WebElement hotDataParentElement = webDriverWait
.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".c-table.opr-toplist1-table>tbody")));
List<WebElement> hotDataElement = hotDataParentElement.findElements(By.tagName("tr"));
for (WebElement element : hotDataElement) {
WebElement rankElement = element.findElement(By.cssSelector("td>span>span"));
WebElement titleElement = element.findElement(By.cssSelector("td>span>a"));
System.out.println(rankElement.getText() + "、" + titleElement.getText());
}
driver.navigate().back();// 返回上一个搜索页面
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.close();
}
}
}
因为这里搜索后会跳转页面,页面在重新渲染,所以需要使用 WebDriverWait (第二个参数就是最长等待时间秒数)来查找页面元素,如果直接用 driver.findElement() 那么程序会直接报错找不到元素。
打包可执行jar包(包含依赖)
参考教程:https://www.cnblogs.com/huahua035/p/11988631.html
最后打包出来的东西应该类似这样子:

我们只要执行那个 fl-auto.jar 即可:java -jar fl-auto.jar
运行可能会出现错误:
Exception in thread "main" java.lang.IllegalStateException: The driver executable does not exist: /Users/mac/workspace/code/idea/fl-auto/out/artifacts/fl_auto_jar/file:xxxxxxxx/fl-auto/out/artifacts/fl_auto_jar/fl-auto.jar!/chromedriver
这是找不到 chromedriver 的原因,虽然我们打出来的 jar 中是包含 chromedriver 文件的。
usingDriverExecutable(File file) 参数只接收 File 参数,打包出来的 chromedriver 存在 jar 包之中,所以得到的路径就很奇怪,这里可以改进一下获取 chromedriver 的方法:
package org.example;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.io.File;
import java.net.URL;
import java.util.List;
/**
* Hello selenium!
*/
public class App {
public static void main(String[] args) {
ChromeDriverService service = new ChromeDriverService.Builder()
.usingDriverExecutable(new File(getChromeDriverPath(args)))
.build();
WebDriver driver = new ChromeDriver(service);
// 最多等待10秒
WebDriverWait webDriverWait = new WebDriverWait(driver, 10);
try {
driver.get("https://www.baidu.com");
driver.findElement(By.name("wd")).sendKeys("selenium" + Keys.ENTER);
// 等待百度热榜数据出现(最多等待10秒)
WebElement hotDataParentElement = webDriverWait
.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".c-table.opr-toplist1-table>tbody")));
List<WebElement> hotDataElement = hotDataParentElement.findElements(By.tagName("tr"));
for (WebElement element : hotDataElement) {
WebElement rankElement = element.findElement(By.cssSelector("td>span>span"));
WebElement titleElement = element.findElement(By.cssSelector("td>span>a"));
System.out.println(rankElement.getText() + "、" + titleElement.getText());
}
} catch (Exception e) {
e.printStackTrace();
} finally {
driver.close();
}
}
private static String getChromeDriverPath(String[] args) {
String path = null;
// 传入参数指定chromedriver路径
if (args != null && args.length > 0) {
path = args[0];
}
// 根目录下是否存在chromedriver
if (path == null || path.isEmpty()) {
File file = new File("chromedriver");
if (file.exists()) {
path = "chromedriver";
}
}
// resources 下的chromedriver(开发时用到)
if (path == null || path.isEmpty()) {
URL chromeDriverUrl = App.class.getClassLoader().getResource("chromedriver");
if (chromeDriverUrl == null) {
throw new RuntimeException("Can't find chromedriver.");
}
path = chromeDriverUrl.getPath();
}
return path;
}
}
当我们开发时就是直接获取 resources 目录下的 chromedriver ,而打包出来的jar可以通过给定指定路径,例如 java -jar fl-auto.jar /Users/mac/fl-auto/src/main/resources/chromedriver。
或者把 chromedriver 放到打包后的根目录下:

然后在运行:java -jar fl-auto.jar 即可。

2104

被折叠的 条评论
为什么被折叠?



