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

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<WebElement> 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<WebDriver> wait = new FluentWait<WebDriver>(driver)
//                .withTimeout(Duration.ofSeconds(30))
//                .pollingEvery(Duration.ofSeconds(5));
        Wait<WebDriver> 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<String, Object> ignoredProtocolHandlers = new HashMap<>();
        ignoredProtocolHandlers.put("protocol", "macappstores");
        Map<String, Object> customHandlers = new HashMap<>();
        customHandlers.put("ignored_protocol_handlers", Arrays.asList(ignoredProtocolHandlers));
        customHandlers.put("enabled", true);
        Map<String, Object> prefs = new HashMap<>();
        prefs.put("custom_handlers", customHandlers);
        return prefs;
    }

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

    public static Object getAllowProtocolPrefs() {
        Map<String, Object> appleUrl = new HashMap<>();
        appleUrl.put("macappstores", true);
        Map<String, Object> allowedOriginProtocolPairs = new HashMap<>();
        allowedOriginProtocolPairs.put("https://apps.apple.com", appleUrl);
        Map<String, Object> protocolHandler = new HashMap<>();
        protocolHandler.put("allowed_origin_protocol_pairs", allowedOriginProtocolPairs);
        Map<String, Object> 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,这个是谷歌回来的,没有收藏帖子,知道即可【不可用】
    在这里插入图片描述
    既然只有一个可用,那就将就了,不影响程序继续进行。

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服务器上。
先贴一下文档地址:

##远程 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本地安装命令)
在这里插入图片描述

参考文章:

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Java中使用Selenium来监听浏览器请求,你可以使用Selenium WebDriver提供的`BrowserMobProxy`类。BrowserMobProxy是一个代理服务器,它可以捕获和修改浏览器的请求和响应。 下面是一个简单的示例代码,演示如何使用BrowserMobProxy来监听浏览器请求: ```java import net.lightbody.bmp.BrowserMobProxy; import net.lightbody.bmp.BrowserMobProxyServer; import net.lightbody.bmp.core.har.Har; import net.lightbody.bmp.proxy.CaptureType; import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; public class SeleniumRequestListener { public static void main(String[] args) { // 创建BrowserMobProxy实例 BrowserMobProxy proxy = new BrowserMobProxyServer(); proxy.start(0); // 获取代理服务器地址和端口 String proxyUrl = proxy.getSocketAddress().getHostName() + ":" + proxy.getPort(); // 配置ChromeOptions,使用代理服务器 ChromeOptions options = new ChromeOptions(); options.addArguments("--proxy-server=http://" + proxyUrl); // 创建ChromeDriver实例,并设置ChromeOptions WebDriver driver = new ChromeDriver(options); // 启用代理服务器的请求捕获 proxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT); // 创建一个新的HAR(HTTP Archive)对象,用于存储请求和响应数据 Har har = proxy.newHar(); // 访问网页 driver.get("https://www.example.com"); // 获取HAR数据 har = proxy.getHar(); // 在这里可以对请求和响应数据进行处理 // 例如,可以遍历har.getLog().getEntries()来获取每个请求的详细信息 // 关闭浏览器和代理服务器 driver.quit(); proxy.stop(); } } ``` 在这个示例中,我们创建了一个BrowserMobProxy实例,并启用了请求捕获。然后,我们配置ChromeDriver来使用这个代理服务器。接下来,我们访问一个网页,并获取捕获的请求和响应数据。 你可以在`// 在这里可以对请求和响应数据进行处理`的部分对请求和响应数据进行处理,例如打印请求URL、检查响应状态码等。 这只是一个简单的示例,你可以根据自己的需求进行扩展和修改。希望对你有所帮助!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值