selenium控制浏览器获取数据(java 版本)

目录

    • 1、前言
    • 2、操作案例
    • 3、代码解析
      • 3.1、WebDriverManager
      • 3.2、解决苹果url scheme导致的protocol handler弹窗问题
      • 3.3、操作
    • 4、chrome headless 模式

1、前言

  我这边是有个获取苹果的商店的版本更新信息的功能的,虽然直接使用 http 请求能拿到html 的最新版本更新的内容,但是我觉得这是只拿到最新,拿到历史记录需要模拟点击加载出本地的数据,才能获取到 html 进行解析,如果要历史的怎么办?
  所以研究了下有什么可以操作网页的框架,接着发现没啥能实现的,都是需要模拟浏览器运行,似乎都不太能行,遇到像 vue 这些新生代的框架加载不出来,直接报错,最后发现原来 chrome 出了个 headless 的server服务器运行的无界面运行模式,通过命令行制定参数即可。
  研究了下chrome 的 headless 模式,学习完之后,发现与 chrome 通信需要还需要使用 js 库进行通信,与我想的直接使用命令行返回html的结果有点出入,最后想到测试框架selenium,就去看了下,发现这个框架可以满足,我代码操作浏览器,操作完获取 html 解析即可。

浏览器版本:版本 105.0.5195.102(正式版本) (arm64)
系统:macOS

2、操作案例

下面即为可运行的示例代码

import io.github.bonigarcia.wdm.WebDriverManager;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

import java.util.*;

public class SeleniumUtil {

    public static void main(String[] args) throws InterruptedException {
        WebDriverManager.chromedriver().setup();
        ChromeOptions options = new ChromeOptions();
        boolean headless = true;
        options.setHeadless(headless);
        options.getCapabilityNames();
        options.addArguments("--disable-gpu");//设置为 headless 模式避免报错用的参数
        options.setPageLoadStrategy(PageLoadStrategy.NORMAL);
        options.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.IGNORE);//文档地址:https://www.selenium.dev/zh-cn/documentation/webdriver/capabilities/shared/#unhandledpromptbehavior
        //传下面参数来禁止掉谷歌受自动化控制的信息栏
        options.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
        if (!headless) {//有界面时给予跳过弹窗的处理
            Object prefs = getAllowProtocolPrefs();
            //Object prefs = getExcludedProtocolPrefs();
            //Object prefs = getCustomProtocolPrefs();
            options.setExperimentalOption("prefs", prefs);
        }
        options.addArguments("blink-settings=imagesEnabled=false");//禁用图片
        ChromeDriver driver = new ChromeDriver(options);
        try {
            driver.get("https://apps.apple.com/cn/app/id1314842898");
            handlerAlert(driver);
            String title = driver.getTitle();
            WebElement historyClass = driver.findElementByClassName("version-history");
            if (historyClass.isEnabled()) {
                historyClass.click();
                WebElement historyListUl = driver.findElement(By.className("version-history__items"));
                List historyList = historyListUl.findElements(By.tagName("li"));
                for (WebElement item : historyList) {
                    WebElement version = item.findElement(By.tagName("h4"));
                    WebElement updateTime = item.findElement(By.tagName("time"));
                    WebElement updateDesc = item.findElement(By.className("we-clamp"));
                    System.out.println("index=" + historyList.indexOf(item));
                    System.out.println("version=" + version.getText() + " updateTime=" + updateTime.getText());
                    System.out.println("updateDesc=" + updateDesc.getText());
                    System.out.println();
                }
            } else {
                System.out.println("not load");
            }
            System.out.println(title);
            Thread.sleep(5000L);
        } finally {
            driver.quit();
        }
    }

    public static void handlerAlert(ChromeDriver driver) {
        //        Wait wait = new FluentWait(driver)
//                .withTimeout(Duration.ofSeconds(30))
//                .pollingEvery(Duration.ofSeconds(5));
        Wait wait = new WebDriverWait(driver, 1L);
        try {
            wait.until(ExpectedConditions.alertIsPresent());
            Alert alert = driver.switchTo().alert();
            alert.dismiss();
        } catch (Exception e) {
        }
    }

    public static Object getCustomProtocolPrefs() {
        Map ignoredProtocolHandlers = new HashMap<>();
        ignoredProtocolHandlers.put("protocol", "macappstores");
        Map customHandlers = new HashMap<>();
        customHandlers.put("ignored_protocol_handlers", Arrays.asList(ignoredProtocolHandlers));
        customHandlers.put("enabled", true);
        Map prefs = new HashMap<>();
        prefs.put("custom_handlers", customHandlers);
        return prefs;
    }

    public static Object getExcludedProtocolPrefs() {
        Map excludedSchemes = new HashMap<>();
        excludedSchemes.put("macappstores", true);
        Map protocolHandler = new HashMap<>();
        protocolHandler.put("excluded_schemes", excludedSchemes);
        Map prefs = new HashMap<>();
        prefs.put("protocol_handler", protocolHandler);
        return prefs;
    }

    public static Object getAllowProtocolPrefs() {
        Map appleUrl = new HashMap<>();
        appleUrl.put("macappstores", true);
        Map allowedOriginProtocolPairs = new HashMap<>();
        allowedOriginProtocolPairs.put("https://apps.apple.com", appleUrl);
        Map protocolHandler = new HashMap<>();
        protocolHandler.put("allowed_origin_protocol_pairs", allowedOriginProtocolPairs);
        Map prefs = new HashMap<>();
        prefs.put("protocol_handler", protocolHandler);
        return prefs;
    }

}

gradle依赖示例:

    implementation 'org.seleniumhq.selenium:selenium-java:4.4.0'
    implementation group: 'io.github.bonigarcia', name: 'webdrivermanager', version: '5.3.0'
    implementation group: 'com.alibaba', name: 'fastjson', version: '2.0.12'

3、代码解析

3.1、WebDriverManager

  WebDriverManager是第三方的安装浏览器驱动的自动化库-安装浏览器驱动-1. 驱动管理软件,按照示例直接执行WebDriverManager.chromedriver().setup()语句即可,省去这个匹配版本的和成本。但我发现他每次都要 setup 一次,我没发现判断本地存在不存在驱动的参数,所以将就下直接 setup 吧。

3.2、解决苹果url scheme导致的protocol handler弹窗问题

  首先这个弹窗在selenium中的 alert 是获取不到的,这个不属于控制范围,所以要解决,不然在有界面情况下,操作不了浏览器,headless无界面不会弹出不用解决。
  代码里面有相关的测试代码,获取 alert 参考-JavaScript 警告框,提示框和确认框,里面的 wait 对象就是 wait 的策略-等待

下面为可找到的几种方式解释:

  • 始终允许跳转【可用】
    prefs参数中设置:{“protocol_handler”:{“allowed_origin_protocol_pairs”:{“https://apps.apple.com”:{“macappstores”:true}}}}
    f12开发者打开 network 菜单,触发即可看到外部协议的跳转地址,可以开头即是协议,自己查看即可
  • 禁止跳转 【不可用,手动操作界面勾选点取消都不行,最新版本应该改版更新了】
    prefs参数中设置:protocol_handler:{excluded_schemes:{“macappstores”:true}}
  • 设置禁用的 url,这个没找到 api 相关 api,大概率在protocol_handler的policy参数,有兴趣自己去找,但估计很费时不建议,直接贴地址-how to disable external protocol dialog?,并且贴出prefs参数可能的值得参数名定义-chrome/common/pref_names.cc[这个文件我谷歌了好多版本,贴出来不一定最新]
  • 设置custom_handlers,这个是谷歌回来的,没有收藏帖子,知道即可【不可用】
    selenium控制浏览器获取数据(java 版本)_第1张图片
    既然只有一个可用,那就将就了,不影响程序继续进行。

ps:自己找的时候可以在/Users/hui/Library/Application Support/Google/Chrome/Default/Preferences文件下找到生效的配置或者chrome://prefs-internals/查看配置,我自己手动点始终允许,找不到了相关配置,所以确信可以,相信如果界面设置了可行的方式,在这个文件找新增的配置,就可以找到对应的配置。
note:chrome://chrome-urls/可以看到所有支持的 chrome 设置路径

3.3、操作

  其实没啥好说,和普通 dom 操作一样,通过 tag、class、id名进行查找。或者 f12直接元素上面copy xpath 什么的直接寻找路径

4、chrome headless 模式

  虽说selenium可以操作浏览器,但是服务器可没有界面给你操作,所以这个 chrome 的 headless 模式也是重点,不然你怎么部署linux服务器上。
先贴一下文档地址:

  • Getting Started with Headless Chrome
  • headless-chrome/index.md【github地址】
    这里也直接贴出运行的命令行:
##远程 debug
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --remote-debugging-port=9222 https://apps.apple.com/cn/app/id1032287195
##直接输出 pdf
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --print-to-pdf=fileName.pdf --timeout=30000 https://apps.apple.com/cn/app/id1032287195
##截图
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --screenshot=fileName.jpg --window-size=1280,900 --timeout=30000 https://apps.apple.com/cn/app/id1032287195
##导出 dom 文件
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --dump-dom --timeout=30000 https://apps.apple.com/cn/app/id1032287195 > output.html
## repl默认,不太清楚是个啥
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --headless --disable-gpu --repl --crash-dumps-dir=./tmp https://apps.apple.com/cn/app/id1032287195
  • 默认输出文件为当前执行命令文件夹,不指定名字有默认名字,pdf=output.pdf,截图=screenshot.png,dom 为直接打印,重定向一下输出到文件即可。
  • 里面参数中,一个参数很重要参数是 --disable-gpu,linux 服务器一般没有 gpu 的,自己电脑测试应该要不要加都行。
  • 上面 debug 案例中访问网址chrome://inspect可以有界面的访问,这里 debug 只能本地,远程的话,自己百度怎么设置,而且远程也是不推荐的,容易被入侵什么的。
  • headless模式支持的参数可以自行访问-headless/app/headless_shell_switches.cc

note:这里不细讲,基本就是用各种参数输出结果,把上面网址换成https://www.baidu.com更好模拟,用上面那个网址加载时间略久,可以用作异常案例测试。

linux 下执行:

google-chrome-stable --headless --screenshot --window-size=1280,900 https://www.baidu.com
google-chrome-stable --headless --disable-gpu --dump-dom https://www.baidu.com > output.txt
google-chrome-stable --headless --headless --disable-gpu --remote-debugging-port=9222 https://www.baidu.com 

没什么问题,正常输出,安装 chrome 直接官网下载个deb文件,接着sudo dpkg -i 文件名.deb即可(ubuntu本地安装命令)
selenium控制浏览器获取数据(java 版本)_第2张图片

参考文章:

  • chrome远程调试-selenium chromedriver无界面模式调试
  • Selenium用法详解【Java爬虫】
  • 如何禁用“始终在关联的应用程序中打开这些类型的链接", Chrome警报

你可能感兴趣的:(java,selenium,java,chrome,headless)