------------------------------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
希望你能順利完成 :)