如何使用HtmlUnit加载ajax?

时间:2011-07-22 22:35:29

标签: java ajax youtube htmlunit

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吗?

4 个答案:

答案 0 :(得分:3)

这是一种方法:

  1. 按照previous question
  2. 的方式搜索网页
  3. 按ID选择search-lego-refinements块。
  4. 当您从之前的ID开始时,使用XPath导航到URL(//ul/li/a)。
  5. 点击所选链接。
  6. 以下代码示例显示了如何完成此操作:

    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());
       }
    }
    

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

    但是你必须对搜索参数进行编码(例如用+替换空格)。

答案 1 :(得分:3)

这对我有用。设置此

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 :(得分:1)

我之前为了类似的目的使用过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来获取结果。当您点击结果页面上的排序下拉列表(这是一个装饰<button>)时,绝对定位<ul>会显示(这模拟了组合的下拉部分),其中<li>每个菜单项的元素。 <li>个元素包含一个附加了<span>属性的特殊href元素。当您点击<span>元素时,Javascript会将浏览器导航到此href值。

例如。在我的例子中,按相关性排序<span>元素如下所示:

<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>

您可以相对轻松地获取这些跨度列表,因为托管<ul><body>中唯一的此类孩子。虽然您必须首先单击下拉按钮,因为它将使用Javascript创建包含上述所有子项的<ul>元素。您可以使用此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>​
]

希望这会让你走向正确的方向。

答案 3 :(得分:1)