我这边是有个获取苹果的商店的版本更新信息的功能的,虽然直接使用 http 请求能拿到html 的最新版本更新的内容,但是我觉得这是只拿到最新,拿到历史记录需要模拟点击加载出本地的数据,才能获取到 html 进行解析,如果要历史的怎么办?
所以研究了下有什么可以操作网页的框架,接着发现没啥能实现的,都是需要模拟浏览器运行,似乎都不太能行,遇到像 vue 这些新生代的框架加载不出来,直接报错,最后发现原来 chrome 出了个 headless 的server服务器运行的无界面运行模式,通过命令行制定参数即可。
研究了下chrome 的 headless 模式,学习完之后,发现与 chrome 通信需要还需要使用 js 库进行通信,与我想的直接使用命令行返回html的结果有点出入,最后想到测试框架selenium,就去看了下,发现这个框架可以满足,我代码操作浏览器,操作完获取 html 解析即可。
浏览器版本:版本 105.0.5195.102(正式版本) (arm64)
系统:macOS
下面即为可运行的示例代码
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'
WebDriverManager是第三方的安装浏览器驱动的自动化库-安装浏览器驱动-1. 驱动管理软件,按照示例直接执行WebDriverManager.chromedriver().setup()语句即可,省去这个匹配版本的和成本。但我发现他每次都要 setup 一次,我没发现判断本地存在不存在驱动的参数,所以将就下直接 setup 吧。
首先这个弹窗在selenium中的 alert 是获取不到的,这个不属于控制范围,所以要解决,不然在有界面情况下,操作不了浏览器,headless无界面不会弹出不用解决。
代码里面有相关的测试代码,获取 alert 参考-JavaScript 警告框,提示框和确认框,里面的 wait 对象就是 wait 的策略-等待
下面为可找到的几种方式解释:
ps:自己找的时候可以在/Users/hui/Library/Application Support/Google/Chrome/Default/Preferences文件下找到生效的配置或者chrome://prefs-internals/查看配置,我自己手动点始终允许,找不到了相关配置,所以确信可以,相信如果界面设置了可行的方式,在这个文件找新增的配置,就可以找到对应的配置。
note:chrome://chrome-urls/可以看到所有支持的 chrome 设置路径
其实没啥好说,和普通 dom 操作一样,通过 tag、class、id名进行查找。或者 f12直接元素上面copy xpath 什么的直接寻找路径
虽说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
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本地安装命令)
参考文章: