Android網絡爬蟲程序(基於Jsoup)

摘要:基於 Jsoup 實現一個 Android 的網絡爬蟲程序,抓取網頁的內容並顯示出來。寫這個程序的主要目的是抓取海投網的宣講會信息(公司、時間、地點)並在移動端顯示,這樣就能夠隨時隨地的瀏覽在學校舉辦的宣講會信息了。html


1、Jsoup簡介

Jsoup 是一個 Java 的開源HTML解析器,可直接解析某個URL地址、HTML文本內容。它提供了一套很是方便的API,可經過DOM,CSS以及相似於jQuery的操做方法來取出和操做數據。java

Jsoup主要有如下功能:node

  • 從一個URL,文件或字符串中解析HTML;android

  • 使用DOM或CSS選擇器來查找、取出數據;git

  • 對HTML元素、屬性、文本進行操做;github

  • 清除不受信任的HTML (來防止XSS攻擊)瀏覽器

好了,下面寫幾段代碼來講明 Jsoup 是如何優雅的進行 HTML 文檔處理的。首先,咱們須要去Jsoup官網 下載Jsoup的jar包,而後加入項目的依賴庫中。網絡

1) HTML解析

Jsoup 能夠從一個字符串、文件或者一個 URL 中解析HTML,解析的目的主要是爲了獲得一個乾淨完整的解析結果,並生成 Document 對象實例。多線程

// Parse a document from a String
String html = "<html><head><title>神奕的博客</title></head>"
        +"<body><p>搭個博客寫學習筆記!!</p></body></html>";
Document doc = Jsoup.parse(html);

// Load a Document from a File
File input = new File("D://a.html");
Document doc = Jsoup.parse(input, "UTF-8");

// Load a Document from a URL
Document doc = Jsoup.connect("http://example.com/").get();

當加載和解析一個本地的HTML文件時,若是在加載文件的時候發生錯誤,將拋出 IOException,應做適當處理。dom

2) 數據提取

將HTML解析成一個Document以後,就可使用傳統的 DOM 方法進行數據抽取。例如:

// 海投網
String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/";
Document doc = Jsoup.connect(url).get();

Elements elements = doc.getElementsByTag("company");
for(Element e : elements) {
    System.out.println(e.text());
}

Document 對象和 Elements 對象提供了一系列相似於DOM的方法來查找元素,好比 getElementById(String id)、getElementsByTag(String tag) 等等。更多方法請看《Jsoup Cookbook》。

另外,還可使用 Selector 選擇器(相似於CSS或jQuery語法)來查找元素。以下:

// 海投網
String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/";
Document doc = Jsoup.connect(url).get();

// 經過標籤company查找元素
Elements company = doc.select("company");
// 帶有href屬性的a元素
Elements links = doc.select("a[href]");
// 擴展名爲.png的圖片
Elements pngs = doc.select("img[src$=.png]");
// class等於content的div標籤
Element content = doc.select("div.content").first();

選擇器實現了很是強大和靈活的查找功能。select方法在Document、Element 或 Elements 對象中均可以使用,且是上下文相關的,所以可實現指定元素的過濾或者鏈式選擇訪問。select方法將返回一個Elements集合,並提供一組方法來抽取和處理結果。

經過 DOM 方法或者 Selector 方法查找到一些 Elements 元素以後,咱們須要從這些元素中取得數據,下面是幾個經常使用的方法:

  • 取得一個屬性的值,可使用Node.attr(String key)方法;

  • 取得一個元素中的文本,可使用Element.text()方法;

  • 取得元素或屬性中的HTML內容,可用Element.html()Node.outerHtml()方法

  • 取得一個元素的 id :Element.id()

  • 取得一個元素的標籤名:Element.tagName()

  • 取得一個元素的類名:Element.className()

3) 數據修改

在解析一個 Document 以後可能想修改其中的某些屬性值、HTML或文本內容,而後再保存到磁盤或都輸出到前臺頁面。例如:咱們能夠爲文檔中的全部圖片增長可點擊連接、修改連接地址或者是修改文本等。Jsoup 提供了不少方法用來進行修改,這裏就不列舉了,請移步 Jsoup Cookbook


2、海投網的頁面抓取

海投網是一個爲高校畢業生服務的招聘信息網,創始人是華中科技大學的畢業生。如今我要抓取在華中科技大學舉辦的宣講會的信息,網頁以下圖:



查看網頁源代碼,以下圖:


能夠看出,公司名是在一個 company 標籤內,宣講會時間是在一個類名爲 text-center 的 td 標籤內,學校的具體地點則是在一個類名爲 preach-tbody-addre 的 td 標籤內。這麼一分析,要提取華中科技大學的宣講會信息就變得挺簡單了。

Java代碼以下:

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;

public class Main {
    public static void main(String[] args) throws IOException {
        String url = "http://xjh.haitou.cc/wh/uni-1/after/hold/page-1/";
        Connection conn = Jsoup.connect(url);
        // 修改http包中的header,假裝成瀏覽器進行抓取
        conn.header("User-Agent", "Mozilla/5.0 (X11; Linux x86_64; rv:32.0) Gecko/    20100101 Firefox/32.0");
        Document doc = conn.get();
        
        // 獲取tbody元素下的全部tr元素
        Elements elements = doc.select("tbody tr");
        for(Element element : elements) {
        	String companyName = element.getElementsByTag("company").text();
        	String time = element.select("td.text-center").first().text();
        	String address = element.getElementsByClass("preach-tbody-addre").text();
        	
        	System.out.println("公司:"+companyName);
        	System.out.println("宣講時間:"+time);
        	System.out.println("宣講學校:華中科技大學");
        	System.out.println("具體地點:"+address);
        	System.out.println("---------------------------------");
        }
    }
}

某些網站禁止爬蟲,不能抓取或者抓取必定數量後封IP。這時候咱們須要假裝成瀏覽器進行抓取,這能夠經過修改http包中的header來實現(設置User-Agent)。運行上面的程序獲得輸出結果:

公司:瑞聲科技(常州)有限公司
宣講時間:2015-03-07 19:00(週六)
宣講學校:華中科技大學
具體地點:大學生活動中心305階梯教室
---------------------------------
公司:普聯技術有限公司
宣講時間:2015-03-08 19:00(週日)
宣講學校:華中科技大學
具體地點:大學生活動中心305階梯教室
---------------------------------
公司:大聯大投資控股股份有限公司
宣講時間:2015-03-09 09:30(週一)
宣講學校:華中科技大學
具體地點:大學生活動中心305階梯教室
---------------------------------
......


3、應用到Android程序中

開發 Android 程序,你須要搭建開發環境,很簡單:先安裝Java的JDK(最好不低於1.6),而後去Android官網下載並安裝 Android Studio 就好了。

在Android程序中使用 Jsoup 須要注意兩點:

  • 在AndroidManifest.xml文件中添加網絡訪問權限android.permission.INTERNET

  • Android在4.0以後,不容許在主線程裏執行網絡(http)請求,也就是說 Jsoup 的代碼須要寫在子線程裏。

1) 多線程

4.0 版本之後,若是你在主線程裏嘗試進行網絡操做,會報android.os.NetworkOnMainThreadException 的異常。因此咱們須要開闢子線程進行異步加載,用到ThreadRunnableHandler這三個類:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.setContentView(R.layout.share_mblog_view);
    // 開闢一個線程
    new Thread(runnable).start();
}

Runnable runnable = new Runnable(){
    @Override
    public void run() {
    	/**
         * 要執行的操做
         */
    	// 執行完畢後給handler發送一個空消息
        handler.sendEmptyMessage(0);
    }
}

Handler handler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        /**
         * 處理UI
         */
    	// 當收到消息時就會執行這個方法
    }
}

2) 判斷網絡鏈接是否可用

若是在沒有可用網絡的狀況下執行網絡爬蟲程序,App將會報錯。因此在每次執行以前都應該先判斷網絡是否可用。大體步驟以下:

① 獲取ConnectivityManager對象

Context context = activity.getApplicationContext();
// 獲取手機全部鏈接管理對象(包括對wi-fi,net等鏈接的管理)
ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
②  獲取NetworkInfo對象

NetworkInfo info = cm.getActiveNetworkInfo();
③  判斷網絡類型,Android的網絡分爲兩大類:WIFI 和 手機網絡

// WIFI 斷定條件
info != null && info.getType() == ConnectivityManager.TYPE_WIFI
// 手機網絡 斷定條件
info !=null && info.getType() ==  ConnectivityManager.TYPE_MOBILE

而手機網絡具體又分爲不少類,好比移動3G、移動2G、聯通2G等等。這裏就不說了,自行Google。

④ 判斷網絡鏈接是否可用(包括全部網絡類型)

public boolean isNetworkAvailable(Activity activity)
{
    Context context = activity.getApplicationContext();
    ConnectivityManager cm = (ConnectivityManager) 
    		context.getSystemService(Context.CONNECTIVITY_SERVICE);

    if (cm == null)
        return false;
    else
    {   // 獲取全部NetworkInfo對象
        NetworkInfo[] networkInfo = cm.getAllNetworkInfo();
        if (networkInfo != null && networkInfo.length > 0)
        {
            for (int i = 0; i < networkInfo.length; i++)
                if (networkInfo[i].getState() == NetworkInfo.State.CONNECTED)
                    return true;  // 存在可用的網絡鏈接
        }
    }
    return false;
}
注意,上述操做須要在 AndroidManifest.xml 文件中添加訪問網絡狀態的權限:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


另外,本程序在 UI 界面開發上涉及到 Android 中的 ListView(顯示)、PopupWindow(菜單)、ProgressDialog(加載)、AlertDialog(提示)等控件的使用。由於本文並非討論 Android 控件的使用,在這裏就不贅述了。

源碼下載:https://github.com/SongLee24/android-crawler




我的站點:http://songlee24.github.com
相關文章
相關標籤/搜索