2011-07-22 23 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")); 

    } 
} 

Mã này đang hoạt động nhưng tôi chỉ muốn sắp xếp các clip youtube chẳng hạn như ngày tải lên. Làm thế nào để làm điều này với HtmlUnit? Tôi phải bấm vào bộ lọc, điều này sẽ tải nội dung theo yêu cầu ajax và sau đó tôi nên bấm vào liên kết "Ngày tải lên". Tôi chỉ không biết bước đầu tiên này, để tải nội dung ajax. Điều này có thể thực hiện với HtmlUnit không?Làm thế nào để tải ajax với HtmlUnit?

Trả lời

3

Dưới đây là một cách để làm điều đó:

  1. Tìm kiếm trang như bạn đã làm trong previous question của bạn.
  2. Chọn search-lego-refinements chặn theo id.
  3. Sử dụng XPath để điều hướng đến URL (//ul/li/a khi bạn bắt đầu từ id trước đó).
  4. Nhấp vào liên kết đã chọn.

Các mẫu mã sau đây cho thấy làm thế nào nó có thể được thực hiện:

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

Bạn chỉ có thể duyệt qua URL truy vấn chính xác cũng (http://www.youtube.com/results?search_type=videos&search_query=nyan+cat&search_sort=video_date_uploaded).

Nhưng sau đó bạn sẽ phải mã hóa (các) thông số tìm kiếm của mình (thay thế dấu cách bằng + chẳng hạn).

+0

Điều này hoạt động giống như nó cần. Cảm ơn nhiều. –

1

Tôi đã chơi với HTMLUnit trước đó cho các mục đích tương tự.

Thực ra bạn có thể tìm thấy tất cả thông tin bạn cần here. HTMLUnit có hỗ trợ AJAX được bật theo mặc định để khi bạn nhận được đối tượng newPage trong mã của mình, bạn có thể phát hành các sự kiện nhấp trên trang (tìm phần tử cụ thể và gọi hàm là click()). Phần khó nhất là AJAX là không đồng bộ, do đó bạn phải gọi wait() hoặc sleep() sau khi thực hiện nhấp chuột ảo để mã Javascript trên trang web có thể xử lý các hành động. Đây không phải là cách tiếp cận tốt nhất kể từ khi sử dụng mạng làm cho sleep() không đáng tin cậy. Bạn có thể tìm thấy một số điều trên trang thay đổi khi bạn thực hiện một sự kiện thực hiện cuộc gọi AJAX (ví dụ: tiêu đề tiêu đề thay đổi) để bạn có thể kiểm tra thường xuyên nếu thay đổi này đã xảy ra với trang web hay không. (Tôi nên đề cập rằng có một event resynchronizer được tích hợp vào HTMLUnit, tuy nhiên tôi không thể quản lý để làm cho nó hoạt động như tôi mong đợi.) Tôi sử dụng thanh công cụ dành cho nhà phát triển Firebug hoặc Chrome để kiểm tra trang web. Bạn có thể kiểm tra cây DOM trước và sau khi gọi AJAX và theo cách này bạn sẽ biết cách tham khảo các điều khiển cụ thể (như liên kết và menu thả xuống) trên trang.

Tôi sẽ sử dụng XPath để lấy các phần tử cụ thể, ví dụ: bạn có thể làm điều này (từ ví dụ của Đơn vị HTML):

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

YouTube thực sự không sử dụng AJAX để sử dụng kết quả của nó. Khi bạn nhấp vào trình đơn thả xuống sắp xếp trên trang kết quả (đây là trang trí <button>), vị trí tuyệt đối <ul> hiển thị (điều này mô phỏng phần thả xuống của combo) có các thành phần <li> cho từng mục menu. <li> yếu tố có chứa một phần tử đặc biệt <span> với thuộc tính href được đính kèm. Khi bạn nhấp vào mã Javascript yếu tố <span> điều hướng trình duyệt đến giá trị href này.

Ví dụ: trong trường hợp của tôi sắp xếp theo yếu tố liên quan <span> trông như thế này:

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

Bạn có thể lấy danh sách các nhịp tương đối dễ dàng kể từ khi lưu trữ <ul> là đứa con duy nhất như vậy <body>. Mặc dù bạn phải nhấp vào nút thả xuống trước tiên vì nó sẽ tạo thành phần <ul> với tất cả các child được mô tả ở trên bằng cách sử dụng Javascript. Bạn có thể nhận nút sắp xếp bằng XPath này:

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

Bạn có thể kiểm tra các truy vấn XPath của mình ví dụ: ngay trong Chrome nếu bạn mở công cụ nhà phát triển và bảng điều khiển dành cho nhà phát triển Javascript từ thanh công cụ của nó. Sau đó, bạn có thể thử nghiệm như sau:

> $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>​ 
] 

Hy vọng điều này sẽ giúp bạn đi đúng hướng.

+0

Cảm ơn bạn đã giải thích chi tiết. Tôi nghĩ rằng đó là ajax, nhưng bạn đúng, đây chỉ là danh sách ẩn. Nó đơn giản hóa vấn đề của tôi, nhưng tôi vẫn phải học cách sử dụng ajax trong HtmlUnit :) –

3

này đã làm việc cho tôi. Đặt số này

webClient.setAjaxController(new NicelyResynchronizingAjaxController()); 

Điều này sẽ làm cho tất cả các cuộc gọi ajax được đồng bộ.

Đây là cách tôi thiết lập đối tượng WebClient của tôi

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); 
Các vấn đề liên quan