【问题标题】:How to load ajax with HtmlUnit?如何使用 HtmlUnit 加载 ajax?
【发布时间】:2011-10-11 10:15:13
【问题描述】:
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.List;

import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlTextInput;

public class YoutubeBot {
private static final String YOUTUBE = "http://www.youtube.com";

public static void main(String[] args) throws FailingHttpStatusCodeException, MalformedURLException, IOException {
    WebClient webClient = new WebClient();
    webClient.setThrowExceptionOnScriptError(false);

    // This is equivalent to typing youtube.com to the adress bar of browser
    HtmlPage currentPage = webClient.getPage("http://www.youtube.com/results?search_type=videos&search_query=official+music+video&search_sort=video_date_uploaded&suggested_categories=10%2C24&uni=3");

    // Get form where submit button is located
    HtmlForm searchForm = (HtmlForm) currentPage.getElementById("masthead-search");

    // Get the input field.
    HtmlTextInput searchInput = (HtmlTextInput) currentPage.getElementById("masthead-search-term");
    // Insert the search term.
    searchInput.setText("java");

    // Workaround: create a 'fake' button and add it to the form.
    HtmlButton submitButton = (HtmlButton) currentPage.createElement("button");
    submitButton.setAttribute("type", "submit");
    searchForm.appendChild(submitButton);

    //Workaround: use the reference to the button to submit the form. 
    HtmlPage newPage = submitButton.click();

    //Find all links on page with given class
    final List<HtmlAnchor> listLinks = (List<HtmlAnchor>) currentPage.getByXPath("//a[@class='ux-thumb-wrap result-item-thumb']");      

    //Print all links to console
    for (int i=0; i<listLinks.size(); i++)
        System.out.println(YOUTUBE + listLinks.get(i).getAttribute("href"));

    }
}

此代码有效,但我只想对 youtube 剪辑进行排序,例如按上传日期。如何用 HtmlUnit 做到这一点?我必须点击过滤器,这应该通过 ajax 请求加载内容,然后我应该点击“上传日期”链接。我只是不知道加载 ajax 内容的第一步。使用 HtmlUnit 可以做到这一点吗?

【问题讨论】:

    标签: java ajax youtube htmlunit


    【解决方案1】:

    这对我有用。设置这个

    webClient.setAjaxController(new NicelyResynchronizingAjaxController());
    

    这会导致所有 ajax 调用都是同步的。

    这就是我设置 WebClient 对象的方式

    WebClient webClient = new WebClient(BrowserVersion.CHROME);
    webClient.getOptions().setJavaScriptEnabled(true);
    webClient.getOptions().setCssEnabled(false);
    webClient.getOptions().setUseInsecureSSL(true);
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
    webClient.getCookieManager().setCookiesEnabled(true);
    webClient.setAjaxController(new NicelyResynchronizingAjaxController());
    webClient.getOptions().setThrowExceptionOnScriptError(false);
    webClient.getCookieManager().setCookiesEnabled(true);
    

    【讨论】:

      【解决方案2】:
      【解决方案3】:

      这是一种方法:

      1. 像在 previous question 中一样搜索页面。
      2. 按 ID 选择 search-lego-refinements 块。
      3. 使用 XPath 导航到 URL(//ul/li/a,当您从前一个 id 开始时)。
      4. 点击选中的链接。

      下面的代码示例展示了它是如何完成的:

      import java.io.IOException;
      import java.net.MalformedURLException;
      import java.util.List;
      
      import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
      import com.gargoylesoftware.htmlunit.WebClient;
      import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
      import com.gargoylesoftware.htmlunit.html.HtmlButton;
      import com.gargoylesoftware.htmlunit.html.HtmlElement;
      import com.gargoylesoftware.htmlunit.html.HtmlForm;
      import com.gargoylesoftware.htmlunit.html.HtmlPage;
      import com.gargoylesoftware.htmlunit.html.HtmlTextInput;
      
      public class YoutubeBot {
         private static final String YOUTUBE = "http://www.youtube.com";
      
         @SuppressWarnings("unchecked")
         public static void main(String[] args) throws FailingHttpStatusCodeException, MalformedURLException, IOException {
            WebClient webClient = new WebClient();
            webClient.setThrowExceptionOnScriptError(false);
      
            // This is equivalent to typing youtube.com to the adress bar of browser
            HtmlPage currentPage = webClient.getPage(YOUTUBE);
      
            // Get form where submit button is located
            HtmlForm searchForm = (HtmlForm) currentPage.getElementById("masthead-search");
      
            // Get the input field
            HtmlTextInput searchInput = (HtmlTextInput) currentPage.getElementById("masthead-search-term");
      
            // Insert the search term
            searchInput.setText("java");
      
            // Workaround: create a 'fake' button and add it to the form
            HtmlButton submitButton = (HtmlButton) currentPage.createElement("button");
            submitButton.setAttribute("type", "submit");
            searchForm.appendChild(submitButton);
      
            // Workaround: use the reference to the button to submit the form.
            currentPage = submitButton.click();
      
            // Get the div containing the filters
            HtmlElement filterDiv = currentPage.getElementById("search-lego-refinements");
      
            // Select the first link from the filter block (Upload date)
            HtmlAnchor sortByDateLink = ((List<HtmlAnchor>) filterDiv.getByXPath("//ul/li/a")).get(0);
      
            // Click the 'Upload date' link
            currentPage = sortByDateLink.click();
      
            System.out.println(currentPage.asText());
         }
      }
      

      您也可以浏览正确的查询 URL (http://www.youtube.com/results?search_type=videos&amp;search_query=nyan+cat&amp;search_sort=video_date_uploaded)。

      但是您必须对搜索参数进行编码(例如,将空格替换为 +)。

      【讨论】:

        【解决方案4】:

        我之前曾出于类似目的使用过 HTMLUnit。

        其实你可以在here找到所有你需要的信息。 HTMLUnit 默认启用 AJAX 支持,因此当您在代码中获取 newPage 对象时,您可以在页面上发出点击事件(查找特定元素并将其称为 click() 函数)。最棘手的部分是 AJAX 是异步的,因此您必须在执行虚拟点击后调用 wait()sleep(),以便网站上的 Javascript 代码可以处理这些操作。这不是最好的方法,因为网络使用使sleep() 不可靠。当您执行进行 AJAX 调用的事件时,您可能会发现页面上的某些内容发生了变化(例如标题标题更改),因此您可以定期检查该更改是否已经发生在站点上。 (我应该提一下,HTMLUnit 中内置了一个event resynchronizer,但是我无法让它像我预期的那样工作。)我使用 Firebug 或 Chrome 的开发人员工具栏来检查该站点。您可以在 AJAX 调用之前和之后查看 DOM 树,这样您就知道如何引用页面上的特定控件(如链接和下拉菜单)。

        然后我会使用 XPath 来获取特定元素,例如。您可以这样做(来自 HTML 单元的示例):

        //get div which has a 'name' attribute of 'John'
        final HtmlDivision div = (HtmlDivision) page.getByXPath("//div[@name='John']").get(0);
        

        YouTube 实际上并没有使用 AJAX 来处理它的结果。当您单击结果页面上的排序下拉列表(这是一个装饰的&lt;button&gt;)时,将显示一个绝对定位的&lt;ul&gt;(这模拟组合的下拉部分),其中每个菜单项都有&lt;li&gt; 元素。 &lt;li&gt; 元素包含一个特殊的 &lt;span&gt; 元素,并附加了一个 href 属性。当您单击 &lt;span&gt; 元素时,Javascript 会将浏览器导航到此 href 值。

        例如。在我的情况下,按相关性排序 &lt;span&gt; 元素如下所示:

        <span href="/results?search_type=videos&amp;search_query=test&amp;suggested_categories=2%2C24%2C10%2C1%2C28" class=" yt-uix-button-menu-item" onclick=";window.location.href=this.getAttribute('href');return false;">Relevancia</span>
        

        您可以相对容易地获得这些 span 的列表,因为托管 &lt;ul&gt;&lt;body&gt; 的唯一此类子代。尽管您必须先单击下拉按钮,因为它将使用 Javascript 创建具有上述所有子元素的 &lt;ul&gt; 元素。您可以使用此 XPath 获得排序依据:

        //div[@class='sort-by floatR']/button
        

        您可以测试您的 XPath 查询,例如。如果您从它的工具栏打开开发者工具和 Javascript 开发者控制台,就在 Chrome 中。然后你可以这样测试:

        >  $x("//div[@class='sort-by floatR']/button")
        
        [
        <button type=​"button" class=​" yt-uix-button yt-uix-button-text yt-uix-button-active" onclick=​";​return false;​" role=​"button" aria-pressed=​"true" aria-expanded=​"true" aria-haspopup=​"true" aria-activedescendant data-button-listener=​"26">​…​</button>​
        ]
        

        希望这能让你找到正确的方向。

        【讨论】:

        • 感谢您的详细解释。我认为它是 ajax,但你是对的,这只是隐藏列表。它简化了我的问题,但我仍然需要学习在 HtmlUnit 中使用 ajax :)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-01-27
        • 2016-01-28
        • 1970-01-01
        • 2014-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多