------------------------------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 (新網址)
以下是我們要抓的資料
- 幣別
- 即期匯率 買入 賣出
- 現今匯率 買入 賣出
- 更新時間
幣別 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
以下為程式碼:
- RateItem
用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;
}
}
- MainActiviy
這段為擷取資料並放入容器裡,記得要用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 ListRateList; Runnable runnable = new Runnable(){ @Override public void run() { try { Document doc = Jsoup.connect(url).get(); RateList = new ArrayList //去處裡字串 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); }(); 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();
- ListAdpater
這裡就是我們自訂的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;
}
}
- listview_custom.xml
※還沒找到一個簡單的方案可以放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-
- listview.header.xml
-?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-
- activity_main.xml
※這部分最重要就只有固定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 Exampleshttp://pclevin.blogspot.tw/2015/03/jsoup-parser-html-examples.html


要抓取油價 https://www.cpc.com.tw/
回覆刪除一直無法連結抓取 但是其他網站沒問題
有甚麼可能遺漏的嗎?
hi 大星
刪除我剛看了一下 發現因為中油的油價的產生 並非"靜態"哦
他是透過ajax 直接動態產生 所以你必需去找到他真正的網址
我先口述跟你分享
1.到中油網站用f12檢查 會找到div id= t_sPrice1 油價在這顯示
2.所以我們去搜尋t_sPrice1 你會發現 在下方有一個ajax 的 function
3.我們就是要從這個function看出實際的url是哪一個 可以發現有一個
https://www.cpc.com.tw/GetOilPriceJson.aspx?type=TodayOilPriceString
所以你要透過這個連結 才能實際抓取到你要的資料
這邊再給你一個連結分享
https://kknews.cc/zh-tw/code/gmbq6ve.html
希望你能順利完成 :)