Как загрузить AJAX с HtmlUnit? - PullRequest
8 голосов
/ 23 июля 2011
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 ]

4 голосов
/ 18 июля 2014

Это сработало для меня. Установите это

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);
3 голосов
/ 23 июля 2011

Вот один из способов сделать это:

  1. Поиск страницы, как вы сделали в предыдущем вопросе .
  2. Выберите search-lego-refinements блок по идентификатору.
  3. Используйте XPath для перехода к URL (//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());
   }
}

Вы также можете просто просмотреть правильный URL-адрес запроса (http://www.youtube.com/results?search_type=videos&search_query=nyan+cat&search_sort=video_date_uploaded).

Но тогда вам придется кодировать параметры поиска (замените пробелы, например, +).

1 голос
/ 20 октября 2011
1 голос
/ 23 июля 2011

Я играл с HTMLUnit ранее для подобных целей.

На самом деле вы можете найти всю необходимую информацию здесь .В HTMLUnit поддержка AJAX включена по умолчанию, поэтому, когда вы получаете в коде объект newPage, вы можете генерировать события кликов на странице (найти определенный элемент и вызвать его функцию click()).Самое сложное в том, что AJAX является асинхронным, поэтому вам нужно вызывать wait() или sleep() после выполнения виртуального клика, чтобы код Javascript на сайте мог обрабатывать действия.Это не лучший подход, поскольку использование сети делает sleep() ненадежным.На странице вы можете найти что-то, что меняется, когда вы выполняете событие, вызывающее вызовы AJAX (например, изменяется заголовок заголовка), поэтому вы можете регулярно проверять, произошло ли это изменение на сайте или нет.(Я должен отметить, что в HTMLUnit встроен ресинхронизатор событий , однако мне не удалось заставить его работать так, как я ожидал.) Я использую Firebug или панель инструментов разработчика Chrome для изучения сайта.Вы можете проверить дерево DOM до и после вызовов AJAX, и таким образом вы будете знать, как ссылаться на определенные элементы управления (например, ссылки и выпадающие меню) на странице.

Я бы использовал 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>.Хотя сначала нужно нажать на кнопку выпадающего меню, потому что он создаст элемент <ul> со всеми описанными выше дочерними элементами с использованием Javascript.Вы можете получить сортировку по кнопке с этим XPath:

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

Вы можете проверить свои запросы XPath, например.прямо в Chrome, если вы откроете инструменты разработчика и консоль разработчика Javascript с его панели инструментов.Тогда вы можете проверить это следующим образом:

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

Надеюсь, что это приведет вас в правильном направлении.

...