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吗?
答案 0 :(得分:3)
这是一种方法:
search-lego-refinements
块。//ul/li/a
)。以下代码示例显示了如何完成此操作:
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&search_query=test&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)