1. 程式人生 > >android 開發--抓取網頁解析網頁內容的若干方法(網路爬蟲)(正則表示式)

android 開發--抓取網頁解析網頁內容的若干方法(網路爬蟲)(正則表示式)

網頁有兩種格式,一種是xml另一種是html,目前似乎好像大部分都是html格式的,檢視網頁格式的方法是在瀏覽器中右鍵-->檢視原始碼

一,XML解析的三大方法

(1) SAX: Simple API for XML

SAX是一個解析速度快並且佔用記憶體少的XML解析器。SAX解析XML檔案採用的是事件驅動,也就是它並不需要解析完整個文件,
在按內容順序解析文件的過程中,SAX會判斷當前讀到的字元是否符合XML語法中的某部分,如果符合則觸發事件其實就是一些回撥函式,
這些方法定義在ContentHandler

(2)DOM:Document Object Model

DOM解析是將XML檔案全部載入,組成一顆dom樹,然後通過節點以及節點之間的關係來解析XML檔案。
對於特別大的文件,解析和載入整個文件可能很慢且很耗資源。

(3)pull

Pull是Android內建的xml解析器Pull解析器的執行方式與SAX 解析器相似。它提供了類似的事件,如:開始元素和結束元素事件,使用parser.next()可以進入下一個元素並觸發相應事件。事件將作為數值程式碼被髮送,因此可以使用一個switch對感興趣的事件進行處理。當元素開始解析時,呼叫parser.nextText()方法可以獲取下一個Text型別節點的值。

二,HTML解析的三大方法

(1)Jsoup

jsoup 是一款 Java 的HTML 解析器,可直接解析某個URL地址、HTML文字內容。它提供了一套非常省力的API,可通過DOM,CSS以及類似於JQuery的操作方法來取出和操作資料。
這個方法很簡便,但是成功率不大,不推薦。 (2)urlconnection 與httpclient相似,httpclient是增強版的urlconnection

(3)httpclient

HttpClient是一個很方便進行Http連線操作的工具包,用它可以設定代理和模擬瀏覽器下載網頁。而HtmlParser則是一個開源的,可以對HTML進行處理的工具包,可以很方便的對HTML進行解析。

三,HttpClient原始碼講解

MainActivity2.java

package com.example.webdatashow;
 //使用HttpClient元件訪問網路以及獲取 網頁內容的方法。
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;

import android.view.View;
import android.view.View.OnClickListener;

import android.widget.Button;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity2 extends Activity implements OnClickListener{
	ListView listview;
    private String TAG = "webdatashow";
    private Button jiazai;
    private TextView webDataShow;
    private String pediyUrl = "http://www.stdu.edu.cn/";   
	Handler handler; 
    List<Map<String, Object>> data;
     
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);  
        jiazai = (Button)findViewById(R.id.button1);
        webDataShow = (TextView)findViewById(R.id.webDataShow1);
        jiazai.setOnClickListener(this);    
    }
 
    @Override
    public void onClick(View view) {
            handler = getHandler();
            ThreadStart();
    }
    
    /*httpClientWebData()
     * 返回值型別:String
     * 抓取網頁的document
     * 
     * */
    protected String httpClientWebData() {
    	String content = null;
        DefaultHttpClient httpClinet = new DefaultHttpClient(); //建立一個HttpClient 
        HttpGet httpGet = new HttpGet(pediyUrl);//建立一個GET請求 
        ResponseHandler<String> responseHandler = new BasicResponseHandler();         
			try {
				content = httpClinet.execute(httpGet, responseHandler);
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}//傳送GET請求,並響應內容
			return content;   
    }
    
    /*getDate()
     * 返回值型別:List<Map<String, Object>>
     * 提取網頁document的所需內容
     * 
     * */
	private List<Map<String, Object>> getDate() {
		String httpstring = httpClientWebData();
		List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
		Pattern p = Pattern.compile("latestnewsnews  |  </span>\\s*<span style="white-space:pre">	</span><a href=\"(.*?)\" title=\"(.*?)\"");//正則表示式
		Matcher m = p.matcher(httpstring);
		while (m.find()) {
			MatchResult mr=m.toMatchResult();
			Map<String, Object> map = new HashMap<String, Object>();
			map.put("title", mr.group(1));
			map.put("url", mr.group(2));
			result.add(map);
		}
		return result;	
	}
	
	  /*ThreadStart()
     * 開闢新執行緒
     * 
     * */
	private void ThreadStart() {
		new Thread() {
			public void run() {
				Message msg = new Message();
				try {
					data = getDate();
					msg.what = data.size();
				} catch (Exception e) {
					e.printStackTrace();
					msg.what = -1;
				}
				handler.sendMessage(msg);
			}
		}.start();
	}
	private Handler getHandler() {
    	return new Handler(){
			public void handleMessage(Message msg) {
				if (msg.what < 0) {
					Toast.makeText(MainActivity2.this, "資料獲取失敗", Toast.LENGTH_SHORT).show();
				}else {
					initListview();
				}
			}
        };
	}
	
	  /*initListview()
     * 在listview中顯示資料
     * 
     * */
	private void initListview() {
		Toast.makeText(getApplicationContext(), "doing......", Toast.LENGTH_SHORT).show(); 
		ListView listView = (ListView) findViewById(R.id.listView1);	
		listView.setAdapter(new SimpleAdapter(this,data , android.R.layout.simple_list_item_2,
				new String[] { "title","href" }, new int[] {
				android.R.id.text1,android.R.id.text2
		}));

	}

}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
   <TextView
       android:id="@+id/webDataShow1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" 
         android:text="" />
    <Button
        android:id="@+id/button1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="載入" />

    <ListView
        android:id="@+id/listView1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" >
    </ListView>
    
 

</LinearLayout>

manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.webdatashow"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.INTERNET" />
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.webdatashow.MainActivity2"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

最後寫一點關於正則表示式的寫法:

比如提取這一段原始碼中的新聞標題

 </div>
  	      <div class="contentarea">	
		  	
             <div id="news">
              			    <div id="news_1">
                		<div class="moduletablenews">
					<h3>學校新聞</h3>
					
<div class="newnotice"><div>今日共<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33.html"><font color="red">0</font></a>資訊更新</div></div><ul class="feednews-ul">
	<li class="latestnewsnews">
	<span id="date">09-10  |  </span>
	<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33/21883-2015-09-10-03-51-32.html" title="我校召開第三十一個教師節慶祝表彰大會" target="_blank">
			我校召開第三十一個教師節慶祝表彰大會    
	</a></li>
	<li class="latestnewsnews">
	<span id="date">09-09  |  </span>
	<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33/21884-2015-09-10-07-09-14.html" title="重傳承、勇創新:四方學院圓滿完成迎新工作" target="_blank">
			重傳承、勇創新:四方學院圓滿完成迎...    
	</a></li>
	<li class="latestnewsnews">
	<span id="date">09-07  |  </span>
	<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33/21844-2015-09-07-10-10-04.html" title="交通學院:同憶抗戰史,共築中國夢" target="_blank">
			交通學院:同憶抗戰史,共築中國夢    
	</a></li>
	<li class="latestnewsnews">
	<span id="date">09-06  |  </span>
	<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33/21834-2015-09-06-08-16-17.html" title="歐洲科學院院士張傳增教授來校做學術報告" target="_blank">
			歐洲科學院院士張傳增教授來校做學術報告    
	</a></li>
共同的格式是這樣的:
<li class="latestnewsnews">
	<span id="date">09-10  |  </span>
	<a href="http://xcb.stdu.edu.cn/2009-05-05-02-26-33/21883-2015-09-10-03-51-32.html" title="我校召開第三十一個教師節慶祝表彰大會" target="_blank">
			我校召開第三十一個教師節慶祝表彰大會    
	</a></li>
用(.*?)代替所需要的內容,於是在RegexTester.exe中輸入下式能通過。(\2表示與第二個所需內容重複)
class="latestnewsnews">
	<span id="date">09-10  |  </span>
	<a href="(.*?)" title="(.*?)" target="_blank">
			\2 
繼續精簡去掉無用的:通過
latestnewsnews	<span>  |  </span>
	<a href=\"(.*?)\" title=\"(.*?)\" target=\"_blank\">
			\2 
再多試試去掉其它的雜草:
latestnewsnews	  |  </span>
	<a href="(.*?)" title="(.*?)"
最後用\\s代替換行,並在每個"前加\
latestnewsnews  |  </span>\\s*<span style="white-space:pre">	</span><a href=\"(.*?)\" title=\"(.*?)\"
這就是最好的形態,

說明一下,不好的正則表示式會嚴重地影響匹配時間從而影響你app的執行速度,要反覆研究出最好的正則表示式以便讓你的app更流暢。