1. 程式人生 > >使用Jsoup非同步抓取新聞資料裝載listview(仿開源中國資訊列表)

使用Jsoup非同步抓取新聞資料裝載listview(仿開源中國資訊列表)

最近想寫一下開源中國的客戶端,但是不想自己造資料,才發現有jsoup這麼好用的東西。使用jsoup,你在網站上能看到的任何東西都可以解析出來。jsoup是一個解析網頁原始碼的開源庫,他能按照給定的規則提取出一個網頁中的任意元素,和其他網頁解析庫不同的是,他提取網頁內容的方式和css、jquery的選擇器非常相似。

我們看一下網頁中的資訊和最終的實現效果(網頁截圖比手機截圖晚了兩個小時湊合看吧 0.0):

jsoup規則其實並不難:

開啟開源中國新聞資訊的網頁,右鍵檢視原始碼,找到資訊相關的<div>。

<div class="panel" id='RecentNewsList'>
		<h3 class='tabs'>
		<ul>
			<li><a href="/news/list" class='active'>全部資訊</a></li>
			<li><a href="/news/list?show=industry">綜合資訊</a></li>
			<li><a href="/news/list?show=project">軟體更新資訊</a></li>
		</ul>
		</h3>
		<ul class='List'>
								<li>
												<h2><a href="/news/72054/mybatis-spring-boot-1-0-2" target="_blank">Mybatis Spring Boot 1.0.2 釋出</a></h2>
				<p class='date'><a href="http://my.oschina.net/u/2305107">淡漠悠然</a> 釋出於 1小時前 - 3評</p>				
				<p class='detail'>Mybatis Spring Boot 1.0.2 釋出了,Mybatis Spring Boot 是 MyBatis 和 Spring Boot 的整合。 暫無相關改進說明,檢視專案提交記錄,可點選這裡。 下載: Source code (zip) Source code (tar.gz)...</p>				
            	        						<p class='more'><a href="/news/72054/mybatis-spring-boot-1-0-2" target="_blank" class='more'>顯示全文</a></p>
			</li>
						
								<li>
												<a href="/news/72053/windows-10" target="_blank" class='img'><img src="/img/logo/windows.gif?t=1451964198000" border='0'/></a>
								<h2><a href="/news/72053/windows-10" target="_blank">Win10 年度最重大更新:程式碼、理想與愛</a></h2>
				<p class='date'><a href="http://my.oschina.net/osadmin">oschina</a> 釋出於 5小時前 - 29評</p>				
				<p class='detail'>微軟 Build 2016 大會,讓圍繞 Windows 10 的軟體開發變得格外矚目。 在這次,微軟依舊在開發者大會上帶來了 Windows 10、平臺軟體開發、HoloLens、人工智慧方面的技術進展,當然最讓普通消費者關心的仍然是 Wind...</p>				
            	        		        		<p class='newsImg'><a title="Win10 年度最重大更新:程式碼、理想與愛" href="/news/72053/windows-10" target="_blank"><img alt="...." src="http://static.oschina.net/uploads/space/2016/0331/115526_bt3I_1774694.png"></a></p>
								<p class='more'><a href="/news/72053/windows-10" target="_blank" class='more'>顯示全文</a></p>
			</li><span style="font-family: 'lucida grande', 'lucida sans unicode', lucida, helvetica, 'Hiragino Sans GB', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif; line-height: 23.8px;">							</span>

我們會發現所有的資訊都在id為RecentNewsList的div中,jsoup提供了getElementById()方法:

Document doc = Jsoup.connect(path).timeout(5000).get(); 
Element masthead = doc.getElementById("RecentNewsList");
這時masthead中就包含了d為RecentNewsList的div中所有的資訊。

我們再仔細觀察發現,資訊都是在class為List的ul中,並且以<li></li>為一組,jsoup提供了select()方法,條件遞進,用空格分開:

Elements articleElements =  masthead.select("ul.List li");

ul.List表示class為List的ul,articleElements中包含了所有以<li></li>為一組資訊資訊,每一組資訊如果獲取標題,詳情和發表資訊可用以下程式碼:

Elements titleElement = articleElement.select("h2 a");
Elements summaryElement = articleElement.select("p.detail");
Elements timeElement = articleElement.select("p.date");
此時titleElement,summaryElement,timeElement分別裝著標題,詳情和發表資訊。

以articleElements中的資料作為資料來源的話,我們要以articleElements的長度做個迴圈,逐個取出以上三個資訊賦值給一個Article物件,並將每一個Article物件放到定義的ArrayList物件中中。此時將ArrayList物件作為資料來源設定ListView介面卡。


下面我們看怎麼一步步實現的:

1、首先新建android工程,下載jsoup的jar包匯入工程,由於ADT升級到版本20以後無法載入這個包,用builtpath的方式可能會報錯 java.lang.noclassdeffounderror:org/jsoup/Jsoup,如果報錯,先把jar包remove掉,然後直接將jsoup.jar拷到libs資料夾下,clean一下工程就好了。

2、新建Article實體類,作為ListView介面卡的適配型別

package com.example.osnews;

public class Article {
	private String title="";
	private String summary="";
	private String postTime="";
	
	public Article(String title,String summary,String postTime){
		this.title=title;
		this.summary=summary;
		this.postTime=postTime;
	}
	
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public String getSummary() {
		return summary;
	}
	public void setSummary(String summary) {
		this.summary = summary;
	}
	public String getPostTime() {
		return postTime;
	}
	public void setPostTime(String postTime) {
		this.postTime = postTime;
	}
	
	
}

3、新建ListView的介面卡ListAdapter
package com.example.osnews;

import java.util.ArrayList;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class ListAdapter extends BaseAdapter {

	private ArrayList<Article> mArticleList;
	private int resourceId;
	private Context ctx;
	
	public ListAdapter(Context context, int textViewResourceId, ArrayList<Article> objects) {
		resourceId = textViewResourceId;
		this.mArticleList = objects;
		this.ctx = context;
	}
	@Override
	public int getCount() {
		
		return mArticleList.size();
	}

	@Override
	public Article getItem(int position) {
		return mArticleList.get(position);
	}

	@Override
	public long getItemId(int position) {
		
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		
		Article article = getItem(position);
		View view;
		ViewHolder viewHolder;
		
		if (convertView == null) {
			view= LayoutInflater.from(ctx).inflate(resourceId, null);
			
			viewHolder = new ViewHolder();			
 			viewHolder.title = (TextView) view.findViewById(R.id.title);
			viewHolder.summary = (TextView) view.findViewById(R.id.summary);
			viewHolder.postTime = (TextView) view.findViewById(R.id.postTime);
			view.setTag(viewHolder);
		} else {
			view=convertView;
        	viewHolder = (ViewHolder) view.getTag();
		}
		
		viewHolder.title.setText(article.getTitle());
		viewHolder.summary.setText(article.getSummary());
		viewHolder.postTime.setText(article.getPostTime());
			
		return view;
	}

	static class ViewHolder {
		public TextView title;
		public TextView summary;
		public TextView postTime;
	}
}

4、佈局檔案,就上面一個標題和下面一個listView,自己在下載原始碼檢視。

5、MainActivity.java

package com.example.osnews;


import java.io.IOException;
import java.util.ArrayList;

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

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.Window;
import android.widget.ListView;

public class MainActivity extends Activity {
	
	private ListView listview; 
	private String path = "http://www.oschina.net/news";
	private ListAdapter adapter;
	
	@SuppressLint("HandlerLeak")
	private Handler handler = new Handler(){
		public void handleMessage(Message msg){
			switch(msg.what){
			case 1 :
				listview.setAdapter(adapter);
				break; 
			default:
				break;
			} 
		} 
	}; 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		setContentView(R.layout.activity_main);
		listview = (ListView) this.findViewById(R.id.listview);
		
		new GetListData().execute(path);
	}
	
	class GetListData extends AsyncTask<String, Void, ArrayList<Article>> {
		@Override 
		protected ArrayList<Article> doInBackground(String... arg0) {
			
			ArrayList<Article> articleList =new ArrayList<Article>();
			try {
				Document doc = Jsoup.connect(path).timeout(5000).get(); 
				Element masthead = doc.getElementById("RecentNewsList");
			    Elements articleElements =  masthead.select("ul.List li");
			    
				if (doc != null) { 
					for(int i = 0; i < articleElements.size(); i++) {
					    
					    Element articleElement = articleElements.get(i);
					    
					    Elements titleElement = articleElement.select("h2 a");
					    Elements summaryElement = articleElement.select("p.detail");
					    Elements timeElement = articleElement.select("p.date");
					    
					    String title = titleElement.text();
					    String summary = summaryElement.text();
					    //if(summary.length() > 70)
					    //	summary = summary.substring(0, 70);
					    String postTime = timeElement.text();
					    
					    Log.i("title", title);
					    Log.i("summary", summary); 
					    Log.i("postTime", postTime);
					    
					    Article article = new Article(title,summary,postTime);
					    articleList.add(article);
					 
					}
				} 
			} catch (IOException e) {
				
				e.printStackTrace(); 
				
			}return articleList; 
		} 
		@Override 
		protected void onPostExecute(ArrayList<Article> articleList) { 
		 
			super.onPostExecute(articleList); 
			adapter = new ListAdapter(MainActivity.this,R.layout.item_article_list,articleList); 
			Log.i("adapter", "----------"+adapter.isEmpty()); 
			Message msg = Message.obtain(); 
			msg.what=1; 
			handler.sendMessage(msg);
		} 
	} 

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		
		int id = item.getItemId();
		if (id == R.id.action_settings) {
			return true;
		}
		return super.onOptionsItemSelected(item);
	}
}
6.AndroidManifest.xml中新增的上網許可權
<uses-permission android:name="android.permission.INTERNET"></uses-permission>

原始碼下載:http://download.csdn.net/detail/chunxiao123ouc/9478282