一、概述
曾幾時,我還是一個屌絲,一個在校大學生,高中的時候老師就對我們撒了一個慌,說...。人們稱它為一個善意的謊言,我卻傻傻信以為正。高三的時候努力拼搏了一段時間,可惜命運總是愛作弄人,高考考到了一個二流的大學,從此我告別了家鄉,踏上了大學校門,來到了一個鳥不生蛋但會拉屎的地方。剛來大學的時候,大一渾渾噩噩的度過,大門不錯,二門不邁,整體呆在宿舍打游戲,打了大半年的游戲,就那樣,大學里最美好的日子離我遠去。往事不堪回首,過了一年,如今已是學長了,作為一個學長,我不甘落后,於是乎上課努力聽講,下課沒事和同學去打球或是去圖書館學習html,css,javascript,java,過着四點一線的生活,在大二一年里拿了兩次獎學金。如今,已經是大三...,時間還在流逝,現在還是個屌絲,一個苦逼的大學生,還有事沒事喜歡爬各種網站,來滿足自己那點小小的虛榮心...。好了,扯淡時間過,該寫代碼了。
原諒我那點發自內心的扯淡。下面我們開始進入今天的主題,HTML解析和網絡爬蟲。
什么是html,網絡爬蟲?
什么是html這里就不多說了,那么什么是網絡爬蟲呢?是不是在網絡上怕的蟲?哈哈,簡直是弱爆了,在前面扯淡的內容中提到了,我喜歡爬各種網站,我爬過我學校的官網和教務管理系統,爬過各種IT網站,做了個簡單的新聞客戶端。網絡爬蟲其實是指自動地抓取萬維網信息的程序或者腳本,或者說是動態地抓取網站數據的程序。
怎樣解析html?
這里我們通過Java解析html的利器Jsoup解析html,使用jsoup輕松搞定html解析,讓你從一個從矮窮挫瞬間變身高大上,高端大氣上檔次。
為什么要解析html?
我們都知道現在網絡數據傳輸有三種常用的形式,xml,json(【JSON解析】JSON解析高手 )和html,我們的客戶端請求服務器,服務器通常給我們返回上面三種形式的數據。同時如果是個人開發,由於沒有自己的服務器,那么我們開發的應用程序就可以通過爬別人的網站解析html得到我們要的數據,當然,這種方式得到的數據不推薦,同時也存在太多的局限了,如:受網站的限制,解析困難等等。當然看了這篇文章解析就不是困難了,呵呵。
二、Jsoup和資源准備
jsoup介紹:
jsoup 是一款Java 的HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於jQuery的操作方法來取出和操作數據。
主要功能:
- 從一個URL,文件或字符串中解析HTML;
- 使用DOM或CSS選擇器來查找、取出數據;
- 可操作HTML元素、屬性、文本;
jar包下載(兩種方式):
jsoup更多信息查看官網:http://jsoup.org
三、HTML解析實戰
新建一個Android項目(編碼設為UTF-8),將下載的jsoup的jar包添加到項目的libs目錄下,並添加到構建路徑中,這里由於不打算開發一個完整的應用,所以用的開發工具是我們更熟悉的eclipse,簡單點,不用Android Studio(as),用as也一樣。
作為測試數據,我們來爬一下這個網站:http://it.ithome.com/
訪問這個網站,可以看到現在最新的頁面顯示如下,當然,它的文章會不斷更新,這是寫文章的時候的頁面(頁面的一部分):

我們的任務是把文章的相關信息抓下來,包括:
- 文章左邊的圖片url
- 文章的標題article
- 文章的內容簡介summary
- 底部的關鍵字tags
- 右上角的發表時間postime
如下圖:

OK,確定好了我們要抓取的信息后,我們通過瀏覽器調試工具Firebug打開查看該頁面的源碼找到我們關心數據的部分:

這個ul里面的li第一個不是我們想要的數據外,其他的每個li中都保存了一篇文章的信息。選擇其中兩個看看。

下面我們可以編寫解析代碼了。
第一步:新建JavaBean,Article.java
package com.jxust.lt.htmlparse;
/**
* 文章bean
* @author lt
*
*/
public class Article {
private String title; // 標題
private String summary; // 文章內容簡介
private String imageUrl; // 圖片url
private String tags; // 關鍵子
private String postime; // 發表時間
// setter...
// getter...
@Override
public String toString() {
return "Article [title=" + title + ", summary=" + summary
+ ", imageUrl=" + imageUrl + ", tags=" + tags + ", postime="
+ postime + "]";
}
}
第二步:新建一個工具類,HtmlParseUtil.java,寫一個連接網絡並解析返回的html頁面的方法:
/**
* 請求網絡加載數據得到文章的集合
* @param url:網站url
*/
public static List<Article> getArticles(String url){
List<Article> articles = new ArrayList<Article>();
Connection conn = Jsoup.connect(url);
try {
// 10秒超時時間,發起get請求,也可以是post
Document doc = conn.timeout(10000).get();
// 1. 只要我們關心的信息數據,這里使用css類選擇器
Element ul = doc.select(".ulcl").get(0);
// 2. 得到所有的li,排除個別不是同種類型的數據
Elements lis = ul.getElementsByTag("li");
for(int i=1;i<lis.size();i++){ // 通過FileBug發現這個網頁里面第一個li不是我們要的類型,所以從1開始
Article article = new Article();
Element li = lis.get(i);
// 數據1,得到圖片的url,通過img標簽的src屬性獲得
Element img = li.getElementsByTag("img").first();
// 獲取標簽的屬性值,參數為屬性名稱
String imageUrl = img.attr("src");
// 數據2,得到文章的標題
Element h2 = li.getElementsByTag("h2").first();
// 取h2元素下面的第一個a標簽的文本即為標題
String title = h2.getElementsByTag("a").first().text();
// 數據3,得到文章的發表時間,取h2元素下面的第一個span標簽的文本即為文章發表時間
String postime = h2.getElementsByTag("span").first().text();
// 數據4,得到文章內容簡介,取li下面第一個p標簽的文本
String summary = li.getElementsByTag("p").first().text();
// 數據5,得到文章的關鍵字,取li下面的class為tags的第一個元素的所有的a標簽文本
Element tagsSpan = li.getElementsByClass("tags").first();
Elements tags = tagsSpan.getElementsByTag("a");
String key = "";
for(Element tag : tags){
key+=","+tag.text();
}
// 去掉key的第一個","號
key = key.replaceFirst(",", "");
article.setTitle(title);
article.setSummary(summary);
article.setImageUrl(imageUrl);
article.setPostime(postime);
article.setTags(key);
articles.add(article);
}
} catch (Exception ex) {
ex.printStackTrace();
}
return articles;
}
在清單文件下添加請求網絡權限:
<uses-permission android:name="android.permission.INTERNET"/>
說明:請求網絡得到Document對象后(不要導出包,是jsoup下的),通過select()方法帥選了class為ulcl的ul元素,該頁面下只有一個class為ulcl,ul下面第一個li不是我們要的,排除,然后得到每個li對象,每個li元素包含一篇文章的信息,解析重要方法說明:
- Document.select(String cssQuery):通過css選擇器獲取E元素集Elements
- Element.getElementsByTag(String tagName):通過標簽名稱獲取元素Elements
- Element.getElementsByClass(String className):通過標類選擇器獲取元素Elements
- Element.getElementById(String id):通過id獲取元素Element
- Element.attr(String attrName):通過屬性名獲取屬性值
- Element.text():獲取標簽元素的文本
有js的DOM及JQuery編程經驗的人應該很容易理解上面的方法,更多的方法信息使用查看Jsoup官網文檔。
第三步:測試解析結果:
使用android單元測試:
- 在AndroidManifest.xml添加instrumentation
<instrumentation android:targetPackage="com.jxust.lt.htmlparse" android:name="android.test.InstrumentationTestRunner"></instrumentation>
- 在AndroidManifest.xml添加use-library
<uses-library android:name="android.test.runner"/>
新建一個測試類HtmlParseTest.java繼承AndroidTestCase
寫一個測試方法:
public void testParseHtml(){
List<Article> articles = HtmlParseUtil.getArticles(url);
for(int i=0;i<articles.size();i++){
Log.e("result"+i, articles.get(i).toString());
}
}
這里的url的值為:"http://it.ithome.com/"
打開模擬器運行測試方法 Run As Android JUnit Test
日志輸出結果:

...

可以看到我們得到了20條數據,我們來看看其中的一條

可以看到文章標題,內容簡介,圖片url,關鍵字,發表時間5個我們關心的數據全都解析出來了。到這里html解析結束了,現在我們有了數據,那么我們就可以將數據顯示在listView中了(這里不會將數據顯示在ListView中,這個很簡單,一個布局一個適配器就搞定了,不懂的可以問),從而可以自己為網站寫個新聞客戶端了,把要的數據全都抓取下來,體驗一下將別人的數據為我所用的快樂,呵呵。
總結一下:
jsoup解析html的步驟:
- 得到Document對象:
- 通過發送Jsoup的get或者post請求返回Document對象
- 將html字符串轉換成Document對象(通過Jsoup.parse()方法):
-
使用Document.select()進行初步篩選數據
-
使用Element的一系列方法篩選出我們要的數據
注意:要對照頁面源碼解析,解析任何數據之前我們都得先知道要解析數據的結構,看着html頁面的源碼調用Document,Element等對象的相關方法怎么簡單怎么解析。
jsoup的get和post請求網絡在實際運用中使用不多,通常我會將jsoup和Volley,XUtils,Okhttp等著名的android網絡框架結合使用,即請求網絡用Volley等框架,解析用Jsoup,至少我就是這樣做的。
