selenium 爬虫

selenium 可以动态爬取网页数据,就像真实用户操作浏览器一样,从终端用户的角度测试应用程序,WebDriver通过原生浏览器支持或者浏览器扩展直接控制浏览器

webdriver下载

因为selenuim对浏览器的版本存在兼容问题,顾需要针对指定浏览器下载指定版本。

chrome版本下载

chrome 120.0.6099.71 64位

chrome 120.0.6099.71 32位

chrome 119.0.6045.124 64位

chrome 119.0.6045.124 32位

http://edgedl.me.gvt1.com/edgedl/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

https://edgedl.me.gvt1.com/edgedl/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

http://dl.google.com/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

https://dl.google.com/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

http://www.google.com/dl/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

https://www.google.com/dl/release2/chrome/adoddphnvux2ay3bbbynsckujihq_120.0.6099.110/120.0.6099.110_chrome_installer.exe

离线下载地址

https://chrome.noki.eu.org/

https://github.com/lyonna/ChromeOfflineInstallerDownloadAPI

1、添加依赖

        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>4.11.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>32.1.2-jre</version>
        </dependency>

2、工具类

import cn.hutool.core.collection.CollectionUtil;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.edge.EdgeDriver;
import org.openqa.selenium.edge.EdgeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Selenium 工具类
 *
 * @author kou
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class SeleniumUtil {

    private final ReptileProperties reptileProperties;

    /**
     * 获取chromeDriver
     *
     * @return chromeDriver
     */
    public WebDriver chromeDriver() {

        // 加载驱动路径
        System.setProperty("webdriver.chrome.driver", "D:/chromedriver.exe");
        // Chrome默认不允许跨机器调试,需要给启动命令加上白名单
        System.setProperty("webdriver.chrome.whitelistedIps", "");

        ChromeOptions options = new ChromeOptions();
        // 开启一个实验性参数excludeSwitches,用来隐藏window.navigator.webdriver返回true,这个参数必须是List
        options.setExperimentalOption("useAutomationExtension", false);
        // 开启开发者模式
        options.setExperimentalOption("excludeSwitches", Lists.newArrayList("enable-automation"));
        // 发现主要是这句是关键
        options.addArguments("--disable-blink-features=AutomationControlled");
        // options.addArguments("--incognito");
        // options.addArguments("--disable-infobars");
        //options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");
        options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36");

        // 禁用沙箱
        options.addArguments("--no-sandbox");
        // 无头浏览器,这样不会打开浏览器窗口
        // options.addArguments("--headless");
        // options.addArguments("--disable-gpu");
        options.addArguments("--remote-allow-origins=*");

        // 初始化一个谷歌浏览器实例,实例名称叫driver
        WebDriver driver = new ChromeDriver(options);

        return driver;
    }

    /**
     * 获取edgeDriver
     *
     * @return edgeDriver
     */
    public WebDriver edgeDriver() {
        // 加载驱动路径
        System.setProperty("webdriver.edge.driver", "D:/msedgedriver.exe");

        EdgeOptions options = new EdgeOptions();
        // 开启一个实验性参数excludeSwitches,用来隐藏window.navigator.webdriver返回true,这个参数必须是List
        options.setExperimentalOption("useAutomationExtension", false);
        //开启开发者模式
        options.setExperimentalOption("excludeSwitches", Lists.newArrayList("enable-automation"));
        // 发现主要是这句是关键
        options.addArguments("--disable-blink-features=AutomationControlled");
        options.addArguments("--incognito", "--disable-infobars");
        // options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36");
        options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");

        // 禁用沙箱
        options.addArguments("--no-sandbox");
        // 无头浏览器,这样不会打开浏览器窗口
        // options.addArguments("--headless");
        options.addArguments("--disable-gpu");
        options.addArguments("--remote-allow-origins=*");

        // 初始化一个谷歌浏览器实例,实例名称叫driver
        WebDriver driver = new EdgeDriver(options);

        return driver;
    }

    /**
     * 获取firefoxDriver
     *
     * @return firefoxDriver
     */
    public WebDriver firefoxDriver() {
        // 加载驱动路径
        System.setProperty("webdriver.gecko.driver", "D:/geckodriver.exe");
        System.setProperty("webdriver.chrome.whitelistedIps", "");

        FirefoxOptions options = new FirefoxOptions();
        options.addArguments("user-agent=Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36");

        // 无头浏览器,这样不会打开浏览器窗口
        options.addArguments("--headless");

        // 初始化一个谷歌浏览器实例,实例名称叫driver
        WebDriver driver = new FirefoxDriver(options);

        return driver;
    }

    /**
     * 获取表头
     *
     * @param table 表格
     * @return 表头
     */
    public List<String> getTableHead(WebElement table) {

        log.info("开始解析表头...");
        // 获取表头
        WebElement head = table.findElement(By.tagName("thead"));
        if (null == head) {
            return Collections.emptyList();
        }
        List<WebElement> headths = head.findElements(By.tagName("th"));
        List<String> headList = new ArrayList<>(headths.size());
        headths.forEach(t -> {
            headList.add(t.getText());
        });
        log.info("表头解析完成!!!");
        return headList;
    }

    /**
     * 获取表数据
     *
     * @param table 表格
     * @return 表头
     */
    public List<List<String>> getTableBody(WebElement table) {

        log.info("开始解析表数据...");
        // 获取表头
        WebElement tbody = table.findElement(By.tagName("tbody"));
        if (null == tbody) {
            return Collections.emptyList();
        }
        // 获取body数据行
        List<WebElement> bodyTrs = tbody.findElements(By.tagName("tr"));
        if (CollectionUtil.isEmpty(bodyTrs)) {
            return Collections.emptyList();
        }
        List<List<String>> bodyDatas = new ArrayList<>(bodyTrs.size());
        bodyTrs.stream().forEach(r -> {
            List<WebElement> tds = r.findElements(By.tagName("td"));
            List<String> rows = new ArrayList<>(tds.size());
            tds.forEach(d -> {
                rows.add(d.getText());
            });
            bodyDatas.add(rows);
        });
        log.info("表数据解析完成!!!");
        return bodyDatas;
    }

    /**
     * 将参数转化为路径参数
     *
     * @param params 参数
     * @return 路径参数
     */
    public String convertPathParams(Map<String, Object> params) {

        if (CollectionUtil.isEmpty(params)) {
            return "";
        }
        StringBuffer path = new StringBuffer();

        for (Map.Entry<String, Object> p : params.entrySet()) {
            path.append(p.getKey()).append("=").append(p.getValue().toString()).append("&");
        }
        return path.substring(0, path.length() - 1);
    }

}

3、爬取数据

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 数据接口实现类
 *
 * @author kou
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class DataServiceImpl {


    private final SeleniumUtil seleniumUtil;

    /**
     * 获取页面数据
     *
     * @return 数据
     */
    @Override
    public Map<String, Object> getHtmlData() {
        try {

            Map<String, Object> data = new HashMap<>();
            
            String url = "url";
            Map<String, Object> params = new HashMap<>();
            params.put("pageNum", 1);
            params.put("pageSize", 1000);

            String fullUrl = url + seleniumUtil.convertPathParams(params);
            WebDriver driver = seleniumUtil.firefoxDriver();
            driver.get(fullUrl);

            // 打开一个站点
            log.info("开始访问:{}", fullUrl);
            driver.get(fullUrl);

            String title = driver.getTitle();
            log.info("网页:{}", title);

            // 获取表格数据
            WebElement table = driver.findElement(By.id("table"));
            //显式等待,针对某个元素等待,等待超时时间100s,2s检测一次
            WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(100), Duration.ofSeconds(2));
            // wait.until(ExpectedConditions.presenceOfElementLocated(By.id("table")));
            wait.until(new ExpectedCondition<WebElement>() {
                @Override
                public WebElement apply(WebDriver text) {
                    log.info("开始检查tbody数据是否已加载");
                    WebElement table = text.findElement(By.id("table")).findElement(By.tagName("tbody"));
                    if (!table.isDisplayed()) {
                        log.info("检查结果:tbody数据未加载完,等待加载...");
                        return null;
                    }
                    log.info("检查结果:tbody数据加载完成!!!");
                    return table;
                }
            });

            // 获取表头
            List<String> headList = seleniumUtil.getTableHead(table);
            List<List<String>> bodyList = seleniumUtil.getTableBody(table);
            data.put("header", headList);
            data.put("body", bodyList);
            driver.close();
            return data;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

4、抓取控制台日志

        Logs logs = driver.manage().logs();
        LogEntries logEntries = logs.get(LogType.BROWSER);
        for (LogEntry entry : logEntries) {
            System.out.println(new Date(entry.getTimestamp()) + " " + entry.getLevel() + " " + entry.getMessage());
        }

可以添加设置

        ChromeOptions options = new ChromeOptions();
        LoggingPreferences logPrefs = new LoggingPreferences();
        logPrefs.enable(LogType.BROWSER, Level.ALL);
        // options.setCapability(CapabilityType.LOGGING_PREFS, logPrefs);
        options.setCapability("goog:loggingPrefs", logPrefs);
        // 初始化一个谷歌浏览器实例,实例名称叫driver
        ChromeDriver driver = new ChromeDriver(options);

5、获取访问页面的请求

        ChromeDriver driver = seleniumUtil.chromeDriver();

        DevTools devTools = driver.getDevTools();
        devTools.createSession();
        List<String> requests = new ArrayList<>();
        List<String> responses = new ArrayList<>();
        devTools.addListener(Network.requestWillBeSent(), request -> {
            String requestType = String.valueOf(request.getType());
            if (requestType.equals("Fetch") || requestType.equals("XHR")) {
                requests.add(request.getRequest().getUrl());
            }
        });

        devTools.addListener(Network.responseReceived(), response -> {
            String requestType = String.valueOf(response.getType());
            if (requestType.equals("Fetch") || requestType.equals("XHR")) {
                responses.add(response.getResponse().getUrl());
            }
        });

        // 启用监听器
        devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));

        driver.get("url");

6、禁止chrome自动更新  disable_chrome_update.bat  以管理员身份运行

@echo off
chcp 65001 > nul

REM 停止并禁用 gupdate 服务
echo 正在停止和禁用 gupdate 服务...
sc stop gupdate
sc config gupdate start= disabled
echo gupdate 服务已成功停止和禁用。

REM 停止并禁用 gupdatem 服务
echo 正在停止和禁用 gupdatem 服务...
sc stop gupdatem
sc config gupdatem start= disabled
echo gupdatem 服务已成功停止和禁用。

REM 删除 Chrome 的 Update 文件夹及其内容
set "chrome_dir=C:\Program Files (x86)\Google\Update"

REM 尝试结束 Chrome 进程以便删除文件
echo 正在结束 Chrome 进程以便删除文件...
taskkill /F /IM chrome.exe /T
echo Chrome 进程已成功终止。

REM 等待一段时间以确保进程被终止
echo 正在等待一段时间以确保进程被终止...
ping 127.0.0.1 -n 5 > nul

REM 检查 Chrome Update 文件夹是否存在
echo 正在检查 Chrome Update 文件夹是否存在...
if exist "%chrome_dir%" (
    REM 删除文件夹及其内容
    echo 正在删除 Chrome Update 文件夹及其内容...
    tasklist /FI "IMAGENAME eq chrome.exe" 2>NUL | find /I /N "chrome.exe">NUL
    if "%ERRORLEVEL%"=="0" (
        echo 关闭 Chrome 进程...
        taskkill /F /IM chrome.exe /T
    )
    echo 等待一段时间以确保进程被终止...
    ping 127.0.0.1 -n 5 > nul
    rmdir /Q /S "%chrome_dir%"
    
    REM 设置 Update 文件夹权限为拒绝对 SYSTEM 用户的完全控制
    echo 正在设置 Chrome Update 文件夹权限...
    icacls "%chrome_dir%" /deny SYSTEM:(F)
    echo 更新服务已成功关闭并禁用,Chrome Update 文件夹已删除,权限已修改。
) else (
    echo Chrome Update 文件夹不存在,无需删除。
)

pause

脚本功能解析
停止并禁用 gupdate 服务:通过执行命令sc stop gupdate和sc config gupdate start= disabled,停止并禁用 Chrome 的 gupdate 服务。

停止并禁用 gupdatem 服务:通过执行命令sc stop gupdatem和sc config gupdatem start= disabled,停止并禁用 Chrome 的 gupdatem 服务。

删除 Chrome 的 Update 文件夹及其内容:通过设置变量chrome_dir为 Chrome 的安装路径,然后使用命令rmdir /Q /S "%chrome_dir%"删除整个文件夹及其内容。若删除不成功,则使用命令icacls "%chrome_dir%" /deny SYSTEM:(F)拒绝 SYSTEM 用户对该文件夹的完全控制。
 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值