編輯:關於Android編程
大家好,我是M1ko。在互聯網時代的今天,如果一個App不接入互聯網,那麼這個App一定不會有長時間的生命周期,因此Android網絡編程是每一個Android開發者必備的技能,博主是在校大學生,自學Android一年半多,正好通過一個模擬登錄校園網軟件,來給大家演示如何在網頁上抓取我們想要的數據,以及將數據Post給服務器。如果有什麼錯誤或改進歡迎大家指正=-= ,如果想交流博主qq 136057505
好的廢話不多說看一下我們的重點
Httpwatch等軟件抓取Post請求 如何獲取驗證碼使用Jsoup解析數據
Ok首先上項目Github網址:https://github.com/MikoGodZd/LoginNwuWeb1.git
下面是軟件的截圖大家心裡能有項目的大體框架
有Http基礎的朋友都知道,我們是通過Get 與Post請求與服務器進行交互的,Get顧名思義就是獲取信息,Post就是想服務器發請求,但是Post也可以用來獲取信息並且比Get有很多優勢,我們這裡就是使用的Post。Java中有很多方式與服務器進行連接,常見的有HttpUrlCollection,HttpClient。兩者的優缺點:
HttpUrlCollection優點:
- 輕量,拓展性強
- 節省資源
缺點:
- 代碼量大復雜
HttpClient優點:
- 便捷,代碼簡單
缺點:
-拓展性不足,耗費資源多
實際上在Android6.0中Google已經刪除了HttpClient的API官方理由是耗電量大,但是HttpCollection的代碼量實在是大,因此我們還是使用了HttpClient,只需在build.gradle中添加以下語句
android {
即可。
useLibrary 'org.apache.http.legacy'
}
我們要向服務器Post數據要知道Post的地址是誰,這就要用我們的抓包軟件了,博主使用的是HttpWatch與火狐的FireBug,兩個軟件的功能相互補充,兩個軟件基本的使用方法大家自行Google(百度,嘿嘿嘿=-=)
1.驗證碼的地址
ok首先打開我們的校園網登錄網頁:
aspx">http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/Default2.aspx
我們點擊IE 中HttpWatch Record按鈕,然後刷新這個網站我們可以看到如下的界面
我們能夠看到很多Get請求這些Get請求就能夠獲取打開的界面的各個元素,右鍵G右方的網址,選擇Open in the new Tab就可以看到各個元素,細心(累死=-=)的查找之後我們發現驗證碼的網址是
http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/CheckCode.aspx 打開後如下圖顯示
2.登陸post地址<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPiA8YnI+CrvxtcPR6dakwuu1xLXY1rfWrrrzo6zO0sPHu7nSqrvxtcO1x8K9t/7O8cb3tcS12Na3o6y7ucrHyrnTw0h0dHBXYXRjaKOsztLDx8rkyOu9+Mil1cu6xcPcwuvS1Lyw0enWpMLr1q6686OstaW797XHwry/qsq816XIoTxpbWcgc3JjPQ=="/uploadfile/Collfiles/20160413/20160413100313253.png" alt="如下圖所示" title="\">
打開下面的POST DATA就可以看到我們發送的數據以及他們的類別
__VIEWSTATE
Button1
hidPdrs
hidsc
lbLanguag
RadioButtonLi
TextBox2 密碼
txtSecretCode 驗證碼
txtUserName 用戶名 這裡不給用戶名,有需要的QQ 加我136057505=-=
這些值很關鍵,我們在這裡面可以找到對應的值,如果沒有值那就是空,好的這些我們下面會詳細講,我們還沒有獲得Post地址,右鍵Post這一行Copy即可,ok這樣我們發送Post請求的地址就到手了。
http://jwxt.nwu.edu.cn/%28dlxrmg55j21wlaqv2z5rcdyi%29/Default2.aspx
1.驗證碼圖片的獲取
我們有了各種地址,下面就要登陸了,但是我們知道了賬號和密碼(嘿嘿嘿,就不告訴你們=-=),我們還要知道驗證碼,怎麼獲得圖片呢?上文中我們已經知道了驗證碼的地址了,我們可以Get請求,但之前也說過一般使用Post請求來進行,於是我們上代碼
HttpPost httPost = new HttpPost(VERIFATIONURL);
HttpClient client = new DefaultHttpClient();
try {
HttpResponse httpResponse = client.execute(httPost);
byte[] bytes = new byte[1024];
bytes = EntityUtils.toByteArray(httpResponse.getEntity());
bmVerifation = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
HttpClient的詳細用法大家自行Google這裡不詳細展開
VERIFATIONURL就是上文中的驗證碼地址,首先創建post請求,再創建一個Httpclient ,然後是HttpResponse即響應,我們獲取的服務器的返回值就在通過他獲取,我們創建一個byte數組,首先將響應中獲得的數據轉化為byte數組,然後通過 BitmapFactory.decodeByteArray(bytes, 0, bytes.length);方法將byte數組編譯為bitmap,這時候我們就獲得了驗證碼的Bitmap了,我們能直接ImageView.SetBitmap麼?當然不能,首先第一點我們在網頁上獲取圖片是一個耗時操作,所以我們不能在主線程中進行,第二點只有在主線程中才能更新UI因此,我們使用Thread+Handler解決方法,上代碼:
private void DoGetVerifation() {
new Thread(new Runnable() {
@Override
public void run() {
HttpPost httPost = new HttpPost(VERIFATIONURL);
HttpClient client = new DefaultHttpClient();
try {
HttpResponse httpResponse = client.execute(httPost);
byte[] bytes = new byte[1024];
bytes = EntityUtils.toByteArray(httpResponse.getEntity());
bmVerifation = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
Message msg = new Message();
msg.arg1 = 10;
handler.sendMessage(msg);
}
}).start();
}
主線程中接收到msg之後ivVerifation.setImageBitmap(bmVerifation);這樣我們就得到了驗證碼的圖片並且在主界面上顯示出來了
2.發送Post請求登錄
我們目前知道了用戶名,密碼,驗證碼,下一步就是要登錄了,登陸同樣是個耗時操作,當然也要開啟一個新的線程,這沒什麼好說的了,我們同樣是發送Post請求,上代碼:
private void DoLogin(final String user, final String password, final String verifation) {
new Thread(new Runnable() {
@Override
public void run() {
DefaultHttpClient defaultclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(LOGINURL);
HttpResponse httpResponse;
//設置post參數
List params = new ArrayList();
params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
params.add(new BasicNameValuePair("Button1", ""));
params.add(new BasicNameValuePair("hidPdrs", ""));
params.add(new BasicNameValuePair("hidsc", ""));
params.add(new BasicNameValuePair("lbLanguage", ""));
params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
params.add(new BasicNameValuePair("TextBox2", password));
params.add(new BasicNameValuePair("txtSecretCode", verifation));
params.add(new BasicNameValuePair("txtUserName", user));
//獲得個人主界面的HTML
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = defaultclient.execute(httpPost);
Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
if (httpResponse.getStatusLine().getStatusCode() == 200) {
StringBuffer sb = new StringBuffer();
HttpEntity entity = httpResponse.getEntity();
MAINBODYHTML = EntityUtils.toString(entity);
IsLoginSuccessful(MAINBODYHTML);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
首先是創建post,client response,與前面無異,我們獲取數據的Post請求,比如說獲取驗證碼請求是不需要參數的,但是我們登錄需要發送給服務器 用戶名 密碼 驗證碼,於是我們為Post請求設置參數
//設置post參數
List params = new ArrayList();
params.add(new BasicNameValuePair("__VIEWSTATE", "dDwyODE2NTM0OTg7Oz6ZmvWn7xzjizifHN9MgLoDNTRtjQ=="));
params.add(new BasicNameValuePair("Button1", ""));
params.add(new BasicNameValuePair("hidPdrs", ""));
params.add(new BasicNameValuePair("hidsc", ""));
params.add(new BasicNameValuePair("lbLanguage", ""));
params.add(new BasicNameValuePair("RadioButtonList1", "%D1%A7%C9%FA"));
params.add(new BasicNameValuePair("TextBox2", password));
params.add(new BasicNameValuePair("txtSecretCode", verifation));
params.add(new BasicNameValuePair("txtUserName", user));
//獲得個人主界面的HTML
try {
httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));
httpResponse = defaultclient.execute(httpPost);
Log.i("xyz", String.valueOf(httpResponse.getStatusLine().getStatusCode()));
if (httpResponse.getStatusLine().getStatusCode() == 200) {
StringBuffer sb = new StringBuffer();
HttpEntity entity = httpResponse.getEntity();
MAINBODYHTML = EntityUtils.toString(entity);
IsLoginSuccessful(MAINBODYHTML);
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
這些參數就是我們之前使用HttpWatch獲取的Post data 只有類型沒有值的參數我們設置為“”空,使用httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8));方法將我們設置的param 參數集合給httppost,下面和之前一樣,也是通過Response獲取響應值,只不過這裡返回的不是圖片而是赤果果的String=-=,IsLoginSuccessful這個函數是用來判斷我們是否登錄成功,這就要用到我們下面要說的數據解析了。
1.返回的響應?
我們登陸後會得到服務器給我們的響應,我們在火狐的Firebug(HttpWatch也可以個人感覺看響應值Firebug比較方便)上機型一次成功的登陸,看一下他究竟會給我們返回什麼
如圖我們可以知道實際上響應值就是我們的網頁的HTML,如果登陸成功實際上就是返回個人首頁
那麼如果失敗呢?我們模擬一次驗證碼錯誤如下圖,可見同樣返回一個HTML
我們注意這一行
<script language='javascript' defer>alert('驗證碼不正確!!');document.getElementById('TextBox2').focus();
</script>
這實際上是彈出一個窗口,提示我們驗證碼錯誤,下面我們就會利用裡面不同的提示來判斷我們登陸的狀態
2.使用Jsoup進行數據解析
我們創建之前提過的IsLoginSuccessful函數
private void IsLoginSuccessful(String loginresult) {
Document doc = Jsoup.parse(loginresult);
Elements alert = doc.select("script[language]");
Elements success = doc.select("a[href]");
Message msg = new Message();
//先判斷是否登錄成功,若成功直接退出
for (Element link : success) {
//獲取所要查詢的URL,這裡對應地址按鈕的名字叫成績查詢
if (link.text().equals("等級考試查詢")) {
Log.i("xyz", "登錄成功");
msg.arg1 = 6;
handler.sendMessage(msg);
return;
}
}
for (Element link : alert) {
//刷新驗證碼
DoGetVerifation();
//獲取錯誤信息
if (link.data().contains("驗證碼不正確")) {
Log.i("xyz", "驗證碼錯誤");
msg.arg1 = 0;
handler.sendMessage(msg);
} else if (link.data().contains("用戶名不能為空")) {
Log.i("xyz", "用戶名不能為空");
msg.arg1 = 1;
handler.sendMessage(msg);
} else if (link.data().contains("密碼錯誤")) {
Log.i("xyz", "密碼或用戶名錯誤");
msg.arg1 = 2;
handler.sendMessage(msg);
} else if (link.data().contains("密碼不能為空")) {
Log.i("xyz", "密碼不能為空");
msg.arg1 = 3;
handler.sendMessage(msg);
......
}
}
我使用的是一個開源的庫Jsoup進行解析當然還有很多方法,大家自行Google(百度=-=咋老躺槍)
http://www.open-open.com/jsoup/ Jsoup的中文API指南大家可以看一下
首先我們判斷登陸成功,我們在登陸成功的HTML中找到了如下語句
等級考試查詢
我們通過 Elements success = doc.select(“a[href]”);獲取到a href”開頭的數據 然後我們循環判斷是否存在等級考試查詢這一項,如果存在,那肯定返回的響應就是我們的個人首頁了,也就是說登陸成功,我們使用Thread+Handler在主線程中Toast提醒用戶
然後我們通過 Elements alert = doc.select(“script[language]”);獲取到script 開頭的語句,同樣循環判斷,這裡要注意的是,與a href不同的是,我們要用的是link.data(),前面用的是link.text();
JsoupAPI 這樣寫的
我們登錄到了個人首頁,我們想進一步獲取自己的信息比如說四六級成績,怎麼辦呢?同樣還是抓取請求
跟獲取驗證碼網址的方法一樣,我們得到了成績表的網址
http://jwxt.nwu.edu.cn/(dlxrmg55j21wlaqv2z5rcdyi)/xsdjkscx.aspx?xh=2014117170&xm=鄒德宏&gnmkdm=N121606
前面http://jwxt.nwu.edu.cn/(dlxrmg55j21wlaqv2z5rcdyi)/是我們訪問的host是不變的,我們要做的就是獲取http://blog.csdn.net/mikogodzd/article/details/xsdjkscx.aspx?xh=2014117170&xm=鄒德宏&gnmkdm=N121606,這個問題困擾了很長時間,後來猛然發現這萬至竟然在前面說的deng’lu’cheng’登陸成功後返回的HTML中,所以還是使用Jsoup進行分析
Document doc = Jsoup.parse(MAINBODYHTML);
Elements links = doc.select("a[href]");
StringBuffer sb = new StringBuffer();
for (Element link : links) {
//獲取所要查詢的URL,這裡對應地址按鈕的名字叫成績查詢
if (link.text().equals("等級考試查詢")) {
sb.append(link.attr("href"));
}
}
GETSCOREURL = sb.toString();
GETSCOREURL就是我們要的後半個地址,然後我們加上前半個,進行Post請求抓取即可
這裡返回的是一個Html的表格
學年學期等級考試名稱准考證號考試日期成績聽力成績閱讀成績寫作成績綜合成績
2014-20152CET4610041151112625201506483167
1731430
2015-20161CET6610041152209503201512376126
1431070
同樣使用Jsoup
private void parse(String parse) {
Document doc = Jsoup.parse(parse);
Elements trs = doc.select("table").select("tr");
for (int i = 0; i < trs.size(); i++) {
Elements tds = trs.get(i).select("td");
for (int j = 0; j < tds.size(); j++) {
String text = tds.get(j).text();
score[i][j] = text;
Log.i("xyz", score[i][j]);
}
}
}
解析完我們就得到成績的數組了
博主大二黨自學Android開發1年半了,現在終於有入門的感覺了,這篇教程對新手來說坑能會有些難,但是一定要沉得住氣,博主剛下載HttpWatch那一會都懵逼了,啥都不懂,慢慢摸索之後終於有了頭緒,好了就這麼些,郵箱交流的朋友加我QQ 136057505=-=
Android一直沒有提供類似於ios中自帶清除效果的輸入框(ios只要只要添加屬性即可實現),所以在Android當中 想要實現此效果就需要使用自定義控件
直接使用線程在Android開發的時候,當我們需要完成一個耗時操作的時候,通常會新建一個子線程出來,例如如下代碼new Thread(new Runnable() {
本文實例講解了基於基於JMail實現Android郵件發送功能,分享給大家供大家參考,具體內容如下在android上發送郵件方式:第一種:借助GMail APP客戶端,缺
Android開發的時候經常遇到端口號被占用的問題,經常使程序無法運行,很煩人。我總結了一個很好的方法,非常實用。方法如下: (1):方法1:第一步:1:netstat