htmlUnit和Selenium的区别以及使用BrowserMobProxy捕获网络请求

1. Selenium:浏览器自动化之王

核心定位
        跨平台、跨语言的浏览器操控框架,通过驱动真实浏览器实现像素级用户行为模拟

技术架构

核心特性

  • 支持所有主流浏览器(含移动端模拟)

  • 精确的DOM元素定位(XPath/CSS/ID)

  • 屏幕截图与视频录制功能

  • 分布式测试能力(Selenium Grid)

2. HtmlUnit:无头浏览器轻骑兵

核心定位
        纯Java实现的无界面浏览器引擎,专为服务端自动化场景优化。

技术架构

核心特性

  • 毫秒级页面加载速度

  • 线程安全设计

  • 内置基础AJAX支持

  • Cookie自动管理

3. BrowserMobProxy:网络流量手术刀

核心定位
基于Netty开发的HTTP代理服务器,专为Web流量监控与操控设计。

技术架构

核心特性

  • 实时流量镜像

  • 请求/响应内容篡改

  • 性能指标采集(TTFB等)

  • 支持HTTPS中间人攻击

能力对比矩阵

维度SeleniumHtmlUnitBrowserMobProxy
执行环境真实浏览器进程纯JVM环境独立代理服务
JS支持完整ES6+ES5(Rhino引擎)不涉及
网络延迟模拟需扩展原生支持精确到毫秒级控制
跨域请求处理受同源策略限制自动绕过全流量穿透
移动端调试完整设备模拟仅UA伪装流量分析
典型应用场景自动化测试服务端爬虫接口监控

安装浏览器:Google Chrome谷歌为例



4、selenium和BrowserMobProxy捕获网络请求实例

驱动下载:Chrome for Testing 的可用性(135后版本)chromedriver.storage.googleapis.com/index.html(旧版本驱动)
安装需记住安装位置,启动时需要设置驱动路径

代码实现

getDynamicCrawlersDocument方法为htmlunit的请求监控使用
getParamsByNodeUrl方法为selenium的请求实现,selenium需要驱动支持,可以获取到复杂的请求接口

依赖:

<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-java</artifactId><version>4.10.0</version>
</dependency>
<!-- BrowserMob Proxy -->
<dependency><groupId>net.lightbody.bmp</groupId><artifactId>browsermob-core</artifactId><version>2.1.5</version>
</dependency>
<!-- ChromeDriver -->
<dependency><groupId>org.seleniumhq.selenium</groupId><artifactId>selenium-chrome-driver</artifactId><version>4.1.0</version>
</dependency>

代码工具类:

package com.zzkj.zei.utils;import com.zzkj.zei.component.ServerConfig;
import lombok.extern.slf4j.Slf4j;
import net.lightbody.bmp.BrowserMobProxy;
import net.lightbody.bmp.BrowserMobProxyServer;
import net.lightbody.bmp.client.ClientUtil;
import net.lightbody.bmp.core.har.Har;
import net.lightbody.bmp.core.har.HarEntry;
import net.lightbody.bmp.core.har.HarRequest;
import net.lightbody.bmp.mitm.manager.ImpersonatingMitmManager;
import net.lightbody.bmp.proxy.CaptureType;
import org.apache.commons.lang3.ObjectUtils;
import org.htmlunit.BrowserVersion;
import org.htmlunit.FailingHttpStatusCodeException;
import org.htmlunit.ScriptException;
import org.htmlunit.WebClient;
import org.htmlunit.html.HtmlPage;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Proxy;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.htmlunit.ProxyConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.concurrent.TimeUnit;
import java.time.Duration;
import java.util.*;/*** FileName: SeleniumUtils* Author: wzk* Date:2025/4/29 11:31*/
@Component
@Slf4j
public class SeleniumUtils {private static String SELENIUM_PATH;@Value("${selenium.chromedriver_path}")private String seleniumPath; // 非静态变量接收注入@PostConstructpublic void init() {SELENIUM_PATH = this.seleniumPath;}private final static List<String> UA_LIST = Arrays.asList("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/104.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.0.0 Safari/537.36","Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.0.0 Safari/537.36", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36");public static void main(String[] args) {
//        List<interfaceNodeData> interfaceNodeDataList = getParamsByNodeUrl(ce,true);List<interfaceNodeData> interfaceNodeDataList = getDynamicCrawlersDocument(gfwj1, 1000, true);for (interfaceNodeData interfaceNodeData : interfaceNodeDataList) {log.info("方法:{}  链接:{}   请求参数:{}" ,interfaceNodeData.getMethod(),interfaceNodeData.getUrl(), interfaceNodeData.getParams());}}public static List<interfaceNodeData> getDynamicCrawlersDocument(String url, Integer waitTime, boolean javaScriptEnabled) {List<interfaceNodeData> interfaceNodeDatas = new ArrayList<>();// 1. 启动BrowserMob代理BrowserMobProxy proxy = new BrowserMobProxyServer();proxy.start(0); // 自动分配端口int proxyPort = proxy.getPort();try {// 2. 配置HtmlUnit使用代理WebClient browser = new WebClient(BrowserVersion.CHROME);browser.getOptions().setProxyConfig(new ProxyConfig("localhost",proxyPort,"http"));// 启用HTTPS支持(忽略证书验证)browser.getOptions().setSSLInsecureProtocol("ssl");//解决动态页面抓取不到信息问题browser.getOptions().setCssEnabled(false);browser.getOptions().setJavaScriptEnabled(javaScriptEnabled);browser.getOptions().setThrowExceptionOnScriptError(false);browser.getOptions().setUseInsecureSSL(true);// 设置自定义的错误处理类browser.setJavaScriptErrorListener(new JsoupHtmlUintUtils.MyJSErrorListener());// 开始捕获请求proxy.newHar("zzjk");HtmlPage page = null;page = browser.getPage(url);// 等待后台脚本执行时间browser.waitForBackgroundJavaScript(waitTime);//            String pageAsXml = page.asXml();
//            document = Jsoup.parse(pageAsXml.replaceAll("\\<\\?xml.*?\\?>", ""));
//            document.setBaseUri(url);// 5. 获取并分析HAR数据Har har = proxy.getHar();processHarEntries(har, url, interfaceNodeDatas);} catch (ScriptException e) {log.error("getDynamicCrawlersDocument页面:{}     JavaScript 异常:{}", url, e.getMessage());} catch (UnknownHostException e) {log.error("getDynamicCrawlersDocument页面:{}     无法解析或找到指定的主机名:{}", url, e.getMessage());} catch (FailingHttpStatusCodeException e) {log.error("getDynamicCrawlersDocument页面:{}     HTTP 状态异常:{}", url, e.getStatusCode());} catch (Exception e) {log.error("getDynamicCrawlersDocument页面:{}    获取页面异常:{}", url, e.getMessage());} finally {// 6. 清理资源proxy.stop();}return interfaceNodeDatas;}public static List<interfaceNodeData> getParamsByNodeUrl(String url,Boolean isTime){long stat = new Date().getTime();System.setProperty("webdriver.chrome.driver", SELENIUM_PATH);   //设置chrome驱动程序的路径BrowserMobProxy proxy = new BrowserMobProxyServer();proxy.start(0); // 自动选择端口// 获取Selenium的Proxy对象Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxy);ChromeOptions opt = new ChromeOptions();opt.addArguments();opt.addArguments("--headless",               // 开启无界面模式"--disable-gpu",            // 禁用gpu"--remote-allow-origins=*", // 允许所有源访问"--ignore-certificate-errors", // 忽略证书错误"--user-agent=" + UA_LIST.get(0), // 设置请求头"--no-sandbox",             // 禁用沙盒,减少权限检查"--disable-dev-shm-usage",  // 避免共享内存问题"--log-level=3",            // 禁用 Chrome 日志"--blink-settings=imagesEnabled=false", // 禁止图片加载"--disable-extensions",     // 禁用扩展"--disable-javascript",     // 禁用 JavaScript(如果目标页面不需要 JS)"--disable-css",            // 禁用 CSS 渲染(按需)"--disable-fonts",          // 禁用字体加载"--dns-prefetch-disable",    // 禁用 DNS 预解析"--disk-cache-size=0",       // 禁用 缓存"--disable-cache"            // 禁用 缓存);opt.setCapability(CapabilityType.PROXY, seleniumProxy);WebDriver driver = new ChromeDriver(opt);   //初始化一个chrome驱动实例,保存到driver中try {driver.manage().window().maximize();driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); // 启用隐式等待return seleniumGetDocument(driver,proxy,url,isTime);} catch (Exception e) {log.info("错误URL: " + driver.getCurrentUrl());e.printStackTrace();} finally {driver.quit();  // 自动清理Cookies和会话proxy.stop();long end = new Date().getTime();log.info("selenium参数获取时间" + (end - stat));}return null;}public static List<interfaceNodeData> seleniumGetDocument(WebDriver driver, BrowserMobProxy proxy, String url,Boolean isTime) {List<interfaceNodeData> dataList = new ArrayList<>();try {// 启用MITM抓取HTTPSproxy.setMitmManager(new ImpersonatingMitmManager.Builder().trustAllServers(true).build());proxy.setHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.RESPONSE_CONTENT);proxy.newHar("zzkj");// 访问页面并等待driver.get(url);// 等待页面加载完成new WebDriverWait(driver, Duration.ofSeconds(30)).until(webDriver -> ((JavascriptExecutor) webDriver).executeScript("return document.readyState").equals("complete"));// 验证请求是否稳定validationAll(proxy);if (isTime){Thread.sleep(10000); // 休眠10秒}// 处理HAR数据Har har = proxy.getHar();processHarEntries(har, url, dataList);} catch (Exception e) {e.printStackTrace();}return dataList;}private static void validationAll(BrowserMobProxy proxy) throws InterruptedException {int retries = 0;int stableCount = 0;int lastEntrySize = 0;while (retries < 30 && stableCount < 3) { // 最多等30秒,稳定3次Thread.sleep(1000); // 每秒检查一次int currentSize = proxy.getHar().getLog().getEntries().size();if (currentSize == lastEntrySize) {stableCount++;} else {stableCount = 0;lastEntrySize = currentSize;}retries++;}if (retries >= 30) {log.info("----------------------- 请求验证稳定超时 -----------------------");}}// 处理 HAR 条目并过滤private static void processHarEntries(Har har, String baseUrl, List<interfaceNodeData> interfaceNodeDatas) {har.getLog().getEntries().forEach(entry -> {HarRequest request = entry.getRequest();String method = request.getMethod();String toUrl = request.getUrl();log.info("检测链接:{}",toUrl);// 过滤boolean isStaticResource = toUrl.matches(".*\\.(css|js|png|jpg|jpeg|gif|ico|woff|woff2|svg|mp4|mp3)(\\?.*)?$");boolean isStaticPath = toUrl.contains("/material/") ||toUrl.contains("/fonts/") ||toUrl.contains("/script/") ||toUrl.contains("/login/") ||toUrl.contains("/images/");if (("POST".equalsIgnoreCase(method) || "GET".equalsIgnoreCase(method)) &&
//                    !filterOutsideUrl(baseUrl, toUrl) &&isCurrentNodeUrl(baseUrl,toUrl) &&!isStaticResource &&!isStaticPath) {interfaceNodeData interfaceNodeData = new interfaceNodeData();interfaceNodeData.setUrl(toUrl);interfaceNodeData.setMethod(method);interfaceNodeData.setData(entry.getResponse().getContent().getText());interfaceNodeData.setParams(ObjectUtils.isEmpty(request.getPostData()) ? "" : request.getPostData().getText());interfaceNodeDatas.add(interfaceNodeData);}});}private static boolean isCurrentNodeUrl(String sourceUrl, String targetUrl) {// 移除协议、转为小写、处理末尾斜杠和index.htmlString normalizedSource = normalizeUrl(sourceUrl);String normalizedTarget = normalizeUrl(targetUrl);// 判断目标URL是否以当前节点URL开头return !normalizedSource.contains(normalizedTarget);}/*** 标准化URL处理*/private static String normalizeUrl(String url) {// 移除协议头并转为小写String normalized = url.replaceAll("^(http://|https://)", "").toLowerCase();// 移除末尾的 "/" 和 "index.html"normalized = normalized.replaceAll("/+$", "").replaceAll("/index\\.html$", "");return normalized;}}
package com.zzkj.zei.utils;import lombok.Data;/*** FileName: interfaceNodeData* Author: wzk* Date:2025/4/30 17:00*/
@Data
public class interfaceNodeData {String url;String data;String method;String params;interfaceNodeData(){}interfaceNodeData(String url,String method,String data,String params){this.url = url;this.method = method;this.data = data;this.params = params;}
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/905260.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/905260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SSRF相关

SSRF(Server Side Request Forgery,服务器端请求伪造)&#xff0c;攻击者以服务器的身份发送一条构造好的请求给服务器所在地内网进行探测或攻击。 产生原理&#xff1a; 服务器端提供了能从其他服务器应用获取数据的功能&#xff0c;如从指定url获取网页内容、加载指定地址的图…

SaaS备份的必要性:厂商之外的数据保护策略

在当今数字化时代&#xff0c;企业对SaaS&#xff08;软件即服务&#xff09;应用的依赖程度不断攀升。SaaS应用为企业提供了便捷的生产力工具&#xff0c;然而&#xff0c;这也使得数据安全面临诸多挑战&#xff0c;如意外删除、勒索软件攻击以及供应商故障等。因此&#xff0…

【Python 基础语法】

Python 基础语法是编程的基石&#xff0c;以下从核心要素到实用技巧进行系统梳理&#xff1a; 一、代码结构规范 缩进规则 使用4个空格缩进&#xff08;PEP 8标准&#xff09;缩进定义代码块&#xff08;如函数、循环、条件语句&#xff09; def greet(name):if name: # 正确缩…

利用“Flower”实现联邦机器学习的实战指南

一个很尴尬的现状就是我们用于训练 AI 模型的数据快要用完了。所以我们在大量的使用合成数据&#xff01; 据估计&#xff0c;目前公开可用的高质量训练标记大约有 40 万亿到 90 万亿个&#xff0c;其中流行的 FineWeb 数据集包含 15 万亿个标记&#xff0c;仅限于英语。 作为…

自动化测试与功能测试详解

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 什么是自动化测试? 自动化测试是指利用软件测试工具自动实现全部或部分测试&#xff0c;它是软件测试的一个重要组成 部分&#xff0c;能完成许多手工测试无…

MySQL全量,增量备份与恢复

目录 一.MySQL数据库备份概述 1.数据备份的重要性 2.数据库备份类型 3.常见的备份方法 二&#xff1a;数据库完全备份操作 1.物理冷备份与恢复 2.mysqldump 备份与恢复 3.MySQL增量备份与恢复 3.1MySQL增量恢复 3.2MySQL备份案例 三&#xff1a;定制企业备份策略思路…

Ubuntu 安装 Nginx

Nginx 是一个高性能的 Web 服务器和反向代理服务器&#xff0c;同时也可以用作负载均衡器和 HTTP 缓存。 Nginx 的主要用途 用途说明Web服务器提供网页服务&#xff0c;处理用户的 HTTP 请求&#xff0c;返回 HTML、CSS、JS、图片等静态资源。反向代理服务器将用户请求转发到…

人工智能 机器学习期末考试题

自测试卷2 一、选择题 1&#xff0e;下面哪个属性不是NumPy中数组的属性&#xff08; &#xff09;。 A&#xff0e;ndim B&#xff0e;size C&#xff0e;shape D&#xff0e;add 2&#xff0e;一个简单的Series是由&#xff08; &#xff09;的数据组成的。 A&#xff0e;两…

使用阿里云CLI调用OpenAPI

介绍使用阿里云CLI调用OpenAPI的具体操作流程&#xff0c;包括安装、配置凭证、生成并调用命令等步骤。 方案概览 使用阿里云CLI调用OpenAPI&#xff0c;大致分为四个步骤&#xff1a; 安装阿里云CLI&#xff1a;根据您使用设备的操作系统&#xff0c;选择并安装相应的版本。…

K8S Svc Port-forward 访问方式

在 Kubernetes 中&#xff0c;kubectl port-forward 是一种 本地与集群内资源&#xff08;Pod/Service&#xff09;建立临时网络隧道 的访问方式&#xff0c;无需暴露服务到公网&#xff0c;适合开发调试、临时访问等场景。以下是详细使用方法及注意事项&#xff1a; 1. 基础用…

23、DeepSeek-V2论文笔记

DeepSeek-V2 1、背景2、KV缓存优化2.0 KV缓存&#xff08;Cache&#xff09;的核心原理2.1 KV缓存优化2.2 性能对比2.3 架构2.4多头注意力 &#xff08;MHA&#xff09;2.5 多头潜在注意力 &#xff08;MLA&#xff09;2.5.1 低秩键值联合压缩 &#xff08;Low-Rank Key-Value …

MySQL OCP试题解析(2)

试题如下图所示&#xff1a; 一、题目背景还原 假设存在以下MySQL用户权限配置&#xff1a; -- 创建本地会计用户CREATE USER accountinglocalhost IDENTIFIED BY acc_123;-- 创建匿名代理用户&#xff08;用户名为空&#xff0c;允许任意主机&#xff09;CREATE USER % IDENTI…

深度学习Y7周:YOLOv8训练自己数据集

&#x1f368; 本文为&#x1f517;365天深度学习训练营中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、配置环境 1.官网下载源码 2.安装需要环境 二、准备好自己的数据 目录结构&#xff1a; 主目录 data images&#xff08;存放图片&#xff09; annotati…

英伟达Blackwell架构重构未来:AI算力革命背后的技术逻辑与产业变革

——从芯片暴力美学到分布式智能体网络&#xff0c;解析英伟达如何定义AI基础设施新范式 开篇&#xff1a;当算力成为“新石油”&#xff0c;英伟达的“炼油厂”如何升级&#xff1f; 2025年3月&#xff0c;英伟达GTC大会上&#xff0c;黄仁勋身披标志性皮衣&#xff0c;宣布了…

CurrentHashMap的整体系统介绍及Java内存模型(JVM)介绍

当我们提到ConurrentHashMap时&#xff0c;先想到的就是HashMap不是线程安全的&#xff1a; 在多个线程共同操作HashMap时&#xff0c;会出现一个数据不一致的问题。 ConcurrentHashMap是HashMap的线程安全版本。 它通过在相应的方法上加锁&#xff0c;来保证多线程情况下的…

Android开发-设计规范

在Android应用开发中&#xff0c;遵循良好的设计规范不仅能够提升用户体验&#xff0c;还能确保代码的可维护性和扩展性。本文将从用户界面&#xff08;UI&#xff09;、用户体验&#xff08;UX&#xff09;、性能优化以及代码结构等多个维度探讨Android开发中的设计规范&#…

泛型加持的策略模式:打造高扩展的通用策略工具类

一、传统策略模式的痛点与突破 1.1 传统策略实现回顾 // 传统支付策略接口 public interface PaymentStrategy {void pay(BigDecimal amount); }// 具体策略实现 public class AlipayStrategy implements PaymentStrategy {public void pay(BigDecimal amount) { /* 支付宝支…

物联网从HomeAssistant开始

文章目录 一、什么是home-assistant?1.核心架构2.集成架构 二、在树梅派5上安装home-assistant三、接入米家1.对比下趋势2.手动安装插件3.配置方式 四、接入公牛1.手动安装插件2.配置方式 五、接入海尔1.手动安装插件2.配置方式 六、接入国家电网 一、什么是home-assistant? …

系统架构-嵌入式系统架构

原理与特征 嵌入式系统的典型架构可概括为两种模式&#xff0c;即层次化模式架构和递归模式架构 层次化模式架构&#xff0c;位于高层的抽象概念与低层的更加具体的概念之间存在着依赖关系&#xff0c;封闭型层次架构指的是&#xff0c;高层的对象只能调用同一层或下一层对象…

计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 09.天空和背景

天空和背景 对于 3D 场景&#xff0c;通常可以通过在远处的地平线附近创造一些逼真的效果&#xff0c;来增强其真实感。我们可以采用天空盒、天空柱&#xff08;Skydome&#xff09;或天空穹&#xff08;Skydome&#xff09;等技术来模拟天空。 天空盒 天空盒&#xff08;Sk…