1、概述
最近想做一个校园助手类的APP,由于第一次做,所以打算先把每个功能单独实现,防止乱了阵脚。利用教务处登录获取课表和成绩等是一个基本功能,所以以获取课表为例实现了这个功能。完整代码点这里,尝试了好几次的,所以写的比较乱。
2、涉及的关键知识
首先,明确获取课表的流程:其实,获取课表就是让手机模拟浏览器,给服务器传去账号、密码,然后服务器会返回cookies(不懂自行百度),利用cookie就可以穿梭自如了,比如查课表。但是,浏览器登录时,返回的html文件浏览器是会自动解析成网页展现在我们面前的,但是APP就不行了,所以需要我们自己从html字符流中解析出我们需要的信息。就我自己的情况来看,模拟登录花了不少时间(这方面很不熟),解析html很快就解决了(可能是之前实践偶解析JSON)。
这样一个APP主要涉及到3个重要知识点:
1、如何模拟登录:java有自己的HttpURLConnection,但是不够灵活,所以用 apache的HttpClient更方便
2、如何解析HTML:网上查一下,Jsoup很合适。
3、AsyncTask:显然获取网页并解析是不能再主线程进行的,所以需要使用异步任务。
4、回调函数:这个不是必须的,但是我为了降低程序耦合度把网络请求那部分单独成了一个类,这时候更新主UI就需要利用回调函数了,之后细说。
3、模拟登陆(以窝工UPR为例)
1、解析教务处登录界面,看到一些教程用的还是HttpWatch,确实过时了,显然直接Google浏览器F12就行了。如下图,选择Network
点一下登录,就可以看到一大堆信息,我们需要的是第一个:
点击就可以看到详细信息了:
重要的有两个,Cookie,拿到这个我们才能导出去访问,Form Data就是模拟登录时要提交(POST)的表单数据;
4、HTML解析
利用上一步模拟登录拿到的Cookie就可以去获取课表了,同理,需要解析网页,如下,点击我的课表
可以看到一大堆信息,找到需要的
这个Response就是会返回我们的字符流,我们需要的就是解析它,设计到Jsoup的详细使用就不细说了,自己也是今天才接触,
总之借助以上两步就可以写出主要类Login.java的代码了,如下:
1 package com.example.upr.net; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 import java.io.UnsupportedEncodingException; 8 import java.util.ArrayList; 9 import java.util.List; 10 11 import org.apache.http.HttpEntity; 12 import org.apache.http.HttpResponse; 13 import org.apache.http.NameValuePair; 14 import org.apache.http.client.ClientProtocolException; 15 import org.apache.http.client.HttpClient; 16 import org.apache.http.client.entity.UrlEncodedFormEntity; 17 import org.apache.http.client.methods.HttpGet; 18 import org.apache.http.client.methods.HttpPost; 19 import org.apache.http.cookie.Cookie; 20 import org.apache.http.impl.client.AbstractHttpClient; 21 import org.apache.http.impl.client.DefaultHttpClient; 22 import org.apache.http.message.BasicNameValuePair; 23 import org.jsoup.Jsoup; 24 import org.jsoup.nodes.Document; 25 import org.jsoup.nodes.Element; 26 import org.jsoup.select.Elements; 27 28 import com.example.upr.CompleteListener; 29 30 import android.R.integer; 31 import android.os.AsyncTask; 32 import android.provider.ContactsContract.Contacts.Data; 33 import android.util.Log; 34 import android.widget.Toast; 35 36 public class Login extends AsyncTask{ 37 38 private String zjh; 39 private String mm; 40 private String webPage; 41 private String [][] kebiao = new String[15][8]; 42 private CompleteListener listener; 43 public Login(String account, String password, CompleteListener listener) { 44 zjh = account; 45 mm = password; 46 47 this.listener = listener; 48 } 49 @Override 50 protected String[][] doInBackground(Void... arg0) { 51 List list = new ArrayList (); 52 list.add(new BasicNameValuePair("zjh", zjh)); 53 list.add(new BasicNameValuePair("mm", mm));//表单信息 54 String encode = "gb2312";//编码格式,从F12工具中可以找到 55 HttpClient httpClient = new DefaultHttpClient(); 56 List cookies; 57 try { 58 HttpEntity entity = new UrlEncodedFormEntity(list ,encode);//封装数据 59 HttpPost post = new HttpPost(com.example.upr.Constant.LOGIN_URL);//建立POST请求 60 post.setEntity(entity); 61 HttpResponse httpResponse = httpClient.execute(post); 62 String data, result; 63 if (httpResponse.getStatusLine().getStatusCode() == 200) {//登录成功 64 cookies = ((AbstractHttpClient) httpClient).getCookieStore().getCookies();//获取Cookie 65 HttpGet httpGet = new HttpGet("http://zhjw.dlut.edu.cn/xkAction.do?actionType=6"); 66 httpGet.setHeader("Cookie", "JSESSIONID="+ cookies.get(0).getValue()+";"+ 67 "NSC_kjbpxv-iuuq="+cookies.get(1).getValue());//注意分号 68 httpResponse = new DefaultHttpClient().execute(httpGet); 69 70 StringBuffer sb = new StringBuffer(); 71 entity = httpResponse.getEntity(); 72 InputStream inputStream = entity.getContent(); 73 BufferedReader br = new BufferedReader(new InputStreamReader(inputStream, encode)); 74 while ((data = br.readLine()) != null) { 75 sb.append(data); //读取返回的网页 76 } 77 result = sb.toString(); 78 webPage = result; 79 80 } else { 81 return null; 82 } 83 } catch (UnsupportedEncodingException e) { 84 e.printStackTrace(); 85 return null; 86 } catch (ClientProtocolException e) { 87 // TODO Auto-generated catch block 88 e.printStackTrace(); 89 return null; 90 } catch (IOException e) { 91 // TODO Auto-generated catch block 92 e.printStackTrace(); 93 return null; 94 } 95 96 Document document = Jsoup.parse(webPage); 97 if (document != null) { 98 Element element = document.getElementById("user");//通过id直接定位到数据 99 Elements trElements = element.select("tr"); 100 //以下是提取课表信息,每个网站不同,一下代码要解析的html放在最下面了 101 for (int i = 0; i < trElements.size(); i++) { 102 Elements tdElements = trElements.get(i).select("td"); 103 for (int j = 0; j < tdElements.size(); j++) { 104 String text = tdElements.get(j).text(); 105 if (tdElements.size() == 1) { 106 continue; 107 } else if (tdElements.size()==9) { 108 if (j != 0) { 109 kebiao[i][j-1] = text; 110 } 111 }else { 112 kebiao[i][j] = text; 113 } 114 115 } 116 } 117 return kebiao; 118 } else { 119 return null; 120 } 121 122 } 123 @Override 124 protected void onPostExecute(String[][] result) { 125 super.onPostExecute(result); 126 listener.onConnectFinish(result);//执行回调函数,更新主UI 127 } 128 } 129 /* 130 131
303 304 */132 148133 134 135星期一136 137星期二138 139星期三140 141星期四142 143星期五144 145星期六146 147星期日149 162150 151上午
第1节(08:00-08:45) 152计算机网络A_01(西部校区综合教学2号楼B406) 153 154155 156 编译原理_01(校部综合教学1号楼综151) 157 158计算机组成原理_01(西部校区综合教学2号楼B103) 159 160161 163 174第2节(08:50-09:35) 164计算机网络A_01(西部校区综合教学2号楼B406) 165 166167 168 编译原理_01(校部综合教学1号楼综151) 169 170计算机组成原理_01(西部校区综合教学2号楼B103) 171 172173 175 185第3节(10:05-10:50) 176177 面向对象程序设计_01(西部校区综合教学2号楼A301) 178 179180 人工智能_01(西部校区综合教学2号楼A101) 181 182183 184 186 203第4节(10:55-11:40) 187188 面向对象程序设计_01(西部校区综合教学2号楼A301) 189 190191 人工智能_01(西部校区综合教学2号楼A101) 192 193194 195 196 197 202198 201199 午 休 200
204 217205 206下午
第5节(13:30-14:15) 207编译原理_01(校部综合教学1号楼综151) 208 209计算机组成原理_01(西部校区综合教学2号楼B103) 210 211212 计算机网络A_01(西部校区综合教学2号楼B406) 213 214215 216 218 229第6节(14:20-15:05) 219编译原理_01(校部综合教学1号楼综151) 220 221计算机组成原理_01(西部校区综合教学2号楼B103) 222 223224 计算机网络A_01(西部校区综合教学2号楼B406) 225 226227 228 230 240第7节(15:35-16:20) 231人工智能_01(西部校区综合教学2号楼A101) 232 233234 235 236 面向对象程序设计_01(西部校区综合教学2号楼A301) 237 238239 241 258第8节(16:25-17:10) 242人工智能_01(西部校区综合教学2号楼A101) 243 244245 246 247 面向对象程序设计_01(西部校区综合教学2号楼A301) 248 249250 251 252 257253 256254 晚 饭 255
259 271260 261晚上
第9节(18:00-18:45) 262社会学*_886(校部综合教学1号楼综152) 263 264人类文明史*_887(校部材料馆材101) 265 266267 268 269 270 272 282第10节(18:55-19:40) 273社会学*_886(校部综合教学1号楼综152) 274 275人类文明史*_887(校部材料馆材101) 276 277278 279 280 281 283 293第11节(19:50-20:35) 284社会学*_886(校部综合教学1号楼综152) 285 286人类文明史*_887(校部材料馆材101) 287 288289 290 291 292 294 302第12节(20:45-21:30) 295296 297 298 299 300 301
5、关于回调函数的使用
这一部分之前完全没预料到,只是后来才发现,不用内部类,想更新UI就得用回调函数,其更深层次的思想是一种设计模式,还是自己学的太少!知识使用完全浮于表面。
6、Demo演示
7、总结
零零散散花了不少时间,一来确实第一次做类似工作,二来自己习惯非常不好,对于新工具,研究API和即学即用能力不强。明天应该好好研究一下HttpClient和Jsoup,达到熟练抓取各种网页的目的!毕竟这只是开始!现在的学习状态实在太低效了。
以上。