Selenium无头浏览器爬虫技术详解与示例

605 阅读4分钟

Selenium无头浏览器爬虫技术详解与示例

Selenium 是一个强大的工具,用于自动化各种浏览器操作。本文详细介绍如何使用 Selenium 控制谷歌浏览器,并提供了相关的代码示例和配置步骤。

一、安装和配置 Chrome 浏览器

访问以下链接下载 ChromeDriver:

https://googlechromelabs.github.io/chrome-for-testing/#stable

将下载的 ChromeDriver 解压并放置在系统路径中,或者记住它的位置以便在代码中引用。

二、设置驱动环境变量

你可以通过两种方式设置 ChromeDriver 的环境变量:

  1. 设置环境变量: 将驱动添加到系统的环境变量中。
  2. 在代码中声明驱动位置:
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 脚本。

示例代码说明

  1. 创建 ApplicationContext 使用 Spring 框架的 AnnotationConfigApplicationContext 来获取应用上下文,并从中获取 WebDriverManager 实例。
  2. 设置 SpiderNode 参数: 创建一个 SpiderNode 对象,并设置相关的浏览器参数(如无头模式、用户代理)。
  3. 创建 WebDriver 实例: 调用 webDriverManager.createWebDriver 方法,传入 SpiderNode 对象、浏览器类型和代理信息,创建一个 WebDriver 实例。
  4. 自动化操作: 使用 WebDriver 实例打开网页,查找元素,输入查询,提交表单,打印页面标题,并在操作完成后关闭浏览器。

结论

通过以上示例和 API 列表,你可以更好地理解如何在 Java 项目中使用 Selenium WebDriver 进行浏览器自动化操作。无论是简单的页面访问还是复杂的浏览器交互,Selenium 都提供了强大的功能来满足需求。