You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无需配置代理,如何用Java+Selenium生成HAR文件提取网络资源?

嘿,我完全懂你不想折腾代理又要抓取Selenium多页面网络流量的需求!不用愁,咱们直接用Chrome DevTools Protocol(CDP)和Selenium结合就能搞定,全程不需要任何代理配置,纯Java实现就好。

核心思路

Selenium从3.x版本开始就支持和Chrome DevTools Protocol交互了,我们可以直接通过CDP启用浏览器的网络监听,捕获每个页面加载过程中的所有请求和响应数据,之后把这些数据转成JSON或者标准的HAR格式保存,完美避开代理配置的麻烦。

依赖准备

首先确保你的项目里引入了Selenium的Java依赖,还有Gson用来处理JSON序列化(如果要生成HAR也需要它)。如果用Maven的话,把下面的依赖加到pom.xml里:

<dependencies>
    <!-- Selenium Java -->
    <dependency>
        <groupId>org.seleniumhq.selenium</groupId>
        <artifactId>selenium-java</artifactId>
        <version>4.15.0</version>
    </dependency>
    <!-- Gson for JSON processing -->
    <dependency>
        <groupId>com.google.code.gson</groupId>
        <artifactId>gson</artifactId>
        <version>2.10.1</version>
    </dependency>
    <!-- 可选:用WebDriverManager自动管理ChromeDriver版本,不用手动下载驱动 -->
    <dependency>
        <groupId>io.github.bonigarcia</groupId>
        <artifactId>webdrivermanager</artifactId>
        <version>5.6.0</version>
    </dependency>
</dependencies>
完整代码实现

下面是一个可以直接运行的示例,实现多页面导航并捕获每个页面的网络流量,保存为JSON文件:

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.devtools.DevTools;
import org.openqa.selenium.devtools.v118.network.Network;
import org.openqa.selenium.devtools.v118.network.model.Request;
import org.openqa.selenium.devtools.v118.network.model.Response;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

public class SeleniumNetworkCapture {

    public static void main(String[] args) {
        // 可选:用WebDriverManager自动下载并配置ChromeDriver
        // WebDriverManager.chromedriver().setup();

        // 配置Chrome浏览器选项,比如开启最大化窗口,按需添加无头模式等
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--start-maximized");

        // 初始化ChromeDriver
        ChromeDriver driver = new ChromeDriver(options);
        driver.manage().timeouts().pageLoadTimeout(30, TimeUnit.SECONDS);

        // 获取DevTools会话,用来和CDP交互
        DevTools devTools = driver.getDevTools();
        devTools.createSession();

        // 启用Network域,开始监听网络请求
        devTools.send(Network.enable(Optional.empty(), Optional.empty(), Optional.empty()));

        // 存储每个页面的网络流量条目
        List<NetworkTrafficEntry> trafficEntries = new ArrayList<>();

        // 监听请求发送事件:捕获每个请求的详细信息
        devTools.addListener(Network.requestWillBeSent(), requestEvent -> {
            Request request = requestEvent.getRequest();
            trafficEntries.add(new NetworkTrafficEntry(
                    requestEvent.getRequestId(),
                    request.getUrl(),
                    "REQUEST",
                    request
            ));
        });

        // 监听响应接收事件:捕获每个响应的详细信息
        devTools.addListener(Network.responseReceived(), responseEvent -> {
            Response response = responseEvent.getResponse();
            trafficEntries.add(new NetworkTrafficEntry(
                    responseEvent.getRequestId(),
                    response.getUrl(),
                    "RESPONSE",
                    response
            ));
        });

        // ====== 第一个页面导航 ======
        driver.get("https://example.com");
        // 这里可以加显式等待,确保页面完全加载(比如等待某个核心元素出现)
        // new WebDriverWait(driver, 10).until(ExpectedConditions.visibilityOfElementLocated(By.tagName("h1")));

        // 保存第一个页面的流量到JSON文件
        saveTrafficToJson(trafficEntries, "page1_network_traffic.json");
        trafficEntries.clear(); // 清空列表,准备捕获下一个页面的流量

        // ====== 第二个页面导航 ======
        driver.get("https://example.org");
        // 同样添加显式等待
        saveTrafficToJson(trafficEntries, "page2_network_traffic.json");

        // 清理资源
        devTools.send(Network.disable());
        driver.quit();
    }

    // 自定义类,用来封装单个网络请求/响应的信息
    static class NetworkTrafficEntry {
        private String requestId;
        private String url;
        private String type; // REQUEST 或 RESPONSE
        private Object data; // 存储Request或Response对象

        public NetworkTrafficEntry(String requestId, String url, String type, Object data) {
            this.requestId = requestId;
            this.url = url;
            this.type = type;
            this.data = data;
        }

        // Getter和Setter方法,Gson需要这些来序列化
        public String getRequestId() { return requestId; }
        public void setRequestId(String requestId) { this.requestId = requestId; }
        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
        public String getType() { return type; }
        public void setType(String type) { this.type = type; }
        public Object getData() { return data; }
        public void setData(Object data) { this.data = data; }
    }

    // 将网络流量列表保存为格式化的JSON文件
    private static void saveTrafficToJson(List<NetworkTrafficEntry> entries, String filePath) {
        Gson gson = new GsonBuilder().setPrettyPrinting().create();
        String jsonContent = gson.toJson(entries);

        try (FileWriter writer = new FileWriter(filePath)) {
            writer.write(jsonContent);
            System.out.println("网络流量已成功保存到:" + filePath);
        } catch (IOException e) {
            System.err.println("保存JSON文件时出错:" + e.getMessage());
            e.printStackTrace();
        }
    }
}
生成标准HAR文件(可选)

如果需要生成符合标准的HAR文件(可以直接导入Chrome开发者工具查看),我们可以基于捕获到的请求和响应数据构造HAR格式的结构。下面是补充的代码片段:

// 新增HAR相关的实体类,对应HAR标准结构
static class HARLog {
    private String version = "1.2";
    private HARCreator creator = new HARCreator("Selenium CDP Capture", "1.0");
    private List<HAREntry> entries = new ArrayList<>();

    // Getter和Setter
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }
    public HARCreator getCreator() { return creator; }
    public void setCreator(HARCreator creator) { this.creator = creator; }
    public List<HAREntry> getEntries() { return entries; }
    public void setEntries(List<HAREntry> entries) { this.entries = entries; }
}

static class HARCreator {
    private String name;
    private String version;

    public HARCreator(String name, String version) {
        this.name = name;
        this.version = version;
    }

    // Getter和Setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getVersion() { return version; }
    public void setVersion(String version) { this.version = version; }
}

static class HAREntry {
    private HARRequest request;
    private HARResponse response;

    // Getter和Setter
    public HARRequest getRequest() { return request; }
    public void setRequest(HARRequest request) { this.request = request; }
    public HARResponse getResponse() { return response; }
    public void setResponse(HARResponse response) { this.response = response; }
}

static class HARRequest {
    private String url;
    private String method;
    private List<HARHeader> headers = new ArrayList<>();

    // Getter和Setter
    public String getUrl() { return url; }
    public void setUrl(String url) { this.url = url; }
    public String getMethod() { return method; }
    public void setMethod(String method) { this.method = method; }
    public List<HARHeader> getHeaders() { return headers; }
    public void setHeaders(List<HARHeader> headers) { this.headers = headers; }
}

static class HARResponse {
    private int status;
    private String statusText;
    private List<HARHeader> headers = new ArrayList<>();

    // Getter和Setter
    public int getStatus() { return status; }
    public void setStatus(int status) { this.status = status; }
    public String getStatusText() { return statusText; }
    public void setStatusText(String statusText) { this.statusText = statusText; }
    public List<HARHeader> getHeaders() { return headers; }
    public void setHeaders(List<HARHeader> headers) { this.headers = headers; }
}

static class HARHeader {
    private String name;
    private String value;

    public HARHeader(String name, String value) {
        this.name = name;
        this.value = value;
    }

    // Getter和Setter
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getValue() { return value; }
    public void setValue(String value) { this.value = value; }
}

// 将捕获的流量转换为标准HAR格式并保存
private static void saveTrafficToHAR(List<NetworkTrafficEntry> entries, String filePath) {
    HARLog harLog = new HARLog();
    // 用Map来临时存储请求,方便和响应配对
    java.util.Map<String, HAREntry> requestMap = new java.util.HashMap<>();

    for (NetworkTrafficEntry entry : entries) {
        if ("REQUEST".equals(entry.getType())) {
            Request req = (Request) entry.getData();
            HAREntry harEntry = new HAREntry();
            HARRequest harReq = new HARRequest();
            harReq.setUrl(req.getUrl());
            harReq.setMethod(req.getMethod());
            // 转换请求头
            req.getHeaders().forEach((k, v) -> harReq.getHeaders().add(new HARHeader(k, v)));
            harEntry.setRequest(harReq);
            requestMap.put(entry.getRequestId(), harEntry);
        } else if ("RESPONSE".equals(entry.getType())) {
            Response res = (Response) entry.getData();
            HAREntry harEntry = requestMap.get(entry.getRequestId());
            if (harEntry != null) {
                HARResponse harRes = new HARResponse();
                harRes.setStatus(res.getStatus());
                harRes.setStatusText(res.getStatusText());
                // 转换响应头
                res.getHeaders().forEach((k, v) -> harRes.getHeaders().add(new HARHeader(k, v)));
                harEntry.setResponse(harRes);
            }
        }
    }

    // 将配对好的条目加入HAR日志
    harLog.getEntries().addAll(requestMap.values());

    // 保存为HAR文件
    Gson gson = new GsonBuilder().setPrettyPrinting().create();
    String harContent = gson.toJson(harLog);

    try (FileWriter writer = new FileWriter(filePath)) {
        writer.write(harContent);
        System.out.println("HAR文件已成功保存到:" + filePath);
    } catch (IOException e) {
        System.err.println("保存HAR文件时出错:" + e.getMessage());
        e.printStackTrace();
    }
}

然后在主方法里,把saveTrafficToJson换成saveTrafficToHAR就能生成标准HAR文件了。

注意事项
  • CDP版本匹配:代码里用的是v118的Network API,要确保你的Chrome浏览器和ChromeDriver版本和这个API版本对应,或者用WebDriverManager自动管理版本,避免版本不兼容的问题。
  • 页面等待策略:不要只依赖pageLoadTimeout,最好用显式等待(比如等待页面核心元素加载完成),确保所有网络请求都已经完成再捕获流量。
  • 多页面清理:每个页面导航前一定要清空流量列表,避免不同页面的流量混在一起。
  • Firefox支持:如果用Firefox,同样可以通过CDP实现,不过API细节会略有不同,Chrome的CDP支持是最成熟的。

内容的提问来源于stack exchange,提问作者Bala

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:25:17