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的操作方法來取出和操作資料。(3)httpclient
HttpClient是一個很方便進行Http連線操作的工具包,用它可以設定代理和模擬瀏覽器下載網頁。而HtmlParser則是一個開源的,可以對HTML進行處理的工具包,可以很方便的對HTML進行解析。
三,HttpClient原始碼講解
MainActivity2.java
main.xmlpackage 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 })); } }
<?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更流暢。