Selenium无头浏览器爬虫技术详解与示例
Selenium 是一个强大的工具,用于自动化各种浏览器操作。本文详细介绍如何使用 Selenium 控制谷歌浏览器,并提供了相关的代码示例和配置步骤。
一、安装和配置 Chrome 浏览器
访问以下链接下载 ChromeDriver:
https://googlechromelabs.github.io/chrome-for-testing/#stable
将下载的 ChromeDriver 解压并放置在系统路径中,或者记住它的位置以便在代码中引用。
二、设置驱动环境变量
你可以通过两种方式设置 ChromeDriver 的环境变量:
- 设置环境变量: 将驱动添加到系统的环境变量中。
- 在代码中声明驱动位置:
System.setProperty("webdriver.chrome.driver", "path/to/chromedriver");
三、添加 Selenium 依赖
在 Maven 项目中添加 Selenium 依赖:
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>4.10.0</version>
</dependency>
四、实现驱动加载
首先,我们创建一个 SpiderNode 类来管理爬虫节点:
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.text.StringEscapeUtils;
import com.alibaba.fastjson.JSONArray;
public class SpiderNode {
private Map<String, Object> jsonProperty = new HashMap<>();
private List<SpiderNode> nextNodes = new ArrayList<>();
private List<SpiderNode> prevNodes = new ArrayList<>();
private Set<String> parentNodes;
private Map<String, String> condition = new HashMap<>();
private Map<String, String> exception = new HashMap<>();
private Map<String, String> transmitVariable = new HashMap<>();
private String nodeName;
private String nodeId;
private AtomicInteger counter = new AtomicInteger();
// Getters, Setters, and other methods
}
接下来创建 DriverProvider 接口:
package org.spiderflow.selenium.driver;
import org.openqa.selenium.WebDriver;
import org.spiderflow.model.SpiderNode;
public interface DriverProvider {
String JAVASCRIPT_DISABLED = "javascript-disabled";
String USER_AGENT = "user-agent";
String HEADLESS = "headless";
String IMAGE_DISABLED = "image-disabled";
String HIDE_SCROLLBAR = "hide-scrollbar";
String PLUGIN_DISABLE = "plugin-disable";
String JAVA_DISABLE = "java-disable";
String INCOGNITO = "incognito";
String SANDBOX = "sandbox";
String WINDOW_SIZE = "window-size";
String MAXIMIZED = "maximized";
String ARGUMENTS = "arguments";
String support();
WebDriver getWebDriver(SpiderNode node, String proxyStr);
}
实现 ChromeDriverProvider 类:
import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.spiderflow.model.SpiderNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ChromeDriverProvider implements DriverProvider {
@Value("${selenium.driver.chrome:null}")
private String chromeDriverPath;
@Override
public String support() {
return chromeDriverPath != null ? "chrome" : null;
}
@Override
public WebDriver getWebDriver(SpiderNode node, String proxyStr) {
System.setProperty("webdriver.chrome.driver", chromeDriverPath);
ChromeOptions options = new ChromeOptions();
String userAgent = node.getStringJsonValue(USER_AGENT);
if (StringUtils.isNotBlank(userAgent)) {
options.addArguments("user-agent=\"" + userAgent +"\"");
}
if ("1".equals(node.getStringJsonValue(JAVASCRIPT_DISABLED))) {
options.addArguments("–-disable-javascript");
}
if ("1".equals(node.getStringJsonValue(IMAGE_DISABLED))) {
options.addArguments("blink-settings=imagesEnabled=false");
}
if ("1".equals(node.getStringJsonValue(HIDE_SCROLLBAR))) {
options.addArguments("--hide-scrollbars");
}
if ("1".equals(node.getStringJsonValue(HEADLESS))) {
options.setHeadless(true);
}
if (!"1".equals(node.getStringJsonValue(SANDBOX))) {
options.addArguments("--no-sandbox");
}
if ("1".equals(node.getStringJsonValue(INCOGNITO))) {
options.addArguments("--incognito");
}
if ("1".equals(node.getStringJsonValue(PLUGIN_DISABLE))) {
options.addArguments("--disable-plugins");
}
if ("1".equals(node.getStringJsonValue(JAVA_DISABLE))) {
options.addArguments("--disable-java");
}
String windowSize = node.getStringJsonValue(WINDOW_SIZE);
if (StringUtils.isNotBlank(windowSize)) {
options.addArguments("--window-size=" + windowSize);
}
if ("1".equals(node.getStringJsonValue(MAXIMIZED))) {
options.addArguments("--start-maximized");
}
String arguments = node.getStringJsonValue(ARGUMENTS);
if (StringUtils.isNotBlank(arguments)) {
options.addArguments(Arrays.asList(arguments.split("\r\n")));
}
if (StringUtils.isNotBlank(proxyStr)) {
Proxy proxy = new Proxy();
proxy.setHttpProxy(proxyStr);
options.setProxy(proxy);
}
return new ChromeDriver(options);
}
}
五、实现 Firefox 驱动
类似地,实现 FireFoxDriverProvider 类:
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.spiderflow.model.SpiderNode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class FireFoxDriverProvider implements DriverProvider {
@Value("${selenium.driver.firefox:null}")
private String firefoxDriverPath;
@Override
public String support() {
return firefoxDriverPath != null ? "firefox" : null;
}
@Override
public WebDriver getWebDriver(SpiderNode node, String proxyStr) {
System.setProperty("webdriver.gecko.driver", firefoxDriverPath);
FirefoxOptions options = new FirefoxOptions();
FirefoxProfile profile = new FirefoxProfile();
if (StringUtils.isNotBlank(proxyStr)) {
String[] hp = proxyStr.split(":");
profile.setPreference("network.proxy.type", 1);
profile.setPreference("network.proxy.http", hp[0]);
profile.setPreference("network.proxy.http_port", NumberUtils.toInt(hp[1], 8080));
profile.setPreference("network.proxy.no_proxies_on", "");
}
String userAgent = node.getStringJsonValue(USER_AGENT);
if (StringUtils.isNotBlank(userAgent)) {
profile.setPreference("general.useragent.override", userAgent);
}
if ("1".equals(node.getStringJsonValue(HEADLESS))) {
options.setHeadless(true);
}
if ("1".equals(node.getStringJsonValue(IMAGE_DISABLED))) {
profile.setPreference("permissions.default.image", 2);
}
String windowSize = node.getStringJsonValue(WINDOW_SIZE);
if (StringUtils.isNotBlank(windowSize)) {
options.addArguments("--window-size=" + windowSize);
}
String arguments = node.getStringJsonValue(ARGUMENTS);
if (StringUtils.isNotBlank(arguments)) {
options.addArguments(Arrays.asList(arguments.split("\r\n")));
}
String preferences = node.getStringJsonValue("preferences");
if (StringUtils.isNotBlank(preferences)) {
Arrays.asList(preferences.split("\r\n")).forEach(preference -> {
int index = preference.indexOf("=");
if (index > -1 && preference.length() > index + 1) {
String key = preference.substring(0, index);
String value = preference.substring(index + 1);
if (StringUtils.isNotBlank(value)) {
if ("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value)) {
profile.setPreference(key, "true".equalsIgnoreCase(value));
} else if ("0".equals(value) || NumberUtils.toInt(value, 0) != 0) {
profile.setPreference(key, NumberUtils.toInt(value, 0));
} else {
profile.setPreference(key, value);
}
}
}
});
}
options.setProfile(profile);
FirefoxDriver driver = new FirefoxDriver(options);
if ("1".equals(node.getStringJsonValue(MAXIMIZED))) {
driver.manage().window().maximize();
}
return driver;
}
}
六、创建驱动实例
使用 DriverProvider 创建 WebDriver 实例:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class WebDriverManager {
@Autowired
private ChromeDriverProvider chromeDriverProvider;
@Autowired
private FireFoxDriverProvider fireFoxDriverProvider;
public WebDriver createWebDriver(SpiderNode node, String browser, String proxy) {
if ("chrome".equalsIgnoreCase(browser)) {
return chromeDriverProvider.getWebDriver(node, proxy);
} else if ("firefox".equalsIgnoreCase(browser)) {
return fireFoxDriverProvider.getWebDriver(node, proxy);
}
return null;
}
}
通过上述步骤,我们完成了 Selenium 在 Linux 系统上的安装与配置,并实现了 Chrome 和 Firefox 浏览器的驱动程序。现在,你可以根据需要使用这些驱动程序进行浏览器的自动化操作。
使用示例:使用 Selenium 控制浏览器
下面是一个使用 Selenium 控制 Chrome 浏览器的示例。该示例展示了如何使用上面创建的 WebDriverManager 类来创建一个 WebDriver 实例,并通过它进行简单的浏览器自动化操作。
示例代码
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.By;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SeleniumExample {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
WebDriverManager webDriverManager = context.getBean(WebDriverManager.class);
// 创建 SpiderNode 节点,设置相关参数
SpiderNode node = new SpiderNode();
node.setStringJsonValue(DriverProvider.HEADLESS, "1");
node.setStringJsonValue(DriverProvider.USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36");
// 创建 WebDriver 实例
WebDriver driver = webDriverManager.createWebDriver(node, "chrome", null);
try {
// 打开一个网页
driver.get("https://www.google.com");
// 找到搜索框并输入查询
driver.findElement(By.name("q")).sendKeys("Selenium WebDriver");
// 提交搜索表单
driver.findElement(By.name("q")).submit();
// 打印页面标题
System.out.println("Page title is: " + driver.getTitle());
// 等待几秒钟查看结果
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 关闭浏览器
driver.quit();
}
}
}
WebDriver 常用 API 列表
以下是 Selenium WebDriver 中常用的一些 API 及其说明:
| API 方法 | 说明 |
|---|---|
get(String url) | 打开指定的 URL。 |
getCurrentUrl() | 获取当前页面的 URL。 |
getTitle() | 获取当前页面的标题。 |
findElement(By by) | 查找页面上的第一个符合条件的元素。 |
findElements(By by) | 查找页面上所有符合条件的元素,返回一个列表。 |
getPageSource() | 获取当前页面的源代码。 |
close() | 关闭当前窗口。 |
quit() | 退出 WebDriver 实例,关闭所有窗口。 |
getWindowHandles() | 获取所有窗口的句柄。 |
getWindowHandle() | 获取当前窗口的句柄。 |
switchTo().window(String name) | 切换到指定窗口。 |
switchTo().frame(int index) | 切换到指定索引的 frame。 |
switchTo().alert() | 切换到 alert。 |
navigate().to(String url) | 导航到指定的 URL。 |
navigate().back() | 导航到上一个页面。 |
navigate().forward() | 导航到下一个页面。 |
navigate().refresh() | 刷新当前页面。 |
manage().window().maximize() | 最大化当前窗口。 |
manage().window().getSize() | 获取当前窗口的尺寸。 |
manage().timeouts().implicitlyWait(long time, TimeUnit unit) | 设置隐式等待时间。 |
executeScript(String script, Object... args) | 执行 JavaScript 脚本。 |
示例代码说明
- 创建
ApplicationContext: 使用 Spring 框架的AnnotationConfigApplicationContext来获取应用上下文,并从中获取WebDriverManager实例。 - 设置
SpiderNode参数: 创建一个SpiderNode对象,并设置相关的浏览器参数(如无头模式、用户代理)。 - 创建
WebDriver实例: 调用webDriverManager.createWebDriver方法,传入SpiderNode对象、浏览器类型和代理信息,创建一个WebDriver实例。 - 自动化操作: 使用 WebDriver 实例打开网页,查找元素,输入查询,提交表单,打印页面标题,并在操作完成后关闭浏览器。
结论
通过以上示例和 API 列表,你可以更好地理解如何在 Java 项目中使用 Selenium WebDriver 进行浏览器自动化操作。无论是简单的页面访问还是复杂的浏览器交互,Selenium 都提供了强大的功能来满足需求。