jsoup爬蟲技術及爬取微博資料例項
最近實現了一個爬取微博資料的小程式,藉此對爬蟲技術 jsoup的使用,以及實際開發過程中的細節進行總結。
首先,對於網路爬蟲的理解,它是一種能夠自動下載網頁、解析網頁的程式。網路中的資訊分散在數以億計的網頁中,而這些網頁中的資料儲存於數以百萬計的伺服器中。現實中的使用者只需通過在瀏覽器中訪問超連結便可以獲取資訊。爬蟲便可以通過模擬瀏覽器的方式,將多個超連結對應的網頁資訊收集起來。而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;
}
}
至此,微博資料的爬取完成!