Java爬虫系列三:使用Jsoup解析HTML
在上一篇随笔《java爬虫系列二:使用httpclient抓取页面html》中介绍了怎么使用httpclient进行爬虫的第一步--抓取页面html,今天接着来看下爬虫的第二步--解析抓取到的html。
有请第二步的主角:jsoup粉墨登场。下面我们把舞台交给jsoup,让他完成本文剩下的内容。
============华丽的分割线=============
一、jsoup自我介绍
大家好,我是jsoup。
我是一款java 的html解析器,可直接解析某个url地址、html文本内容。它提供了一套非常省力的api,可通过dom,css以及类似于jquery的操作方法来取出和操作数据,用java写爬虫的同行们十之八九用过我。为什么呢?因为我在这个方面功能强大、使用方便。不信的话,可以继续往下看,代码是不会骗人的。
二、jsoup解析html
上一篇中,httpclient大哥已经抓取到了博客园首页的html,但是一堆的代码,不是程序员的人们怎么能看懂呢?这个就需要我这个html解析专家出场了。
下面通过案例展示如何使用jsoup进行解析,案例中将获取博客园首页的标题和第一页的博客文章列表
请看代码(在上一篇代码的基础上进行操作,如果还不知道如何使用httpclient的朋友请进行阅读):
- 引入依赖
<dependency> <groupid>org.jsoup</groupid> <artifactid>jsoup</artifactid> <version>1.12.1</version> </dependency>
- 实现代码。实现代码之前首先要分析下html结构。标题是<title>不用说了,那文章列表呢?按下浏览器的f12,查看页面元素源码,你会发现列表是一个大的div,id="post_list",每篇文章是小的div,class="post_item"
接下来就可以开始代码了,jsoup核心代码如下(整体源码会在文章末尾给出):
/** * 下面是jsoup展现自我的平台 */ //6.jsoup解析html document document = jsoup.parse(html); //像js一样,通过标签获取title system.out.println(document.getelementsbytag("title").first()); //像js一样,通过id 获取文章列表元素对象 element postlist = document.getelementbyid("post_list"); //像js一样,通过class 获取列表下的所有博客 elements postitems = postlist.getelementsbyclass("post_item"); //循环处理每篇博客 for (element postitem : postitems) { //像jquery选择器一样,获取文章标题元素 elements titleele = postitem.select(".post_item_body a[class='titlelnk']"); system.out.println("文章标题:" + titleele.text());; system.out.println("文章地址:" + titleele.attr("href")); //像jquery选择器一样,获取文章作者元素 elements footele = postitem.select(".post_item_foot a[class='lightblue']"); system.out.println("文章作者:" + footele.text());; system.out.println("作者主页:" + footele.attr("href")); system.out.println("*********************************"); }
根据以上代码你会发现,我通过jsoup.parse(string html)方法对httpclient获取到的html内容进行解析获取到document,然后document可以有两种方式获取其子元素:像js一样 可以通过getelementxxxx的方式 和 像jquery 选择器一样通过select()方法。 无论哪种方法都可以,我个人推荐用select方法处理。对于元素中的属性,比如超链接地址,可以使用element.attr(string)方法获取, 对于元素的文本内容通过element.text()方法获取。
- 执行代码,查看结果(不得不感慨博客园的园友们真是太厉害了,从上面分析首页html结构到jsoup分析的代码执行完,这段时间首页多了那么多文章)
由于新文章发布的太快了,导致上面的截图和这里的输出有些不一样。
三、jsoup的其他用法
我,jsoup,除了可以在httpclient大哥的工作成果上发挥作用,我还能自己独立干活,自己抓取页面,然后自己分析。分析的本领已经在上面展示过了,下面来展示自己抓取页面,其实很简单,所不同的是我直接获取到的是document,不用再通过jsoup.parse()方法进行解析了。
除了能直接访问网上的资源,我还能解析本地资源:
代码:
public static void main(string[] args) { try { document document = jsoup.parse(new file("d://1.html"), "utf-8"); system.out.println(document); } catch (ioexception e) { e.printstacktrace(); } }
四、jsoup另一个值得一提的功能
你肯定有过这种经历,在你的页面文本框中,如果输入html元素的话,保存后再查看很大概率会导致页面排版乱七八糟,如果能对这些内容进行过滤的话,就完美了。
刚好我jsoup就能做到。
public static void main(string[] args) { string unsafe = "<p><a href='网址' onclick='stealcookies()'>博客园</a></p>"; system.out.println("unsafe: " + unsafe); string safe = jsoup.clean(unsafe, whitelist.basic()); system.out.println("safe: " + safe); }
通过jsoup.clean方法,用一个白名单进行过滤。执行结果:
unsafe: <p><a href='网址' onclick='stealcookies()'>博客园</a></p> safe: <p><a rel="nofollow">博客园</a></p>
五、结束语
通过以上大家相信我很强大了吧,不仅可以解析httpclient抓取到的html元素,我自己也能抓取页面dom,我还能load并解析本地保存的html文件。
此外,我还能通过一个白名单对字符串进行过滤,筛掉一些不安全的字符。
最最重要的,上面所有功能的api的调用都比较简单。
============华丽的分割线=============
码字不易,点个赞再走呗~~
最后,附上案例中 解析博客园首页文章列表的完整源码:
package httpclient_learn; import java.io.ioexception; import org.apache.http.httpentity; import org.apache.http.httpstatus; import org.apache.http.client.clientprotocolexception; import org.apache.http.client.methods.closeablehttpresponse; import org.apache.http.client.methods.httpget; import org.apache.http.client.utils.httpclientutils; import org.apache.http.impl.client.closeablehttpclient; import org.apache.http.impl.client.httpclients; import org.apache.http.util.entityutils; import org.jsoup.jsoup; import org.jsoup.nodes.document; import org.jsoup.nodes.element; import org.jsoup.select.elements; public class httpclienttest { public static void main(string[] args) { //1.生成httpclient,相当于该打开一个浏览器 closeablehttpclient httpclient = httpclients.createdefault(); closeablehttpresponse response = null; //2.创建get请求,相当于在浏览器地址栏输入 网址 httpget request = new httpget("https://www.cnblogs.com/"); //设置请求头,将爬虫伪装成浏览器 request.setheader("user-agent","mozilla/5.0 (windows nt 6.1) applewebkit/537.36 (khtml, like gecko) chrome/74.0.3729.169 safari/537.36"); // httphost proxy = new httphost("60.13.42.232", 9999); // requestconfig config = requestconfig.custom().setproxy(proxy).build(); // request.setconfig(config); try { //3.执行get请求,相当于在输入地址栏后敲回车键 response = httpclient.execute(request); //4.判断响应状态为200,进行处理 if(response.getstatusline().getstatuscode() == httpstatus.sc_ok) { //5.获取响应内容 httpentity httpentity = response.getentity(); string html = entityutils.tostring(httpentity, "utf-8"); system.out.println(html); /** * 下面是jsoup展现自我的平台 */ //6.jsoup解析html document document = jsoup.parse(html); //像js一样,通过标签获取title system.out.println(document.getelementsbytag("title").first()); //像js一样,通过id 获取文章列表元素对象 element postlist = document.getelementbyid("post_list"); //像js一样,通过class 获取列表下的所有博客 elements postitems = postlist.getelementsbyclass("post_item"); //循环处理每篇博客 for (element postitem : postitems) { //像jquery选择器一样,获取文章标题元素 elements titleele = postitem.select(".post_item_body a[class='titlelnk']"); system.out.println("文章标题:" + titleele.text());; system.out.println("文章地址:" + titleele.attr("href")); //像jquery选择器一样,获取文章作者元素 elements footele = postitem.select(".post_item_foot a[class='lightblue']"); system.out.println("文章作者:" + footele.text());; system.out.println("作者主页:" + footele.attr("href")); system.out.println("*********************************"); } } else { //如果返回状态不是200,比如404(页面不存在)等,根据情况做处理,这里略 system.out.println("返回状态不是200"); system.out.println(entityutils.tostring(response.getentity(), "utf-8")); } } catch (clientprotocolexception e) { e.printstacktrace(); } catch (ioexception e) { e.printstacktrace(); } finally { //6.关闭 httpclientutils.closequietly(response); httpclientutils.closequietly(httpclient); } } }
上一篇: 腾讯后台研发实习 完整面经 ( 已拿offer )
下一篇: Java并发容器,底层原理深入分析