Java:爬虫htmlunit

为什么htmlunit与HttpClient两者都可以爬虫、网页采集、通过网页自动写入数据,我们会推荐使用htmlunit呢?

 一、网页的模拟化 

首先说说HtmlUnit相对于HttpClient的最明显的一个好处,HtmlUnit更好的将一个网页封装成了一个对象,如果你非要说HttpClient返回的接口HttpResponse实际上也是存储了一个对象那也可以,但是HtmlUnit不仅保存了这个网页对象,更难能可贵的是它还存有这个网页的所有基本操作甚至事件。这就是说,我们对于操作这个网页可以像在jsp中写js一样,这是非常方便的,比如:你想某个节点的上一个节点,查找所有的按钮,查找样式为“bt-style”的所有元素,对于某些元素先进行一些改造,然后再转成String,或者我直接得到这个网页之后操作这个网页,完成一次提交都是非常方便的。这意味着你如果想分析一个网页会来的非常的容易

二、网络响应的自动化处理 

HtmlUnit拥有强大的响应处理机制,我们知道:常见的404是找不到资源,100等是继续,300等是跳转...我们在使用HttpClient的时候它会把响应结果告诉我们,当然,你可以自己来判断,比如说,你发现响应码是302的时候,你就在响应头去找到新的地址并自动再跳过去,发现是100的时候就再发一次请求,你如果使用HttpClient,你可以这么去做,也可以写的比较完善,但是,HtmlUnit已经较为完整的实现了这一功能,甚至说,他还包括了页面JS的自动跳转(响应码是200,但是响应的页面就是一个JS)

三、并行控制 和串行控制 

既然HtmlUnit封装了那么多的底层api和hHttpClient操作,那么它有没有给我们提供自定义各种响应策略和监听整个执行过程的方法呢?,答案是肯定的。由于HtmlUnit提供的监听和控制方法比较多,我说几个大家可能接触比较少,但很有用的方法。其他的类似于:设置CSS有效,设置不抛出JS异常,设置使用SSL安全链接,诸如此类,大家通过webClient.getOptions().set***,就可以设置了,这种大家都比较熟了。 

四、强大的缓存机制     

为什么第一次获取一个网页可能会比较慢,但是第二次来拿就特别快呢?在HtmlUnit源码webClient类中的loadWebResponseFromWebConnection方法中我们可以看到。 

以下简单介绍下如何去分析网页及涉及的代码:

其中url可以直接浏览器访问地址直接解析页面,也可以通过分析页面请求接口(开启google浏览器F12开发者模式,刷新对应页面即可查看请求数据地址 -- >> 具体数据需要通过分享查看)

引入maven包: 

<htmlunit.version>2.70.0</htmlunit.version>
<junit.version>4.13.2</junit.version>

<dependency>
    <groupId>net.sourceforge.htmlunit</groupId>
    <artifactId>htmlunit</artifactId>
    <version>${htmlunit.version}</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
    <scope>test</scope>
</dependency>
@Slf4j
public class ThreadLocalClientFactory {

	//单例工厂模式
	private final static ThreadLocalClientFactory instance = new ThreadLocalClientFactory();
	//线程的本地实例存储器,用于存储WebClient实例
	private ThreadLocal<WebClient> clientThreadLocal;

	public ThreadLocalClientFactory() {
		clientThreadLocal = new ThreadLocal<>();
	}

	/**
	 * 获取工厂实例
	 *
	 * @return 工厂实例
	 */
	public static ThreadLocalClientFactory getInstance() {
		return instance;
	}

	public WebClient crawlPageApi() {
		WebClient webClient;
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例
		 * 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		if ((webClient = clientThreadLocal.get()) == null) {
			webClient = new WebClient(BrowserVersion.CHROME);
			clientThreadLocal.set(webClient);
			log.info("为线程 [ " + Thread.currentThread().getName() + " ] 创建新的WebClient实例!");
		} else {
			log.info("线程 [ " + Thread.currentThread().getName() + " ] 已有WebClient实例,直接使用. . .");
		}
		return webClient;
	}

	/**
	 * 获取一个模拟FireFox3.6版本的WebClient实例
	 *
	 * @return 模拟FireFox3.6版本的WebClient实例
	 */
	public WebClient crawlPageWithAnalyseJs() {
		WebClient webClient;
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例
		 * 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		if ((webClient = clientThreadLocal.get()) == null) {
			webClient = new WebClient(BrowserVersion.CHROME);
			webClient.getOptions().setCssEnabled(false);
			webClient.getOptions().setJavaScriptEnabled(true);  //需要解析js
			webClient.getOptions().setThrowExceptionOnScriptError(false);  //解析js出错时不抛异常
			webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
			webClient.getOptions().setTimeout(1000);
			clientThreadLocal.set(webClient);
			log.info("为线程 [ " + Thread.currentThread().getName() + " ] 创建新的WebClient实例!");
		} else {
			log.info("线程 [ " + Thread.currentThread().getName() + " ] 已有WebClient实例,直接使用. . .");
		}
		return webClient;
	}

	public WebClient crawlPageWithoutAnalyseJs(){
		WebClient webClient;
		/**
		 * 如果当前线程已有WebClient实例,则直接返回该实例
		 * 否则重新创建一个WebClient实例并存储于当前线程的本地变量存储器
		 */
		if ((webClient = clientThreadLocal.get()) == null) {
			webClient.getOptions().setCssEnabled(false);
			webClient.getOptions().setJavaScriptEnabled(false);
			webClient.getOptions().setTimeout(20000);
	        webClient.setJavaScriptErrorListener(new MyJSErrorListener());
			webClient.setJavaScriptTimeout(5000);
			clientThreadLocal.set(webClient);
			log.info("为线程 [ " + Thread.currentThread().getName() + " ] 创建新的WebClient实例!");
		} else {
			log.info("线程 [ " + Thread.currentThread().getName() + " ] 已有WebClient实例,直接使用. . .");
		}
		return webClient;
	}

}

有时候抓取某些网站即使设置webClient.getOptions().setThrowExceptionOnScriptError(false);

也不能关闭提示信息导致每次访问网页都会打印一大串无用信息,但是我们并不关心js报错问题,只关心拿下来页面的结果。

所以我们可以针对【DefaultJavaScriptErrorListener 】重写它里面的方法,把所有log输出语句全部删除,就可以不打印错误信息了

public class MyJSErrorListener extends DefaultJavaScriptErrorListener {

	@Override
	public void scriptException(HtmlPage page, ScriptException scriptException) {
	}

	@Override
	public void timeoutError(HtmlPage page, long allowedTime, long executionTime) {
	}

	@Override
	public void malformedScriptURL(HtmlPage page, String url, MalformedURLException malformedURLException) {

	}

	@Override
	public void loadScriptError(HtmlPage page, URL scriptUrl, Exception exception) {

	}

	@Override
	public void warn(String message, String sourceName, int line, String lineSource, int lineOffset) {

	}
}
@Slf4j
public class SpiderUtils {

	/**
	 * 获取http请求
	 *
	 * @param url
	 * @return
	 * @throws Exception
	 */
	public static String crawlPageApi(String url) throws Exception {
		// WebClient webClient = PooledClientFactory.getInstance().getClient();
		WebClient webClient = ThreadLocalClientFactory.getInstance().crawlPageApi();
		//抓取网页
		Page page = webClient.getPage(url);
		//打印当前线程名称及网页标题
		log.info(Thread.currentThread().getName() + " [ " + url + " ] : " + page.toString());
		WebResponse response = page.getWebResponse();
		String json = response.getContentAsString();
		log.info(Thread.currentThread().getName() + " [ " + json + " ] : ");
		return json;
	}

	/**
	 * 功能描述:抓取页面时并解析页面的js
	 *
	 * @param url
	 * @throws Exception
	 */
	public static HtmlPage crawlPageWithAnalyseJs(String url) throws Exception {
		WebClient webClient = ThreadLocalClientFactory.getInstance().crawlPageWithAnalyseJs();
		//抓取网页
		HtmlPage page = webClient.getPage(url);
		webClient.waitForBackgroundJavaScript(1000);
		//打印当前线程名称及网页标题
		System.out.println(Thread.currentThread().getName() + " [ " + url + " ] : " + page.getTitleText());
		return page;
	}

	/**
	 * 功能描述:抓取页面时不解析页面的js
	 *
	 * @param url
	 * @throws Exception
	 */
	public static HtmlPage crawlPageWithoutAnalyseJs(String url) throws Exception {
		WebClient webClient = ThreadLocalClientFactory.getInstance().crawlPageWithAnalyseJs();
		//抓取网页
		HtmlPage page = webClient.getPage(url);
		//打印当前线程名称及网页标题
		System.out.println(Thread.currentThread().getName() + " [ " + url + " ] : " + page.getTitleText());
		return page;
	}

}

目前来说,只是简单运用爬虫爬取抓取对应数据进行分析,具体的爬取规则需要根据实际情况来制定,数据量过大的时候还需要考虑通过读写分离,分库分表来解决效率问题

  • 29
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Java爬虫是一种利用Java语言编写的爬虫程序,可以用于自动化地获取互联网上的信息。获取网页源代码是Java爬虫程序中的一项基本功能,可以利用Java提供的网络编程和HTML解析库来实现。 Java爬虫获取网页源代码的过程通常分为以下几个步骤: 1. 网络连接:Java爬虫需要通过网络连接到要爬取的网站,可以使用Java提供的URLConnection或者Apache HttpClient等库进行网络连接。 2. 发起请求:一旦网络连接建立,可以构造HTTP请求,并附上必要的参数和头信息,向服务器发送请求。 3. 接收响应:服务器收到请求后,会返回HTTP响应,Java爬虫需要接收并解析这个响应。 4. HTML解析:HTTP响应中通常包括HTML代码,需要使用HTML解析库(如Jsoup)对其进行解析,提取出需要的内容。 5. 存储数据:Java爬虫通常需要将从网站上爬取的数据存储到数据库或者文件中,以供后续分析和使用。 拆分HTML是指将HTML代码拆分成元素,根据标签将其分解成一棵树形结构,每个节点代表一个元素,例如div、p等。Java提供了很多HTML解析库,如Jsoup、HtmlUnit等,可以方便地将HTML代码拆分成元素,并通过操作DOM树实现对元素的查找和修改等操作。在Java爬虫中,拆分HTML代码是非常重要的一步,可以大大简化数据的提取和处理过程,从而实现高效、精确的数据抓取。 ### 回答2: Java爬虫是一种程序,能够自动地获取Web页面数据并提取感兴趣的内容。Java爬虫获取源代码的方法是使用Java网络请求库发送HTTP GET请求,并通过响应获取所需的HTML源代码。 拆分HTML源代码的方法通常包括两个步骤。首先,Java爬虫需要使用HTML解析工具(例如Jsoup)将HTML源代码转换成可操作的DOM对象。这样,Java爬虫就可以对HTML进行操作,例如查找特定的元素、获取元素属性或文本内容。 第二步是使用Java爬虫的逻辑或正则表达式进行HTML内容的拆分。拆分HTML的方法通常有两种:一种是通过使用Java的正则表达式库快速地定位所需的内容;另一种方法是使用HTML标签语言对HTML代码进行结构性解析,以此找到我们需要的内容。 在拆分HTML的过程中,Java爬虫需要针对每个网站的HTML源代码进行特定的处理,以确保获取所需信息的准确性和完整性。一般而言,Java爬虫需要根据网站的HTML结构和文件格式,制定适当的策略,确保爬取数据质量达到预期。 ### 回答3: Java爬虫是一种可以自动化获取网页内容的程序,通常用于获取互联网上的数据和信息,比如抓取网页内容、分析网页结构等。 获取网页内容的第一步是获取源代码,这可以通过Java中的URLConnection或HttpClient等类库实现。这些类库可以建立HTTP连接,获取网页源代码。获取到的源代码通常是一个包含HTML标签和CSS样式等文本字符串。 随后需要对获取的源代码进行解析和处理,这时我们可以选择使用各种类库和工具(比如Jsoup)来解析HTML代码,根据HTML标签来提取所需的数据。这些工具可以方便地捕获网页中的标签、属性、文本等信息,并对其进行处理和转换。 当数据被提取后,我们还需要将其进行处理,主要是拆分HTML文件,分离出所需的内容。这需要使用正则表达式等方法,对提取的数据进行逐一判断,分割。这样我们就可以将网页内容划分为独立的组件,包括标题、文本、图像等,并且可以根据需要进行重新排布。 总的来说,Java爬虫获取网页源代码并拆分HTML是获取网页信息的基本流程。通过各种类库和工具,我们可以轻松地处理原始源代码,提取出所需的数据,并按照需要进行处理和调整,实现对网页信息的自动化采集和处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值