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

使用Html Unit结合XPath无法获取预期结果的技术问题

问题描述

我尝试用Html Unit通过谷歌搜索爬取《印度教徒报》2019年6月24日的头条新闻,能成功获取搜索结果页面,但使用XPath提取头条新闻链接时始终失败,(HtmlAnchor) searchResultPage.getByXPath(xpath)返回null。我确认浏览器中看到的页面结构对应的XPath是正确的,搜索结果页面显示第二条结果为目标新闻链接。

出问题的代码片段:

HtmlPage page = client.getPage("https://www.google.co.in");

List<HtmlForm> allForms = page.getForms();
System.out.println("No of Forms Detected :  ---  " + allForms.size());
HtmlForm searchForm = allForms.get(0);
HtmlTextArea searchTextArea = searchForm.getTextAreaByName("q");
searchTextArea.setText("24 June 2019 news, the hindu");

HtmlInput gSearch = searchForm.getInputByName("btnK");
HtmlPage searchResultPage = (HtmlPage) gSearch.click();
client.waitForBackgroundJavaScript(5000 * 2);
System.out.println(searchResultPage.asNormalizedText());
String xpath = "//*[@id='web']/ol/li[2]/div/div[1]/h3/a";

HtmlAnchor topNewsLink = (HtmlAnchor) searchResultPage.getByXPath(xpath).get(0);
HtmlPage postPage = topNewsLink.click();

System.out.println(postPage.asNormalizedText());
问题原因及解决方案

核心原因

  1. 谷歌页面动态渲染差异:Html Unit的JavaScript渲染能力和真实浏览器有差距,即使调用waitForBackgroundJavaScript,页面实际渲染后的DOM结构可能和浏览器开发者工具中看到的不一致,导致依赖固定层级的XPath失效。
  2. XPath依赖不稳定的页面结构:谷歌搜索页面的ID(如web)、层级结构会频繁更新,固定位置(li[2])也可能因搜索结果排序变化而失效。
  3. 搜索框类型错误:谷歌搜索框实际是<input>元素,而非代码中使用的<textarea>,这可能导致搜索执行不完整,返回的结果页面不符合预期。

解决步骤

1. 修正搜索框元素类型

将HtmlTextArea改为HtmlTextInput,并使用setValueAttribute设置搜索内容:

HtmlTextInput searchTextInput = searchForm.getInputByName("q");
searchTextInput.setValueAttribute("24 June 2019 news, the hindu");

2. 优化页面等待与渲染

延长JavaScript等待时间,或刷新页面确保DOM完全加载:

client.waitForBackgroundJavaScript(10000);
searchResultPage = (HtmlPage) searchResultPage.refresh();

3. 使用鲁棒的XPath定位

放弃依赖固定层级和ID,改用目标域名、父容器类名等稳定特征定位:

// 匹配包含thehindu.com链接的搜索结果项
String xpath = "//div[@class='g']//a[contains(@href, 'thehindu.com')]";
List<HtmlAnchor> links = (List<HtmlAnchor>) searchResultPage.getByXPath(xpath);

4. 调试页面结构

打印页面XML对比真实DOM,确认XPath是否匹配:

System.out.println(searchResultPage.asXml());

5. 优化Html Unit配置

模拟主流浏览器,禁用不必要的特性减少渲染差异:

WebClient client = new WebClient(BrowserVersion.CHROME);
client.getOptions().setJavaScriptEnabled(true);
client.getOptions().setCssEnabled(false);
client.getOptions().setThrowExceptionOnScriptError(false);
修改后的完整代码示例
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.BrowserVersion;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import java.util.List;

public class HinduNewsCrawler {
    public static void main(String[] args) {
        try (WebClient client = new WebClient(BrowserVersion.CHROME)) {
            // 配置客户端
            client.getOptions().setJavaScriptEnabled(true);
            client.getOptions().setCssEnabled(false);
            client.getOptions().setThrowExceptionOnScriptError(false);
            client.getOptions().setThrowExceptionOnFailingStatusCode(false);

            // 访问谷歌搜索页
            HtmlPage page = client.getPage("https://www.google.co.in");

            // 获取搜索表单并输入关键词
            List<HtmlForm> allForms = page.getForms();
            System.out.println("检测到的表单数量: ---  " + allForms.size());
            HtmlForm searchForm = allForms.get(0);
            HtmlTextInput searchTextInput = searchForm.getInputByName("q");
            searchTextInput.setValueAttribute("24 June 2019 news, the hindu");

            // 执行搜索
            HtmlInput gSearch = searchForm.getInputByName("btnK");
            HtmlPage searchResultPage = (HtmlPage) gSearch.click();
            
            // 等待页面渲染并刷新
            client.waitForBackgroundJavaScript(10000);
            searchResultPage = (HtmlPage) searchResultPage.refresh();

            // 定位目标链接
            String xpath = "//div[@class='g']//a[contains(@href, 'thehindu.com')]";
            List<HtmlAnchor> links = (List<HtmlAnchor>) searchResultPage.getByXPath(xpath);

            if (!links.isEmpty()) {
                HtmlAnchor topNewsLink = links.get(0);
                HtmlPage postPage = topNewsLink.click();
                client.waitForBackgroundJavaScript(5000);
                System.out.println(postPage.asNormalizedText());
            } else {
                System.out.println("未找到《印度教徒报》的相关新闻链接");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:01:01