基于Selenium的爬虫实现

原创文章,转载请注明原文章地址,谢谢!

Selenium

selenium 是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11),Mozilla Firefox,Safari,Google Chrome,Opera等。selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Grid)。Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此可以用于任何支持JavaScript的浏览器上。selenium可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题。

小试牛刀(windows chrome浏览器)

chrome驱动镜像地址:https://npm.taobao.org/mirrors/chromedriver/
下载对应版本的chromedriver驱动,注意版本对应,不然会报错。将下载好的chromedriver.exe放在你本地和chrome.exe同一个目录下。
创建一个maven项目,添加maven依赖。


   org.seleniumhq.selenium
   selenium-java
   3.4.0

public static void main(String[] args) {
    System.setProperty("webdriver.chrome.driver", "D:\\chromedriver.exe");
    WebDriver webDriver = new ChromeDriver();
    webDriver.get("http://www.baidu.com");
    String title = webDriver.getTitle();
    System.out.println(title);
    webDriver.close();
    webDriver.quit();
}
Selenium元素定位
元素定位 对应方法
id findElement(By.id())
name findElement(By.name())
class name findElement(By.className())
tag name findElement(By.tagName())
link text findElement(By.linkText())
partial link text findElement(By.partialLinkText())
xpath findElement(By.xpath())
css selector findElement(By.cssSelector())

上述定位方法的用法
假如我们有一个Web页面,通过前端工具(如,Firebug)查看到一个元素的属性是这样的


  
  
    
新闻
hao123
//通过link text定位:
driver.findElement(By.linkText("新闻")
driver.findElement(By.linkText("hao123")
//通过partialLink text定位:
driver.findElement(By.partialLinkText("新")
driver.findElement(By.partialLinkText("hao")
driver.findElement(By.partialLinkText("123")

关于xpaht和css的定位比较复杂,请参考: xpath语法、css选择器

控制浏览器操作
  • 控制浏览器窗口大小

有时候我们希望能以某种浏览器尺寸找开,访问的页面在这种尺寸下运行。例如可以将浏览器设置成移动端大小(480* 800),然后访问移动站点,对其样式进行评估;WebDriver 提供了 manage().window().setSize()方法来设置浏览器的大小。

maximize() 设置浏览器最大化
setSize() 设置浏览器宽高
public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.cn");
    driver.manage().window().maximize();
    Thread.sleep(2000);
    driver.get("https://m.baidu.cn");
    driver.manage().window().setSize(new Dimension(480, 800));
    Thread.sleep(2000);
    driver.quit();
}

在PC端执行自动化测试脚本大多的情况下是希望浏览器在全屏幕模式下执行,那么可以使用 maximize()方法使打开的浏览器全屏显示,其用法与 setSize()相同,但它不需要任何参数。

  • 控制浏览器后退、前进

在使用浏览器浏览网页时,浏览器提供了后退和前进按钮,可以方便地在浏览过的网页之间切换,WebDriver也提供了对应的back()和forward()方法来模拟后退和前进按钮。下面通过例子来演示这两个方法的使用。

back() 模拟浏览器后退按钮
forward() 模拟浏览器前进按钮
public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    //get 到百度首页
    driver.get("https://www.baidu.com/");
    System.out.printf("now accesss %s \n", driver.getCurrentUrl());
    Thread.sleep(2000);
    //点击“新闻” 链接
    driver.findElement(By.linkText("新闻")).click();
    System.out.printf("now accesss %s \n", driver.getCurrentUrl());
    Thread.sleep(2000);
    //执行浏览器后退
    driver.navigate().back();
    System.out.printf("back to %s \n", driver.getCurrentUrl());
    Thread.sleep(2000);
    //执行浏览器前面
    driver.navigate().forward();
    System.out.printf("forward to %s \n", driver.getCurrentUrl());
    Thread.sleep(2000);
    driver.quit();
}

为了看清脚本的执行过程,下面每操作一步都通过printf()方法来打印当前的URL地址。

  • 刷新页面

有时候需要手动刷新(F5)页面。

refresh() 刷新页面(F5)
driver.navigate().refresh();
WebDriver常用方法

clear()方法用于清除文本输入框中的内容。
sendKeys()方法模拟键盘向输入框里输入内容。 但是它的作用不仅于此, 我们还可以用它发送键盘按键, 甚至用它来指定上传的文件。
click()方法可以用来单击一个元素,前提是它是可以被单击的对象,它与 sendKeys()方法是Web页面操作中最常用到的两个方法。 其实click()方法不仅仅用于单击一个按钮,它还可以单击任何可以单击的文字/图片链接、复选框、单选框、下拉框等。

public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com/");
    WebElement searchText = driver.findElement(By.id("kw"));
    WebElement searchButton = driver.findElement(By.id("su"));
    searchText.sendKeys("Java");
    searchText.clear();
    searchButton.sendKeys("Selenium");
    searchButton.click();
    driver.quit();
}

submit()方法用于提交表单。 例如,在搜索框输入关键字之后的“回车” 操作, 就可以通过 submit()方法模拟

WebDriver driver = new ChromeDriver();
WebElement searchText = driver.findElement(By.id("kw"));
searchText.sendKeys("Selenium");
searchText.submit();

getSize() 返回元素的尺寸。
getText() 获取元素的文本。
getAttribute(name) 获得属性值。
isDisplayed() 设置该元素是否用户可见。

    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com/");
    //获得百度输入框的尺寸
    WebElement size = driver.findElement(By.id("kw"));
    System.out.println(size.getSize());
    //返回百度页面底部备案信息
    WebElement text = driver.findElement(By.id("cp"));
    System.out.println(text.getText());
    //返回元素的属性值, 可以是 id、 name、 type 或元素拥有的其它任意属性
    WebElement ty = driver.findElement(By.id("kw"));
    System.out.println(ty.getAttribute("type"));
    //返回元素的结果是否可见, 返回结果为 True 或 False
    WebElement display = driver.findElement(By.id("kw"));
    System.out.println(display.isDisplayed());
    driver.quit();
}
模拟鼠标操作

通过前面例子了解到,可以使用click()来模拟鼠标的单击操作,现在的Web产品中提供了更丰富的鼠标交互方式, 例如鼠标右击、双击、悬停、甚至是鼠标拖动等功能。在WebDriver中,将这些关于鼠标操作的方法封装在ActionChains类提供。
Actions 类提供了鼠标操作的常用方法:
contextClick() 右击
clickAndHold() 鼠标点击并控制
doubleClick() 双击
dragAndDrop() 拖动
release() 释放鼠标
perform() 执行所有Actions中存储的行为

//百度首页设置悬停下拉菜单。
public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com/");
    WebElement searchSetting = driver.findElement(By.linkText("设置"));
    Actions action = new Actions(driver);
    action.clickAndHold(searchSetting).perform();
    driver.quit();
}

import org.openqa.selenium.interactions.Actions;
导入提供鼠标操作的 ActionChains 类
Actions(driver) 调用Actions()类,将浏览器驱动driver作为参数传入。
clickAndHold() 方法用于模拟鼠标悬停操作, 在调用时需要指定元素定位。
perform() 执行所有ActionChains中存储的行为, 可以理解成是对整个操作的提交动作。

//鼠标操作
public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com/");
    WebElement searchSetting = driver.findElement(By.linkText("设置"));
    Actions action = new Actions(driver);
    //鼠标右键点击指定的元素
    action.contextClick(driver.findElement(By.id("element"))).perform();
    //鼠标右键点击指定的元素
    action.doubleClick(driver.findElement(By.id("element"))).perform();
    //鼠标拖拽动作, 将 source 元素拖放到 target 元素的位置。
    WebElement source = driver.findElement(By.name("element"));
    WebElement target = driver.findElement(By.name("element"));
    action.dragAndDrop(source, target).perform();
    //释放鼠标
    action.release().perform();
}
模拟键盘操作

Keys()类提供了键盘上几乎所有按键的方法。 前面了解到, sendKeys()方法可以用来模拟键盘输入, 除此之 外, 我们还可以用它来输入键盘上的按键, 甚至是组合键, 如 Ctrl+A、 Ctrl+C 等。

public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com");
    WebElement input = driver.findElement(By.id("kw"));
    //输入框输入内容
    input.sendKeys("seleniumm");
    Thread.sleep(2000);
    //删除多输入的一个 m
    input.sendKeys(Keys.BACK_SPACE);
    Thread.sleep(2000);
    //输入空格键+“教程”
    input.sendKeys(Keys.SPACE);
    input.sendKeys("教程");
    Thread.sleep(2000);
    //ctrl+a 全选输入框内容
    input.sendKeys(Keys.CONTROL, "a");
    Thread.sleep(2000);
    //ctrl+x 剪切输入框内容
    input.sendKeys(Keys.CONTROL, "x");
    Thread.sleep(2000);
    //ctrl+v 粘贴内容到输入框
    input.sendKeys(Keys.CONTROL, "v");
    Thread.sleep(2000);
    //通过回车键盘来代替点击操作
    input.sendKeys(Keys.ENTER);
    Thread.sleep(2000);
    driver.quit();
}

需要说明的是,上面的脚本没有什么实际意义,但向我们展示了模拟键盘各种按键与组合键的用法。
import org.openqa.selenium.Keys;
在使用键盘按键方法前需要先导入 keys 类。
以下为常用的键盘操作:
sendKeys(Keys.BACK_SPACE) 回格键(BackSpace)
sendKeys(Keys.SPACE) 空格键(Space)
sendKeys(Keys.TAB) 制表键(Tab)
sendKeys(Keys.ESCAPE) 回退键(Esc)
sendKeys(Keys.ENTER) 回车键(Enter)
sendKeys(Keys.CONTROL,‘a’) 全选(Ctrl+A)
sendKeys(Keys.CONTROL,‘c’) 复制(Ctrl+C)
sendKeys(Keys.CONTROL,‘x’) 剪切(Ctrl+X)
sendKeys(Keys.CONTROL,‘v’) 粘贴(Ctrl+V)
sendKeys(Keys.F1) 键盘 F1
……
sendKeys(Keys.F12) 键盘 F12

获取断言信息

不管是在做功能测试还是自动化测试,最后一步需要拿实际结果与预期进行比较。这个比较的称之为断言。
我们通常可以通过获取title 、URL和text等信息进行断言。text方法在前面已经讲过,它用于获取标签对之间的文本信息。
getTitle(): 用于获得当前页面的title。
getCurrentUrl() : 用户获得当前页面的URL。
getText() 获取页面文本信息。

//下面同样以百度为例,介绍如何获取这些信息。
public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com");
    System.out.println("Search before================");
    //获取当前的 title 和 url
    System.out.printf("title of current page is %s\n", driver.getTitle());
    System.out.printf("url of current page is %s\n", driver.getCurrentUrl());
    //百度搜索
    WebElement search = driver.findElement(By.id("kw"));
    search.sendKeys("Selenium");
    search.sendKeys(Keys.ENTER);
    Thread.sleep(2000);
    System.out.println("Search after================");
    //获取当前的 title 和 url
    System.out.printf("title of current page is %s\n", driver.getTitle());
    System.out.printf("url of current page is %s\n", driver.getCurrentUrl());
    //获取第一条搜索结果的标题
    WebElement result = driver.findElement(By.xpath("//div[@id='content_left']/div/h3/a"));
    System.out.println(result.getText());
    driver.quit();
}

打印结果

Search before================
title of current page is 百度一下, 你就知道
url of current page is https://www.baidu.com/
 
Search after================
title of current page is Selenium_百度搜索
url of current page is
https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=0&rsv_idx=1&tn=baidu&wd=Selenium&rsv_pq=9be
4680700a485c1&rsv_t=e925U%2F%2B9SBTqmRI%2BuARg0%2BTCzrrZWn4jOBJkb1OS2vUjMrZsq5VblQ7toD8
&rqlang=cn&rsv_enter=1&rsv_sug3=8&rsv_sug2=0&inputT=155&rsv_sug4=155
Selenium - Web Browser Automation
设置元素等待

WebDriver提供了两种类型的等待:显式等待和隐式等待。

  • 显示等待

WebDriver提供了显式等待方法,专门针对某个元素进行等待判断。

public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com");
    //显式等待, 针对某个元素等待
    WebDriverWait wait = new WebDriverWait(driver, 10, 1);
    wait.until(new ExpectedCondition() {
        @Override
        public WebElement apply(WebDriver text) {
            return text.findElement(By.id("kw"));
        }
    }).sendKeys("selenium");
    driver.findElement(By.id("su")).click();
    Thread.sleep(2000);
    driver.quit();
}

WebDriverWait类是由WebDirver提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常。具体格式如下:
WebDriverWait(driver, 10, 1)
driver: 浏览器驱动。 10: 最长超时时间, 默认以秒为单位。 1: 检测的的间隔(步长) 时间, 默认为 0.5s。

  • 隐式等待

WebDriver 提供了几种方法来等待元素。
implicitlyWait。识别对象时的超时时间。过了这个时间如果对象还没找到的话就会抛出NoSuchElement异常。
setScriptTimeout。异步脚本的超时时间。WebDriver可以异步执行脚本,这个是设置异步执行脚本脚本返回结果的超时时间。
pageLoadTimeout。页面加载时的超时时间。因为WebDriver会等页面加载完毕再进行后面的操作,所以如果页面超过设置时间依然没有加载完成,那么WebDriver就会抛出异常。

public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    //页面加载超时时间设置为 5s
    driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
    driver.get("https://www.baidu.com/");
    //定位对象时给 10s 的时间, 如果 10s 内还定位不到则抛出异常
    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
    driver.findElement(By.id("kw")).sendKeys("selenium");
    //异步脚本的超时时间设置成 3s
    driver.manage().timeouts().setScriptTimeout(3, TimeUnit.SECONDS);
    driver.quit();
}
定位一组元素

我们已经学习了8种定位方法, 那8种定位方法是针对单个元素定位的, WebDriver还提供了另外8种用于定位一组元素的方法。
import org.openqa.selenium.By;
......
findElements(By.id())
findElements(By.name())
findElements(By.className())
findElements(By.tagName())
findElements(By.linkText())
findElements(By.partialLinkText())
findElements(By.xpath())
findElements(By.cssSelector())

定位一组元素的方法与定位单个元素的方法类似,唯一的区别是在单词 findElement 后面多了一个 s 表示复数。

public static void main(String[] args) throws Exception {
    WebDriver driver = new ChromeDriver();
    driver.get("https://www.baidu.com/");
    WebElement searchText = driver.findElement(By.id("kw"));
    searchText.sendKeys("selenium");
    searchText.submit();
    Thread.sleep(2000);
    //匹配第一页搜索结果的标题, 循环打印
    List searchResult = driver.findElements(By.xpath("//div/div/h3"));
    //打印元素的个数
    System.out.println(searchResult.size());
    // 循环打印搜索结果的标题
    for (WebElement result : searchResult) {
        System.out.println(result.getText());
    }
    System.out.println("-------我是分割线---------");
    //打印第n结果的标题
    WebElement text = searchResult.get(searchResult.size() - 10);
    System.out.println(text.getText());
    driver.quit();
}

打印结果

15
selenium java 教程-90 天从入门到高薪「学习必看」
python selenium 视频-90 天从入门到高薪「学习必看」
Selenium - Web Browser Automation
功能自动化测试工具——Selenium 篇
Selenium Documentation — Selenium Documentation
selenium + python 自动化测试环境搭建 - 虫师 - 博客园
selenium_百度翻译
Selenium_百度百科
怎样开始用 selenium 进行自动化测试(个人总结)_百度经验
Selenium 官网教程_selenium 自动化测试实践_Selenium_领测软件测试网
Selenium - 开源中国社区
selenium 是什么?_百度知道
selenium-0 基础入学, 先就业后付款!
selenium, 亚马逊官网, 正品低价, 货到付款!
selenium java 教程-90 天从入门到高薪「学习必看」
-------我是分割线---------
selenium + python 自动化测试环境搭建 - 虫师 - 博客园
多表单切换

在 Web 应用中经常会遇到 frame/iframe 表单嵌套页面的应用, WebDriver 只能在一个页面上对元素识别与 定位, 对于 frame/iframe 表单内嵌页面上的元素无法直接定位。 这时就需要通过 switchTo().frame()方法将当前定 位的主体切换为 frame/iframe 表单的内嵌页面中。


  
    ...