目录
1、Selenium简介
Selenium 最初是由 Shinya Kasatani 基于火狐(Firefox)浏览器开发的工具,其主 要用于网站的自动化测试。读者可以在火狐浏览器中安装 Selenium IDE 插件,并使用 该插件录制在浏览器中的执行动作(如表单提交、单击和鼠标的移动等)。在本章中, 我们将重点介绍 Selenium WebDriver 的使用。Selenium WebDriver 主要应用于程序 (Java、Python 和 C#等)与浏览器的交互,其可以用来实现数据的采集。
Selenium 不自带浏览器,需要与第三方浏览器结合使用,如本章与 Selenium 结合 使用的浏览器为谷歌浏览器相比 Jsoup 和 HttpClient 等工具, Selenium 有其特有优势,如自动加载页面(执行 JavaScript 脚本)、模拟真实的浏览器 操作(可用于模拟登录)等。
2、Java Selenium环境搭建
2.1、依赖
首先,创建一个 Java Maven 工程,并在该工程下添加两个文件夹 drivers 和 libs。
其次,在 Java 中使用 Selenium,需要下载相关 jar 包,下载 selenium-server-standalone-3.141.59.jar。下载完成后,将该 jar 包放到工程的 libs 目 录下,并将该 jar 包引入项目。
另外一种配置相关 jar 包的方式是下载 selenium-java-3.141.59 压缩包,并进行解压,之后将解压后 文件夹中的所有 jar 包放到 libs 目录下,并将这些 jar 包引入项目。
再者,也可以直接 使用 Maven 工程中的 pom.xml 文件配置所需的 jar 包。
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>3.141.59</version>
</dependency>
2.2、安装浏览器
没有安装Chrome浏览器,则需先安装(建议官方地址下载)
官方下载地址为:https://www.google.cn/chrome/
2.3、下载驱动 chromedriver
在相关 jar 包配置完成之后,需要下载 Selenium WebDriver 连接谷歌浏览器的工具chromedriver,下载之后放到drivers目录下。
下载地址:http://chromedriver.storage.googleapis.com/index.html
3、浏览器的操控
搭建好 Java Selenium 环境后,便可以利用 Java 程序操控浏览器,获取网页 的响应内容。下面以某搜索网站为例,主要任务是使用 Selenium WebDriver 和 googledriver 启动浏览器,打开某搜索网站首页,并在搜索框中自动输入“Java 网 络爬虫”,执行搜索,最后针对响应页面使用 Xpath 语法解析得到搜索结果的标题 和 URL。
这段代码中使用 setProperty(String key, String value) 配置 googledriver,然后实例化 ChromeDriver(声明使用的是谷歌浏览器),接着使用谷歌浏览器打开页面执行一系列操作。值得注意的是在执行搜索的过程中,需要利用 implicitlyWait()方法休息一定的时间,以供网页完全加载数据。最后,使用 Xpath 语法 进行元素定位,解析相应的字段。
示例
public class Test {
public static void main(String[] args) {
//chromedriver配置
System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
//声明使用的是谷歌浏览器
ChromeDriver driver = new ChromeDriver();
//打开baidu
driver.get("http://www.baidu.com");
//元素定位,输入内容
driver.findElementById("kw").sendKeys("Java网络爬虫");
//元素定位,执行单机命令
driver.findElementById("su").click();
//给出一定的响应时间
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.getPageSource();
//使用Xpath解析数据 //*[@id="1"]/div/div[1]/h3/a
List<WebElement> titleList = driver.findElements(By.xpath("//*[@class='result c-container xpath-log new-pmd']/div/div/h3/a"));
//输出标题
for (WebElement e : titleList) {
System.out.println("标题为:" + e.getText() + "\t" + "url为:" + e.getAttribute("href"));
}
driver.quit();
}
}
执行程序,会发现相关操作都会在浏览器中自动执行。另外,在控制台也会 输出解析的标题和 URL,输出如下:
4、元素定位
在使用 Selenium 时,往往需要先通过定位器找到相应的元素,然后再进行其他操 作。例如,在上面程序中,先使用 findElementById(“kw”)定位到搜索框,随后再利用 sendKeys(CharSequence… keysToSend)方法输入搜索内容。
定位器是一种抽象查询语言, 功能是定位元素。Selenium WebDriver 提供了多种定位策略,如 id 定位、name 定位、 class 定位、tag name 定位、link text 定位、Xpath 定位和 CSS 定位等。下面将分别介 绍这些定位策略。
4.1、id定位
在 Java 中,有以下四种方式实现 id 定位策略
//第一种方式返回WebElement
driver.findElementById(<element id>)
//第二种方式返回List<WebElement>
driver.findElementsById(<element id>)
//第三种方式返回WebElement
driver.findElement(By.id(<element id>))
//第四种方式返回List<WebElement>
driver.findElements(By.id(<element id>))
4.2、name定位
当元素中包含 name 属性时,可以使用 name 进行定位。在 Java 中,有以下四种方式实现 name 定位策略
//第一种方式返回WebElement
driver.findElementByName(<element name>)
//第二种方式返回List<WebElement>
driver.findElementsByName(<element name>)
//第三种方式返回WebElement
driver.findElement(By.Name(<element name>))
//第四种方式返回List<WebElement>
driver.findElements(By.Name(<element name>))
4.3、class定位
当元素中包含 class 属性时,可以使用 class 进行定位。在 Java 中,有以下四种方式实现 class 定位 策略
//第一种方式返回WebElement
driver.findElementByClassName(<element classname>)
//第二种方式返回List<WebElement>
driver.findElementsByClassName(<element classname>)
//第三种方式返回WebElement
driver.findElement(By.ClassName(<element classname>))
//第四种方式返回List<WebElement>
driver.findElements(By.ClassName(<element classname>))
4.4、tag name定位
Selenium WebDriver 也提供了 tag name(标签名称)定位的方法,使用标签名称 可以很方便地定位一些元素,如定位所有表格中的、定位所有链接等。程序 展示了实现 tag name 定位策略的四种方式。
//第一种方式返回WebElement
driver.findElementByTagName(<tagName>)
//第二种方式返回List<WebElement>
driver.findElementsByTagName(<tagName>)
//第三种方式返回WebElement
driver.findElement(By.TagName(<tagName>))
//第四种方式返回List<WebElement>
driver.findElements(By.TagName(<element tagName>))
4.5、link text定位
link text 定位,是通过链接文本定位链接的。在 Java 中,有以下四种方式实现 link text 定位策略
//第一种方式返回WebElement
driver.findElementByLinkText(<linkText>)
//第二种方式返回List<WebElement>
driver.findElementsByLinkText(<linkText>)
//第三种方式返回WebElement
driver.findElement(By.LinkText(<linkText>))
//第四种方式返回List<WebElement>
driver.findElements(By.LinkText(<linkText>))
4.6、Xpath定位
Selenium WebDriver 支持使用 Xpath 表达式定位元素,Xpath 使用路径表达式来定位 HTML 或 XML 文档中的节点 或节点集合,在 Java 中,同 样可以使用四种方式实现 Xpath 定位策略
//第一种方式返回WebElement
driver.findElementByXPath(<Xpath query expression>)
//第二种方式返回List<WebElement>
driver.findElementsByXPath(<Xpath query expression>)
//第三种方式返回WebElement
driver.findElement(By.xpath(<Xpath query expression>))
//第四种方式返回List<WebElement>
driver.findElements(By.xpath(<Xpath query expression>))
4.7、CSS选择器
Selenium WebDriver 在定位元素时,也可以使用 CSS 选择器,其实现方式有四种
//第一种方式返回WebElement
driver.findElementByCssSelector(<css selector>)
//第二种方式返回List<WebElement>
driver.findElementsByCssSelector(<css selector>)
//第三种方式返回WebElement
driver.findElement(By.cssSelector(<css selector>))
//第四种方式返回List<WebElement>
driver.findElements(By.cssSelector(<css selector>))
5、模拟登录
Selenium 可以精准地定位浏览器中的元素(如输入框)、模拟真实的浏览器操作 (如输入文本、单击等)。针对一些复杂且需要登录才能获取数据的网站,可以利用 Selenium 的特性,模拟登录该网站,获取登录的 Cookie。之后,使用 Jsoup 或者 Httpclient 采集该网站中的数据。以下,将以某网站的模拟登录为例,介绍 Selenium 的使用情况。
首先定位登录按钮:id=“s-top-loginbtn”
然后在弹出的模态框中定位输入框和登录按钮:
利用 Selenium,输入用户名和密码,并单击“登录”按钮执行登录操作。需要注意的是在模拟单击登录按钮后, 需要使用 sleep(long millis)方法休息一段时间,使登录信息加载完整。
public class LoginRenren {
public static void main(String[] args) throws InterruptedException, IOException {
//chromedriver配置
System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
//声明使用的是谷歌浏览器
ChromeDriver driver = new ChromeDriver();
//打开baidu
driver.get("http://www.baidu.com");
//元素定位,单机登录按钮
WebElement topButton = driver.findElementById("s-top-loginbtn");
topButton.click();
//等待模态框弹出
Thread.sleep(2 * 1000);
//在弹出的登录框中定位元素
WebElement nameInput = driver.findElementById("TANGRAM__PSP_11__userName");
nameInput.clear();
nameInput.sendKeys("15043577079");
WebElement passInput = driver.findElementById("TANGRAM__PSP_11__password");
passInput.clear();
passInput.sendKeys("459594186zcl");
driver.findElementById("TANGRAM__PSP_11__submit").click();
//等待页面充分加载
Thread.sleep(5 * 1000);
//注意,如果baidu弹出了验证框,那无法绕过,后续操作失败。
//获取Cookie
Set<Cookie> cookies = driver.manage().getCookies();
String cookieStr = "";
for (Cookie cookie : cookies) {
cookieStr += cookie.getName() + "=" + cookie.getValue() + ";";
}
System.out.println(cookieStr);
//基于Jsoup,使用cookies请求个人信息页面
Connection.Response response = Jsoup.connect("https://www.baidu.com/my/index")
.header("Host", "www.baidu.com")
.header("Connection", "keep-alive")
.header("Accept", "text/html,application/xhtml+xml, application/xml;q=0.9,image/webp,*;q=0.8")
.header("Origin", "http://www.baidu.com")
.header("Referer", "http://www.baidu.com")
.header("Content-Type", "application/x-www-form-urlencoded")
.cookie("Cookie", cookieStr)
.execute();
//解析数据
Document doc = response.parse();
Elements elements = doc.select("#user-center > div.main-wrapper_3KoRh > div.siderbar-wrapper_Nk4dE > div > div > a");
for (Element e : elements) {
System.out.println(e.select("p:nth-child(2)").text());
}
//关闭浏览器
driver.quit();
}
}
6、动态加载JavaScript数据(操作滚动条)
在使用 Jsoup 和 Httpclient 直接请求 URL 时,有时会发现响应得到的 HTML 中包 含的信息不全,未展示出的信息必须通过执行页面中的 JavaScript 代码才能展示。相 比于 Jsoup 和 Httpclient,Selenium 可以利用 JavascriptExecutor 接口执行任意 JavaScript 代码。
下面将以百度图片中的搜索图片数据为例,讲解如何使用 Selenium 动态加载 JavaScript 数据。
public class FinancialRolling {
public static void main(String[] args) throws InterruptedException {
//chromedriver配置
System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
//声明使用的是谷歌浏览器
ChromeDriver driver = new ChromeDriver();
//打开baidu
driver.get("http://image.baidu.com");
WebElement kw = driver.findElementById("kw");
kw.clear();
kw.sendKeys("美女");
WebElement button = driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));
button.click();
//等待页面加载
Thread.sleep(5 * 1000);
//执行JS操作
JavascriptExecutor js = (JavascriptExecutor)driver;
try {
js.executeScript("scrollTo(0, 5000)");
System.out.println("1");
Thread.sleep(5000); //调整休眠时间可以获取更多的内容
js.executeScript("scrollTo(5000, 10000)");
System.out.println("2");
Thread.sleep(5000);
js.executeScript("scrollTo(10000, 30000)"); // 继续下拉
System.out.println("3");
Thread.sleep(5000);
js.executeScript("scrollTo(10000, 50000)"); //继续下拉
System.out.println("4");
} catch (Exception e) {
System.out.println("Error at loading the page...");
driver.quit();
}
//解析数据
String html = driver.getPageSource();
Document doc = Jsoup.parse(html);
Elements elements = doc.select("#imgid > div:nth-child(1) > ul > li");
for (Element e : elements) {
System.out.println(e.attr("data-thumburl"));
}
//关闭浏览器
driver.quit();
}
}
1
2
3
4
https://t7.baidu.com/it/u=3676218341,3686214618&fm=193&f=GIF
https://img0.baidu.com/it/u=1825304346,2860164199&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666
https://img2.baidu.com/it/u=3323311628,2330835932&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666
https://img2.baidu.com/it/u=3354585195,1512541150&fm=253&fmt=auto&app=138&f=JPEG?w=890&h=500
https://img0.baidu.com/it/u=3794430030,3426009381&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750
https://img0.baidu.com/it/u=2911164322,2998857390&fm=253&fmt=auto&app=138&f=JPEG?w=346&h=500
https://img1.baidu.com/it/u=2339589192,1294229754&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=647
https://img1.baidu.com/it/u=1622759797,988413400&fm=253&fmt=auto&app=138&f=JPEG?w=343&h=500
https://img1.baidu.com/it/u=3392591833,1640391553&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=714
https://img1.baidu.com/it/u=2972437056,3031136317&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666
https://img1.baidu.com/it/u=2775377376,69094478&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666
https://img1.baidu.com/it/u=2278717026,2923133725&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=665
https://img0.baidu.com/it/u=1397203767,4231030802&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=665
https://img0.baidu.com/it/u=3298051423,2256566422&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=683
https://img1.baidu.com/it/u=3649894598,1420228163&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750
https://img2.baidu.com/it/u=1348122341,4168765603&fm=253&fmt=auto&app=138&f=JPEG?w=888&h=500
https://img1.baidu.com/it/u=4139258405,3795750908&fm=253&fmt=auto&app=120&f=JPEG?w=1140&h=760
https://img0.baidu.com/it/u=3481356926,3210196434&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889
https://img2.baidu.com/it/u=2055715645,3308680961&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=750
https://img1.baidu.com/it/u=2236572226,1780107877&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=721
https://img2.baidu.com/it/u=3941944942,3848012963&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=665
https://img0.baidu.com/it/u=2849159227,3411294549&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=698
https://img1.baidu.com/it/u=2119626721,2606712208&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=666
https://img1.baidu.com/it/u=1983760057,2461413428&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500
https://img0.baidu.com/it/u=3673910398,1309627678&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=736
https://img0.baidu.com/it/u=4234484084,1391640149&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800
https://img1.baidu.com/it/u=3809752281,1928271428&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=1082
https://img0.baidu.com/it/u=4126260600,3694641195&fm=253&fmt=auto&app=138&f=JPEG?w=889&h=500
https://img0.baidu.com/it/u=518980756,774483131&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=713
https://img0.baidu.com/it/u=499045485,1119545308&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=1379
https://img1.baidu.com/it/u=316030150,2128050393&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=732
https://img1.baidu.com/it/u=3885230234,3434479436&fm=253&fmt=auto&app=138&f=JPEG?w=333&h=500
7、隐藏浏览器
由以上内容可知使用 Selenium 采集网页数据时,需要不断地调用浏览器。实际 上,通过对 Selenium 的设置,可以达到隐藏浏览器的效果。
public class FinancialRolling {
public static void main(String[] args) throws InterruptedException {
//设置隐藏浏览器
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");
//chromedriver配置
System.setProperty("webdriver.chrome.driver", "drivers/chromedriver");
//声明使用的是谷歌浏览器
ChromeDriver driver = new ChromeDriver(options);
//打开baidu
driver.get("http://image.baidu.com");
WebElement kw = driver.findElementById("kw");
kw.clear();
kw.sendKeys("美女");
WebElement button = driver.findElement(By.cssSelector("#homeSearchForm > span.s_btn_wr > input"));
button.click();
//等待页面加载
Thread.sleep(5 * 1000);
//执行JS操作
JavascriptExecutor js = (JavascriptExecutor)driver;
try {
js.executeScript("scrollTo(0, 5000)");
System.out.println("1");
Thread.sleep(5000); //调整休眠时间可以获取更多的内容
js.executeScript("scrollTo(5000, 10000)");
System.out.println("2");
Thread.sleep(5000);
js.executeScript("scrollTo(10000, 30000)"); // 继续下拉
System.out.println("3");
Thread.sleep(5000);
js.executeScript("scrollTo(10000, 50000)"); //继续下拉
System.out.println("4");
} catch (Exception e) {
System.out.println("Error at loading the page...");
driver.quit();
}
//解析数据
String html = driver.getPageSource();
Document doc = Jsoup.parse(html);
Elements elements = doc.select("#imgid > div:nth-child(1) > ul > li");
for (Element e : elements) {
System.out.println(e.attr("data-thumburl"));
}
//关闭浏览器
driver.quit();
}
}