------------------------------2019/01/07 Update !! ---------------------------------------
目前台銀網站有更改html的格式所以以下棄用
幣別 td class = "titleleft"
匯率 td class = "decimal
更新時間 td style ="width:326px;text-align:left;vertical-align:top;color:#0000FF;font-size:11pt;font-weight:bold;"
這邊要做修正哦 !!!!!!
--------------------------------------------------------------------------------------------------------------
這次利用jsoup 這個lib,來練習如何擷取網頁上的資訊
我們以台灣銀行的匯率做一個簡單的範例
一、
先將jsoup.jar下載下來並加入android studio中
http://jsoup.org/download
二、
開始分析我們要擷取的網頁,我們主要要抓的是匯率,所以從台銀的網頁,開啟開發者模式
台銀的網頁:
https://rate.bot.com.tw/xrt?Lang=zh-TW (新網址)
http://rate.bot.com.tw/Pages/Static/UIP003.zh-TW.htm (舊網址)
以下是我們要抓的資料
- 幣別
- 即期匯率 買入 賣出
- 現今匯率 買入 賣出
- 更新時間
從網頁原始碼上可以發現
幣別 td class = "titleleft"
匯率 td class = "decimal
更新時間 td style ="width:326px;text-align:left;vertical-align:top;color:#0000FF;font-size:11pt;font-weight:bold;"
三、
既然知道資料在哪,那麼就可以開始利用jsoup來擷取網頁的資訊
那麼我們可以利用 select這個方法去抓取資料,
以下為一個簡單範例
private static final String url ="http://rate.bot.com.tw/Pages/Static/UIP003.zh-TW.htm";
public void getInfo(){
try {
Document doc = Jsoup.connect(url).get();
doc.select("td.titleleft").text();
}catch(Exception e){
}
}
※注意:
在OnCreate裡不能直接呼叫Jsoup.connect(url).get(),因為在4.0版之後為了避免連線時間過長導致App ANR,所以要利用Thead去執行Jsoup.connect(url).get()這個動作。
※記得:要加入網路連線的權限哦!!!!!!
jsoup的抓取範本:
http://jsoup.org/cookbook/extracting-data/selector-syntax
研究一下發現我們要抓取的資料格就分為
doc.select("td.titleleft")
doc,select("td.decimal")
doc.select("td[style=width:326px;text-align:left;vertical-align:top;color:#0000FF;font-size:11pt;font-weight:bold;]")
我們就能夠利用ListView把我們需要的資料裝進去
四、
那我們就開始把擷取出來的資料建立起來
主要的架構分為
- RateItem 建立一個物件存入我們的資料
- MainActiviy 執行擷取資料的部分及顯示
- ListViewAdpater 自訂ListView畫面
- listview_custom.xml ListView的layout
- listview.header.xml ListView的header
- activtiy_main.xml
以下為程式碼:
用String去裝匯率資料是比較偷懶的方式,如果有要計算等等,還是建議用double等資料型態去裝
public class RateItem {
private String Currency;
private String CashBuyRate;
private String CashSoldRate;
private String SpotBuyRate;
private String SpotSoldRate;
public RateItem(String Currency,String CashBuyRate,String CashSoldRate,String SpotBuyRate,String SpotSoldRate){
this.Currency = Currency;
this.CashBuyRate = CashBuyRate;
this.CashSoldRate = CashSoldRate;
this.SpotBuyRate = SpotBuyRate;
this.SpotSoldRate = SpotSoldRate;
}
public RateItem(){
this.Currency = "";
this.CashBuyRate = "";
this.CashSoldRate = "";
this.SpotBuyRate = "";
this.SpotSoldRate = "";
}
public String getSpotSoldRate() {
return SpotSoldRate;
}
public void setSpotSoldRate(String spotSoldRate) {
SpotSoldRate = spotSoldRate;
}
public String getSpotBuyRate() {
return SpotBuyRate;
}
public void setSpotBuyRate(String spotBuyRate) {
SpotBuyRate = spotBuyRate;
}
public String getCashSoldRate() {
return CashSoldRate;
}
public void setCashSoldRate(String cashSoldRate) {
CashSoldRate = cashSoldRate;
}
public String getCashBuyRate() {
return CashBuyRate;
}
public void setCashBuyRate(String cashBuyRate) {
CashBuyRate = cashBuyRate;
}
public String getCurrency() {
return Currency;
}
public void setCurrency(String currency) {
Currency = currency;
}
}
這段為擷取資料並放入容器裡,記得要用Thead去處理,不然會跳出錯誤。
◎小小的提醒引用Thead的時,要選對import android.os.Handler,不要選到java的
public class MainActivity extends AppCompatActivity {
private ListView mListView;
private Context mContext;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this,"onCreate0",Toast.LENGTH_SHORT).show();
mListView = (ListView)findViewById(R.id.listView);
//建立Thread
new Thread(runnable).start();
}
private static final String url ="http://rate.bot.com.tw/Pages/Static/UIP003.zh-TW.htm";
private String UpdateTime;
private List RateList;
Runnable runnable = new Runnable(){
@Override
public void run() {
try {
Document doc = Jsoup.connect(url).get();
RateList = new ArrayList();
int i = 0;
for(Element title : doc.select("td.titleLeft")){
RateItem mRateItem = new RateItem();
//取得幣別並存入
mRateItem.setCurrency(title.text());
//匯率為一次四筆所以一次抓出並存入
if (i < doc.select("td.decimal").size()){
//利用eq()可以指定為第幾筆資料
mRateItem.setCashBuyRate(doc.select("td.decimal").eq(i).text());
mRateItem.setCashSoldRate(doc.select("td.decimal").eq(i+1).text());
mRateItem.setSpotBuyRate(doc.select("td.decimal").eq(i+2).text());
mRateItem.setSpotSoldRate(doc.select("td.decimal").eq(i+3).text());
i+=4;
}
RateList.add(mRateItem);
}
//更新時間的抓取
String Temp = doc.select("td[style=width:326px;text-align:left;vertical-align:top;color:#0000FF;font-size:11pt;font-weight:bold;]").text();
//去處裡字串
UpdateTime = Temp.substring(12);
} catch (IOException e) {
e.printStackTrace();
}
//利用handler去更新View
handler.sendEmptyMessage(0);
}
};
@SuppressLint("HandlerLeak")
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
setListViewAdapter();
}
};
private TextView UpdateTimeText;
public void setListViewAdapter(){
LayoutInflater inflater = LayoutInflater.from(this);
//include的TextView直接find就能找到了不需要透過inflater建立View
UpdateTimeText = (TextView)findViewById(R.id.UpdateTimeHeader);
UpdateTimeText.setText("更新時間:"+ UpdateTime);
mListView.setAdapter(new ListViewAdapter(this,RateList));
//原本是直接加入header但為了固定位置,所以直接在xml中include header
//View header = inflater.inflate(R.layout.listview_header, mListView, false);
//mListView.addHeaderView(header, null, false);
}
這裡就是我們自訂的ListView樣式並將資料放入,這次使用Viewhodler來增加執行的效率
public class ListViewAdapter extends BaseAdapter {
private LayoutInflater inflater;
private Context mContext;
private List RateLists;
public ListViewAdapter(Context mContext,List RateList){
inflater = LayoutInflater.from(mContext);
this.mContext = mContext;
this.RateLists = RateList;
}
@Override
public int getCount() {
return RateLists.size();
}
@Override
public Object getItem(int i) {
return RateLists.get(i);
}
private static class ViewHolder{
TextView CurrencyText;
TextView CashBuyText;
TextView CashSoldText;
TextView SpotBuyText;
TextView SpotSoldText;
public ViewHolder(TextView CurrencyText, TextView CashBuyText , TextView CashSoldText,TextView SpotBuyText , TextView SpotSoldText ){
this.CurrencyText = CurrencyText;
this.CashBuyText = CashBuyText;
this.CashSoldText = CashSoldText;
this.SpotBuyText = SpotBuyText;
this.SpotSoldText = SpotSoldText;
}
}
@Override
public long getItemId(int i) {
return RateLists.indexOf(i);
}
@Override
public View getView(int position, View ConvertView, ViewGroup viewGroup) {
ViewHolder holder = null;
if(ConvertView == null){
ConvertView = inflater.inflate(R.layout.listview_custom,viewGroup,false);
holder = new ViewHolder(
(TextView) ConvertView.findViewById(R.id.CurrencyTextView),
(TextView) ConvertView.findViewById(R.id.CashBuyTextView),
(TextView) ConvertView.findViewById(R.id.CashSoldTextView),
(TextView) ConvertView.findViewById(R.id.SpotBuyTextView),
(TextView) ConvertView.findViewById(R.id.SpotSoldTextView)
);
//保存狀態避免一直重新建立還要find id
ConvertView.setTag(holder);
}else{
//取出狀態
holder = (ViewHolder) ConvertView.getTag();
}
RateItem mRateItem = (RateItem)getItem(position);
holder.CurrencyText.setText(mRateItem.getCurrency());
holder.CashBuyText.setText(mRateItem.getCashBuyRate());
holder.CashSoldText.setText(mRateItem.getCashSoldRate());
holder.SpotBuyText.setText(mRateItem.getSpotBuyRate());
holder.SpotSoldText.setText(mRateItem.getSpotSoldRate());
return ConvertView;
}
}
※還沒找到一個簡單的方案可以放XML所以都用-取代,抱歉= =!!!!
-?xml version="1.0" encoding="utf-8"?-
-TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:stretchColumns="*"
android:layout_width="match_parent"
android:layout_height="match_parent"-
-TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"-
-TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Text"
android:padding="3dp"
android:textSize="12sp"
android:gravity="left"
android:id="@+id/CurrencyTextView" -
-TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Text"
android:padding="3dp"
android:gravity="center"
android:id="@+id/CashBuyTextView" -
-TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Text"
android:padding="3dp"
android:gravity="center"
android:id="@+id/CashSoldTextView" -
-TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Text"
android:padding="3dp"
android:gravity="center"
android:id="@+id/SpotBuyTextView" -
-TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="Text"
android:padding="3dp"
android:gravity="center"
android:id="@+id/SpotSoldTextView" -
-/TableRow-
-/TableLayout-
-?xml version="1.0" encoding="utf-8"?-
-TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:stretchColumns="*"-
-LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"-
-TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="更新時間:"
android:gravity="center"
android:padding="3dp"
android:id="@+id/UpdateTimeHeader" /-
-/LinearLayout-
-TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="3dp"
android:padding="5dp"-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text=""
android:padding="3dp"
android:id="@+id/textView" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="現金匯率"
android:id="@+id/CashRateHeader"
android:layout_span="2"
android:layout_gravity="center" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="即期匯率"
android:layout_span="2"
android:layout_gravity="center"
android:id="@+id/SpotRateHeader" /-
-/TableRow-
-TableRow
android:layout_width="match_parent"
android:layout_height="match_parent"-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="幣別"
android:layout_gravity="center"
android:id="@+id/CurrencyHeader" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="買入"
android:layout_gravity="center"
android:id="@+id/CashBuyHeader" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="賣出"
android:layout_gravity="center"
android:id="@+id/CashSoldHeader" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="買入"
android:layout_gravity="center"
android:id="@+id/SpotBuyHeader" /-
-TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="賣出"
android:layout_gravity="center"
android:id="@+id/SpotSoldHeader" /-
-/TableRow-
-/TableLayout-
※這部分最重要就只有固定header所使用的include
-LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity"-
-include layout="@layout/listview_header"/-
-ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/listView"
/-
-/LinearLayout-
最後完成的樣子
參考連結:
jsoup的範例
http://swind.code-life.info/posts/jsoup.html
http://zhaohaiyang.blog.51cto.com/2056753/735346
透過jsoup解析、抓取網頁上的資料,顯示在APP中
http://laeudora.com/iwebinfo/?p=4742
Jsoup Parser HTML Examples
http://pclevin.blogspot.tw/2015/03/jsoup-parser-html-examples.html