httpclient 抓取网页(HtmlUnit使用场景对于使用java实现的网页爬虫程序呢? )

优采云 发布时间: 2022-04-10 08:30

  httpclient 抓取网页(HtmlUnit使用场景对于使用java实现的网页爬虫程序呢?

)

  HtmlUnit简介

  HtmlUnit is a "GUI-Less browser for Java programs". It models HTML documents and provides an API that allows you to invoke pages, fill out forms, click links, etc... just like you do in your "normal" browser.

It has fairly good JavaScript support (which is constantly improving) and is able to work even with quite complex AJAX libraries, simulating Chrome, Firefox or Internet Explorer depending on the configuration used.

It is typically used for testing purposes or to retrieve information from web sites.

HtmlUnit is not a generic unit testing framework. It is specifically a way to simulate a browser for testing purposes and is intended to be used within another testing framework such as JUnit or TestNG. Refer to the document "Getting Started with HtmlUnit" for an introduction.

HtmlUnit is used as the underlying "browser" by different Open Source tools like Canoo WebTest, JWebUnit, WebDriver, JSFUnit, WETATOR, Celerity, Spring MVC Test HtmlUnit, ...

HtmlUnit was originally written by Mike Bowler of Gargoyle Software and is released under the Apache 2 license. Since then, it has received many contributions from other developers, and would not be where it is today without their assistance.

  

HtmlUnit是一个无界面浏览器Java程序。它为HTML文档建模,提供了调用页面、填写表单、单击链接等操作的API。就跟你在浏览器里做的操作一样。

HtmlUnit不错的JavaScript支持(不断改进),甚至可以使用相当复杂的AJAX库,根据配置的不同模拟Chrome、Firefox或Internet Explorer等浏览器。

HtmlUnit通常用于测试或从web站点检索信息。

  HtmlUnit 使用场景

  对于java实现的网络爬虫程序,我们一般可以使用apache的HttpClient组件来获取HTML页面信息。HttpClient实现的http请求返回的响应一般是纯文本文档页面,也就是最原创的html页面。

  对于一个静态的html页面,使用httpClient就足够爬出我们需要的信息了。但是对于越来越多的动态网页,更多的数据是通过异步的JS代码来获取和渲染的,而初始的html页面并没有收录这部分数据。

  

  上图中我们看到的网页在初始文档加载后并没有看到红框中的数据列表。浏览器通过执行异步JS请求,将获取到的动态数据渲染到原创文档页面中,最终变成我们看到的网页。对于这部分需要JS代码执行的数据,httpClient是无能为力的。虽然我们可以通过研究得到JS执行的请求路径,然后用java代码来获取我们需要的部分数据,不说能不能从JS脚本中分析请求路径和请求参数,只分析这部分数据源码成本已经很高了。

  通过上面的介绍,我们了解到现在很大一部分动态网页都是通过异步JS请求获取的数据,然后通过JS渲染页面。那么我们能不能做这样的假设,假设我们的爬虫程序模拟一个浏览器,拿到html页面后,像浏览器一样执行异步JS代码,JS渲染好html页面后,就可以愉快的获取到节点信息了这页纸。那么有没有这样的java程序呢?

  答案是肯定的。

  HtmlUnit就是这样一个库,用来做界面显示的所有异步工作。没有耗时的显示工作,HtmlUnit 加载的完整网页比实际的浏览器块更多。并且根据不同的配置,HtmlUnit可以模拟市面上常见的Chrome、Firefox、IE浏览器等浏览器。

  通过HtmlUnit库,加载一个完整的Html页面(图片和视频除外),然后将其转换成我们常用的字符串格式,使用Jsoup等其他工具获取元素。当然,你也可以直接从HtmlUnit提供的对象中获取网页元素,甚至可以操作按钮、表单等控件。除了不能像可见浏览器那样用鼠标和键盘浏览网页之外,我们可以使用HtmlUnit来模拟所有其他的操作,比如登录网站、写博客等都可以完成。当然,网页内容抓取是最简单的应用。

  HtmlUnit 使用方法 1. 创建一个新的 maven 项目并添加 HtmlUnit 依赖项:

  

net.sourceforge.htmlunit

htmlunit

2.27

  2.新建一个Junit TestCase 试试这个库

  程序代码注释如下:

  package xuyihao.util.depend;

import com.gargoylesoftware.htmlunit.BrowserVersion;

import com.gargoylesoftware.htmlunit.NicelyResynchronizingAjaxController;

import com.gargoylesoftware.htmlunit.WebClient;

import com.gargoylesoftware.htmlunit.html.HtmlPage;

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.junit.Test;

import java.util.List;

/**

* Created by xuyh at 2017/11/6 14:03.

*/

public class HtmlUtilTest {

@Test

public void test() {

final WebClient webClient = new WebClient(BrowserVersion.CHROME);//新建一个模拟谷歌Chrome浏览器的浏览器客户端对象

webClient.getOptions().setThrowExceptionOnScriptError(false);//当JS执行出错的时候是否抛出异常, 这里选择不需要

webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);//当HTTP的状态非200时是否抛出异常, 这里选择不需要

webClient.getOptions().setActiveXNative(false);

webClient.getOptions().setCssEnabled(false);//是否启用CSS, 因为不需要展现页面, 所以不需要启用

webClient.getOptions().setJavaScriptEnabled(true); //很重要,启用JS

webClient.setAjaxController(new NicelyResynchronizingAjaxController());//很重要,设置支持AJAX

HtmlPage page = null;

try {

page = webClient.getPage("http://ent.sina.com.cn/film/");//尝试加载上面图片例子给出的网页

} catch (Exception e) {

e.printStackTrace();

}finally {

webClient.close();

}

webClient.waitForBackgroundJavaScript(30000);//异步JS执行需要耗时,所以这里线程要阻塞30秒,等待异步JS执行结束

String pageXml = page.asXml();//直接将加载完成的页面转换成xml格式的字符串

//TODO 下面的代码就是对字符串的操作了,常规的爬虫操作,用到了比较好用的Jsoup库

Document document = Jsoup.parse(pageXml);//获取html文档

List infoListEle = document.getElementById("feedCardContent").getElementsByAttributeValue("class", "feed-card-item");//获取元素节点等

infoListEle.forEach(element -> {

System.out.println(element.getElementsByTag("h2").first().getElementsByTag("a").text());

System.out.println(element.getElementsByTag("h2").first().getElementsByTag("a").attr("href"));

});

}

}

  上面的示例将获取到的页面中消息列表的标题和超链接 URL 打印到控制台。操作HTML文档的库是Jsoup,需要添加依赖:

  

org.jsoup

jsoup

1.8.3

  等待三十秒后,控制台输出如下:

  十一月 06, 2017 2:17:05 下午 com.gargoylesoftware.htmlunit.IncorrectnessListenerImpl notify

警告: Obsolete content type encountered: 'application/x-javascript'.

十一月 06, 2017 2:17:06 下午 com.gargoylesoftware.htmlunit.javascript.StrictErrorReporter runtimeError

严重: runtimeError: message=[An invalid or illegal selector was specified (selector: '*,:x' error: Invalid selector: :x).] sourceName=[http://n.sinaimg.cn/lib/core/core.js] line=[1] lineSource=[null] lineOffset=[0]

十一月 06, 2017 2:17:06 下午 com.gargoylesoftware.htmlunit.IncorrectnessListenerImpl notify

警告: Obsolete content type encountered: 'application/x-javascript'.

2017-11-06 14:17:11.003:INFO::JS executor for com.gargoylesoftware.htmlunit.WebClient@618c5d94: Logging initialized @7179ms to org.eclipse.jetty.util.log.StdErrLog

十一月 06, 2017 2:17:11 下午 com.gargoylesoftware.htmlunit.javascript.host.WebSocket run

严重: WS connect error

java.util.concurrent.ExecutionException: org.eclipse.jetty.websocket.api.UpgradeException: 0 null

at java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:357)

at java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1895)

at com.gargoylesoftware.htmlunit.javascript.host.WebSocket$1.run(WebSocket.java:151)

at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)

at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)

at java.lang.Thread.run(Thread.java:748)

Caused by: org.eclipse.jetty.websocket.api.UpgradeException: 0 null

at org.eclipse.jetty.websocket.client.WebSocketUpgradeRequest.onComplete(WebSocketUpgradeRequest.java:513)

at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:193)

at org.eclipse.jetty.client.ResponseNotifier.notifyComplete(ResponseNotifier.java:185)

at org.eclipse.jetty.client.HttpExchange.notifyFailureComplete(HttpExchange.java:269)

at org.eclipse.jetty.client.HttpExchange.abort(HttpExchange.java:240)

at org.eclipse.jetty.client.HttpConversation.abort(HttpConversation.java:141)

at org.eclipse.jetty.client.HttpRequest.abort(HttpRequest.java:748)

at org.eclipse.jetty.client.HttpDestination.abort(HttpDestination.java:444)

at org.eclipse.jetty.client.HttpDestination.failed(HttpDestination.java:224)

at org.eclipse.jetty.client.AbstractConnectionPool$1.failed(AbstractConnectionPool.java:122)

at org.eclipse.jetty.util.Promise$Wrapper.failed(Promise.java:136)

at org.eclipse.jetty.client.HttpClient$1$1.failed(HttpClient.java:588)

at org.eclipse.jetty.client.AbstractHttpClientTransport.connectFailed(AbstractHttpClientTransport.java:154)

at org.eclipse.jetty.client.AbstractHttpClientTransport$ClientSelectorManager.connectionFailed(AbstractHttpClientTransport.java:199)

at org.eclipse.jetty.io.ManagedSelector$Connect.failed(ManagedSelector.java:655)

at org.eclipse.jetty.io.ManagedSelector$Connect.access$1300(ManagedSelector.java:622)

at org.eclipse.jetty.io.ManagedSelector$1.failed(ManagedSelector.java:364)

at org.eclipse.jetty.io.ManagedSelector$CreateEndPoint.run(ManagedSelector.java:604)

... 3 more

Caused

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线