最近实现了一个爬取微博数据的小程序,借此对爬虫技术 jsoup的使用,以及实际开发过程中的细节进行总结。
jsoup的jar包下载地址:https://jsoup.org/download
首先,对于网络爬虫的理解,它是一种能够自动下载网页、解析网页的程序。网络中的信息分散在数以亿计的网页中,而这些网页中的数据存储于数以百万计的服务器中。现实中的用户只需通过在浏览器中访问超链接便可以获取信息。爬虫便可以通过模拟浏览器的方式,将多个超链接对应的网页信息收集起来。而Jsoup要做的,就是帮我们完成这样一个程序。它是一个 Java 的开源HTML解析器,可直接解析某个URL地址,它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。它可以帮我们完成:
1. 从一个URL,文件或字符串中解析HTML
2. 使用DOM或CSS选择器来查找、取出数据
3. 对HTML元素、属性、文本进行操作
4. 清除不受信任的HTML (来防止XSS攻击)
jsoup 可以从包括字符串、URL 地址以及本地文件来加载 HTML 文档,并生成 Document 对象实例。下面贴出代码对几种方式进行解析:
// 从 URL中加载 HTML 文档
String url = "https://s.weibo.com/";
Document doc = Jsoup.connect(url).get();
// 从字符串中输入 HTML 文档
String html = "<html><head><title>MyPaper</title></head>" + "<body><p>这是一个页面</p></body></html>";
Document doc = Jsoup.parse(html);
// 从文件中加载 HTML 文档
File input = new File("MyPage.html");try
{
Document doc3 = Jsoup.parse(input, "UTF-8", "https://s.weibo.com/");
}catch(
IOException e)
{
e.printStackTrace();
}
而对于解析出来的Document对象,Element对象则提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的元素:
//通过标签里的id,tag,className等进行查找:
getElementById(String id)
getElementsByTag(String tag)
getElementsByClass(String className)
getElementsByAttribute(String key) (and related methods)
Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling()
Graph: parent(), children(), child(int index)
//定位后对所在标签和元素进行操作
attr(String key) :获取属性
attr(String key, String value) :设置属性
attributes() :获取所有属性
text() : 获取文本内容
text(String value) : 设置文本内容
html() : 获取元素内HTML
html(String value) : 设置元素内的HTML内容
outerHtml() : 获取元素外HTML内容
data() : 获取数据内容
下面用一个爬取微博数据的实例进行讲解:
需求 :从数据源界面如下所示:http://www.weibo.com 爬取:
微博(微博ID,微博昵称,微博内容,发布时间,发布平台,转发数,评论数,点赞数)
首先分析需要爬取的目标网页的url地址:
因此从url中进行加载的语句可写为:
String word = "双一流";
String url = "https://s.weibo.com/weibo?q=" + word + "&Refer=index&page=" + i;
Document doc = Jsoup.connect(url).userAgent("ie7:mozilla/4.0 (compatible; msie 7.0b; windows nt 6.0)")// 模拟浏览器访问
.timeout(3000)// 设置超时
.get();
下面对网页进行分析,为下一步使用Element对象抽取处理元素做准备:
可以注意到每一个微博动态块位于一个 action-type=feed_list_item 的div块中,但每篇热门文章也位于action-type=feed_list_item 的div块中,这是我们需要进行过滤的,对于解析出来的Document对象,Element对象则提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的元素,下面是选取的其中一个 动态块源代码 :
<!--card-wrap-->
<div class="card-wrap" action-type="feed_list_item" mid="4295851763028004" >
<div class="card-top">
<div class="card-top">
<h4 class="title"><i class="icon-title icon-star"></i><a href="/weibo?q=%E5%8F%8C%E4%B8%80%E6%B5%81&xsort=hot&Refer=hotmore" target="_blank" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,click:more">热门</a></h4>
</div>
</div>
<div class="card">
<div class="card-feed">
<div class="avator">
<a href="//weibo.com/1734530730?refer_flag=1001030103_" target="_blank" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:user_pic"><img src="//tvax2.sinaimg.cn/crop.0.0.996.996.50/6762d6aaly8fuuyo3xbt5j20ro0ro74w.jpg" /></a>
</div>
<!--微博内容-->
<div class="content" node-type="like">
<div class="info">
<div class="menu s-fr">
<a href="javascript:void(0);" action-type="fl_menu"><i class="wbicon">c</i></a>
<ul node-type="fl_menu_right" style="display:none;">
<li><a href="javascript:void(0);" onclick="javascript:window.open('//service.account.weibo.com/reportspam?rid=4295851763028004&type=1&from=10501&url=&bottomnav=1&wvr=6', 'newwindow', 'height=700, width=550, toolbar =yes, menubar=no, scrollbars=yes, resizable=yes, location=no, status=no');">举报</a></li>
</ul>
</div>
<div>
<a href="//weibo.com/1734530730?refer_flag=1001030103_" class="name" target="_blank" nick-name="大河报" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:user_name">大河报</a>
<a href="//verified.weibo.com/verify" target="_blank" title="微博官方认证"><i class="icon-vip icon-vip-b"></i></a>
<!--广告微博加关注按钮 -->
</div>
</div>
<p class="txt" node-type="feed_list_content" nick-name="大河报">
【大手笔!河南省财政厅2018年度安排“<em class="s-color-red">双</em><em class="s-color-red">一流</em>”建设资金7.7亿】建设世界<em class="s-color-red">一流</em>大学、<em class="s-color-red">一流</em>学科,简称"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"。河南省建设情况如何?16日,河南省人大召开专题会议,听取河南省"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设情况。记者从会场了解到,郑州大学、河南大学<em class="s-color-red">双</em><em class="s-color-red">双</em>进入"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设高校行列,省财政厅2018年度安排"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设资金 <a href="//weibo.com/1734530730/GEdxCcHIM?refer_flag=1001030103_" action-type="fl_unfold" target="_blank">展开全文<i class="wbicon">c</i></a> </p>
<p class="txt" node-type="feed_list_content_full" nick-name="大河报" style="display: none">
【大手笔!河南省财政厅2018年度安排“<em class="s-color-red">双</em><em class="s-color-red">一流</em>”建设资金7.7亿】建设世界<em class="s-color-red">一流</em>大学、<em class="s-color-red">一流</em>学科,简称"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"。河南省建设情况如何?16日,河南省人大召开专题会议,听取河南省"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设情况。记者从会场了解到,郑州大学、河南大学<em class="s-color-red">双</em><em class="s-color-red">双</em>进入"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设高校行列,省财政厅2018年度安排"<em class="s-color-red">双</em><em class="s-color-red">一流</em>"建设资金7.7亿元。<a href="http://t.cn/EzPKTgk" target="_blank"><i class="wbicon">O</i>大手笔!河南省财政厅2018年度安排“<em class="s-color-red">双</em><em class="s-color-red">一流</em>”建设资金7.7亿</a> <a href="javascript:void(0);" action-type="fl_fold">收起全文<i class="wbicon">d</i></a>
</p>
<!--card解析-->
<div node-type="feed_list_media_prev">
<div class="media media-piclist" node-type="fl_pic_list" action-data="uid=1734530730&mid=4295851763028004&pic_ids=6762d6aaly1fwaco8ljn5j20dw0990t1">
<ul class="m3">
<li><img src="//ww1.sinaimg.cn/thumb150/6762d6aaly1fwaco8ljn5j20dw0990t1.jpg" action-data="uid=1734530730&pic_id=6762d6aaly1fwaco8ljn5j20dw0990t1" action-type="fl_pics" suda-data="key=tblog_search_weibo&value=weibo_ss_1_pic"></li>
</ul>
</div>
</div>
<div node-type="feed_list_media_disp">
</div>
<!--/card解析-->
<p class="from" >
<a href="//weibo.com/1734530730/GEdxCcHIM?refer_flag=1001030103_" target="_blank" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:wb_time">
10月16日 20:52
</a>
来自 <a href="http://app.weibo.com/t/feed/1sxHP2" rel="nofollow">专业版微博</a> </p>
</div>
<!--/微博内容-->
</div>
<div class="card-act">
<ul>
<li><a href="javascript:void(0);" action-type="login" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:fav">收藏</a></li>
<li><a href="javascript:void(0);" action-data="allowForward=1&mid=4295851763028004&name=大河报&uid=1734530730" action-type="login" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:repost">转发 19</a></li>
<li><a href="javascript:void(0);" action-data="pageid=weibo&suda-data=key%3Dtblog_search_weibo%26value%3Dweibo_h_1_p_p" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:comment" action-type="login">评论 46</a></li>
<li><a title="赞" action-data="mid=4295851763028004" action-type="login" href="javascript:void(0);" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:like"><i class="icon-act icon-act-praise"></i> <em>29</em></a></li>
</ul>
</div>
<div node-type="feed_list_repeat"></div>
</div>
</div>
<!--/card-wrap-->
这里以微博ID为例,进行分析,微博ID位于class="name"的a标签中的href中,通过getElementsByClass("name").attr("href")获取到href后,还需使用正则表达式获取到十位数字的微博ID:
//<a href="//weibo.com/1734530730?refer_flag=1001030103_" class="name" target="_blank" nick-name="大河报" suda-data="key=tblog_search_weibo&value=seqid:15397465809059866363|type:1|t:0|pos:1-0|q:%E5%8F%8C%E4%B8%80%E6%B5%81|ext:cate:26,mpos:1,click:user_name">大河报</a>
String Href = link.getElementsByClass("name").attr("href");
String pattern = "/(\\d{10})";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(Href);
String Id;
if (m.find()) {
Id = m.group(1);
} else {
Id = "";
}
下面是其他目标元素的获取,以及置于exce中进行展示:
int k = 1;
Element content = doc.getElementById("pl_feedlist_index");
Elements links = content.getElementsByAttributeValue("action-type", "feed_list_item");
for (Element link : links) {
try {
//此处结合下面catch中的 continue; 实现对“热门文章”(和动态块里的标签种类不同,会执行catch中的代码)过滤;
String id_href = link.getElementsByClass("name").first().attr("href");
String pattern = "/(\\d{10})";
Pattern r = Pattern.compile(pattern);
Matcher m = r.matcher(id_href);
String Id;
if (m.find()) {
Id = m.group(1);
} else {
Id = "";
}
String Name = link.getElementsByClass("name").text();
String Content = link.getElementsByClass("txt").text();
String Time = link.getElementsByClass("from").first().getElementsByAttributeValue("target", "_blank").text();
String PlatForm = link.getElementsByClass("from").first().getElementsByAttributeValue("rel", "nofollow").text();
String Forward = link.getElementsByClass("card-act").first().getElementsByTag("li").get(1).text();
String Comment = link.getElementsByClass("card-act").first().getElementsByTag("li").get(2).text();
String Like = link.getElementsByClass("card-act").first().getElementsByTag("li").get(3).text();
//填入excel中进行展示
row = sheet.createRow(k);
row.createCell(0).setCellValue(Id);
row.createCell(1).setCellValue(Name);
row.createCell(2).setCellValue(Content);
row.createCell(3).setCellValue(Time);
row.createCell(4).setCellValue(PlatForm);
row.createCell(5).setCellValue(Forward);
row.createCell(6).setCellValue(Comment);
row.createCell(7).setCellValue(Like);
k++;
} catch (NullPointerException e) {
continue;
}
}
至此,微博数据的爬取完成!