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

HTMLUnit中button.click()无法返回新HtmlPage对象的问题求助

解决HTMLUnit点击AJAX分页按钮后页面不更新的问题

针对你遇到的分页按钮点击后页面内容未更新的问题,核心原因是AJAX异步加载数据时,HTMLUnit的页面同步逻辑需要调整,以下是具体的修复方案:

关键问题分析

  1. 分页按钮选择可能不准确:nav button[class^='Control__control']:last-child可能选中了不可点击的按钮(比如最后一页的禁用状态按钮),导致点击无效。
  2. AJAX等待逻辑不足:仅调用waitForBackgroundJavaScript可能无法确保DOM完全更新,需要等待特定元素的变化来确认数据加载完成。
  3. 页面对象处理错误:AJAX更新的是当前页面的DOM,而非返回新页面,直接替换page = newPage会导致无法获取更新后的内容。

修复后的代码

package com.mycompany.datacenter.app;

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
import org.w3c.dom.Node;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class App {

    public static void main(String[] args) throws IOException {
        String baseURL = "https://www.datacenters.com";
        String locURL = baseURL + "/locations";

        WebClient client = new WebClient();
        client.getOptions().setTimeout(60000);
        client.getOptions().setRedirectEnabled(true);
        client.getOptions().setJavaScriptEnabled(true);
        client.getOptions().setThrowExceptionOnFailingStatusCode(false);
        client.getOptions().setThrowExceptionOnScriptError(false);
        client.getOptions().setCssEnabled(true);
        client.getOptions().setUseInsecureSSL(true);
        client.setAjaxController(new NicelyResynchronizingAjaxController());

        HtmlPage page = client.getPage(locURL);
        Map<String, DCInfo> dcInfoList = new HashMap<>();

        while (true) {
            // 爬取当前页面数据
            DomNodeList<DomNode> list = page.querySelectorAll("a[class^='LocationTile__anchor']");
            for (DomNode node : list) {
                DCInfo dcInfo = new DCInfo();

                DomNode ownerNode = node.querySelector("div[class^='LocationTile__provider']");
                dcInfo.setDcOwner(ownerNode.getFirstChild().getNodeValue());

                DomNode locationNode = node.querySelector("div[class^='LocationTile__name']");
                dcInfo.setDcName(locationNode.getFirstChild().getNodeValue());

                DomNode locAddressNode = node.querySelector("div[class^='LocationTile__address']");
                dcInfo.setDcAddress(locAddressNode.getFirstChild().getNodeValue());

                Node hRef = node.getAttributes().getNamedItem("href");
                String dcLink = baseURL + hRef.getNodeValue();
                dcInfo.setDcLocationLink(dcLink);
                dcInfoList.put(dcLink, dcInfo);
                System.out.println(dcInfo);
            }

            // 处理分页:找到可点击的下一页按钮
            HtmlButton nextButton = null;
            DomNodeList<DomNode> buttons = page.querySelectorAll("nav button[class^='Control__control']");
            for (DomNode btnNode : buttons) {
                HtmlButton btn = (HtmlButton) btnNode;
                // 判断按钮是否为下一页且可点击(根据实际页面的按钮特征调整)
                if (!btn.isDisabled() && btn.getAttribute("aria-label") != null && btn.getAttribute("aria-label").contains("Next")) {
                    nextButton = btn;
                    break;
                }
            }

            if (nextButton == null) {
                // 没有可点击的下一页按钮,终止循环
                break;
            }

            // 点击下一页并等待AJAX加载完成
            nextButton.click();
            // 等待JavaScript执行完成,确保DOM更新
            client.waitForBackgroundJavaScript(15000);
            // 强制刷新页面的DOM状态
            page = (HtmlPage) page.refresh();
        }

        client.close();
        System.out.println("爬取完成,共获取" + dcInfoList.size() + "条机房信息");
    }
}

核心修改点说明

  • 精准定位下一页按钮:遍历分页按钮,通过isDisabled()和aria-label属性筛选出可点击的下一页按钮,避免选中无效按钮。
  • 优化AJAX等待与页面刷新:点击按钮后,先等待JavaScript执行,再调用page.refresh()强制同步DOM状态,确保获取到更新后的页面内容。
  • 完善循环终止条件:当找不到可点击的下一页按钮时,自动终止循环,避免无限循环。

额外建议

  • 可以增加日志输出,记录每一页的爬取状态和按钮判断结果,方便调试。
  • 如果页面有反爬机制,可适当增加点击后的延迟(比如Thread.sleep(2000)),模拟人工操作间隔。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 02:05:54