2011-07-22 62 views
8
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?如何使用HtmlUnit加載ajax?

回答

3

下面是做這件事:

  1. 搜索頁面,你在你的previous question一樣。
  2. 選擇search-lego-refinements按ID排序。
  3. 使用XPath導航到URL(從上一個ID開始時爲//ul/li/a)。
  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()); 
    } 
} 

你可以只瀏覽了正確的查詢網址,以及(http://www.youtube.com/results?search_type=videos&search_query=nyan+cat&search_sort=video_date_uploaded)。

但是,你將不得不編碼你的搜索參數(例如用+替換空格)。

+0

這種方式就像它應該那樣工作。非常感謝你。 –

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>元素包含附加href屬性的特殊<span>元素。當您點擊<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>​ 
] 

希望這會讓你朝正確的方向。

+0

如何感謝您詳細的解釋。我認爲這是ajax,但你是對的,這只是隱藏的列表。它簡化了我的問題,但我仍然需要學習如何在HtmlUnit中使用ajax :) –

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