两周废寝忘食的创作终于成功了,现在拿出来分享一下。
先不说别的看一下程序运行效果图,我没怎么设计ui所以界面不是很好看但是能说明问题~~~
现在我们来看看实现这个功能需要些什么准备工作,我们需要网络信息抓取工具一般windows可以用httpwatch我是mac系统所以我详细介绍一下mac上面的工具
接下来就可以进行网页信息抓包了,先看一下我们学校的教务网页
然后我们开始进行对浏览器抓包我使用的是Charles首先要配置成下图这个样子然后才可以抓包
首先对登录信息抓包时要找下面这张截图上的信息
然后对获取验证吗抓包
我们在response中可以看到返回信息
然后我们需要下载一个解析HTML源码的架构包叫Jsoup放到libs目录下然后右键选择
as library
这些就准备好了然后我们来看一小段HTML代码方便解析时候讲解
<br> <table width="100%" class="datalist"> <tr> <th> 学年 </th> <th> 学期 </th> <th> 课程号 </th> <th> 课序号 </th> <th> 课程名 </th> <th> 选课属性 </th> <th> 课组 </th> <th> 学分 </th> <th> 平时 </th> <th> 期末 </th> <th> 总评 </th> <th> 是否缓考 </th> <th> 考试性质 </th> <th> 备注 </th> <th> 主讲教师 </th> <th> 课程类别 </th> </tr>可以看到我们需要的信息都被一个一个的标签包裹着,Jsoup所做的就是将我们需要的信息从标签里剥离出来。
然后我们来看代码实现,我这里分了两个Activity来实现
1.MainActivity
public class MainActivity extends ActionBarActivity { //使用SharedPreferences进行用户的用户名密码以及cookie的保存 SharedPreferences sharedPreferences; SharedPreferences.Editor editor; private EditText studentNumber; private EditText passWord; private EditText idCode; private Bitmap bitmap; private ImageView IdcodeImage; //注意这里Handler使用的是import android.os.Handler;这个包 private Handler handler; private Button logIn; String StudentNumber; String PassWord; String IdCode; String groupId=""; String login="登录"; //这条是解析出来进行获取验证码的图片的网址 String url2="http://jw.djtu.edu.cn/academic/getCaptcha.do"; //这条是解析出来进行提交登录信息的网址 String url3="http://jw.djtu.edu.cn/academic/j_acegi_security_check"; //这里使用HttpClient进行数据的获取和提交 HttpClient client; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //实例化HttpClient对象 client=new DefaultHttpClient(); //sharedPreferences第一个参数是给你保存的信息起个名字,第二个参数设置为Context.MODE_PRIVATE属性, // 这样会避免其他应用可以直接访问我们保存的信息 sharedPreferences=getSharedPreferences("params", Context.MODE_PRIVATE); //实例化SharedPreferences.Editor对象 editor=sharedPreferences.edit(); studentNumber=(EditText)findViewById(R.id.studentNumber); passWord=(EditText)findViewById(R.id.key); IdcodeImage=(ImageView)findViewById(R.id.passImage); idCode=(EditText)findViewById(R.id.identifyingCode); logIn=(Button)findViewById(R.id.login); //实例化Handler对象方便线程之间通信 handler=new Handler(); //对我们的验证码绑定一个单击响应事件,这是为了去实现验证码看不清时再更新一张验证码而用 IdcodeImage.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread() { @Override public void run() { //我们需要同步Cookie信息所以从验证码开始就需要获取Cookie List<Cookie> cookies1; //HttpGet来发送获取验证码请求 HttpGet httpGet = new HttpGet(url2); //声明一个HttpResponse HttpResponse httpResponse = null; try { //实例化HttpResponse httpResponse = client.execute(httpGet); } catch (IOException e) { e.printStackTrace(); } //如果服务器响应成功 if (httpResponse.getStatusLine().getStatusCode() == 200) { try { //使用输入流来接受数据 InputStream in = httpResponse.getEntity().getContent(); //bitmap来获取数据流中的图片信息 bitmap = BitmapFactory.decodeStream(in); //关闭输入流 in.close(); String Cookies; //获取Cookie cookies1 = ((AbstractHttpClient) client).getCookieStore().getCookies(); Cookies = "JSESSIONID="+cookies1.get(0).getValue().toString(); //System.out.println(Cookies); //在SharedPreferences中保存cookie editor.putString("Cookies", Cookies); //提交保存数据 editor.commit(); //通过handler.post方法在线程中更新主线程中的验证码图片信息 handler.post(new Runnable() { @Override public void run() { if (bitmap != null) { IdcodeImage.setImageBitmap(bitmap); } } }); } catch (IOException e) { e.printStackTrace(); } } } }.start(); } }); //初始化时获取验证码图片代码与上面的一致 new Thread() { @Override public void run() { List<Cookie> cookies1; HttpGet httpGet = new HttpGet(url2); HttpResponse httpResponse = null; try { httpResponse = client.execute(httpGet); } catch (IOException e) { e.printStackTrace(); } if (httpResponse.getStatusLine().getStatusCode() == 200) { try { InputStream in = httpResponse.getEntity().getContent(); bitmap = BitmapFactory.decodeStream(in); in.close(); String Cookies; cookies1 = ((AbstractHttpClient) client).getCookieStore().getCookies(); Cookies = "JSESSIONID="+cookies1.get(0).getValue().toString(); System.out.println(Cookies); editor.putString("Cookies", Cookies); editor.commit(); handler.post(new Runnable() { @Override public void run() { if (bitmap != null) { IdcodeImage.setImageBitmap(bitmap); } } }); } catch (IOException e) { e.printStackTrace(); } } } }.start(); //对登录按钮绑定单击响应事件 logIn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //获取输入信息 StudentNumber =studentNumber.getText().toString(); PassWord =passWord.getText().toString(); IdCode = idCode.getText().toString(); //这里写入StudentNumber和PassWord是为了做记住密码登录 editor.putString("StudentNumber", StudentNumber); editor.putString("PassWord", PassWord); editor.putString("IdCode", IdCode); editor.commit(); new Thread() { @Override public void run() { //提交数据用List<NameValuePair>的方式 List<NameValuePair> params = new ArrayList<NameValuePair>(); //这里的名称不要有多余的符号,因为提交数据时httppost方法会帮你维护数据 //这里表单的数据顺序要按照刚刚解析所显示的顺序排列 params.add(new BasicNameValuePair("groupId", groupId)); params.add(new BasicNameValuePair("j_username", StudentNumber)); params.add(new BasicNameValuePair("login",login)); params.add(new BasicNameValuePair("j_password", PassWord)); params.add(new BasicNameValuePair("j_captcha", IdCode)); System.out.println(params); try { HttpPost httpPost = new HttpPost(url3); String Cookies; //获取到刚刚在获取验证码时得到的Cookie Cookies = sharedPreferences.getString("Cookies", null); //System.out.println(Cookies); //提交数据做准备 httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //同步cookie httpPost.setHeader("Cookie", Cookies); //获取返回的信息 HttpResponse httpResponse = client.execute(httpPost); //如果响应成功则进入Score_find的Activity if (httpResponse.getStatusLine().getStatusCode() == 200) { String result = EntityUtils.toString(httpResponse.getEntity()); //System.out.println(result); startActivity(new Intent(MainActivity.this, Score_find.class)); } else { System.out.println("登录失败!"); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } }); } }2.Score_find Activity
public class Score_find extends ActionBarActivity{ private String Cookies; HttpClient client; private String url="http://jw.djtu.edu.cn/academic/manager/score/studentOwnScore.do?groupId=&moduleId=2021"; private String year=null; private String trem=null; private String para="0"; private String sortColumn=""; private String Submit="查询"; private TextView showScore; private EditText InputYear; private EditText InputTrem; SharedPreferences sharedPreferences; StringBuffer sb=new StringBuffer(); private Handler handler=null; private Button searchButton; //这两个标记是用于判断用户输入的数据是否合法 private int mark1=0; private int mark2=0; @Override protected void onCreate(final Bundle saveInstanceState) { super.onCreate(saveInstanceState); setContentView(R.layout.score_find); InputTrem=(EditText)findViewById(R.id.InputTrem); InputYear=(EditText)findViewById(R.id.InputYear); searchButton=(Button)findViewById(R.id.searchButton); showScore=(TextView)findViewById(R.id.show_score); //设置showScore可以滚动 showScore.setMovementMethod(ScrollingMovementMethod.getInstance()); handler=new Handler() { @Override public void handleMessage(Message message) { //加载信息 showScore.setText(sb.toString()); } }; sharedPreferences = getSharedPreferences("params", Context.MODE_PRIVATE); Cookies=sharedPreferences.getString("Cookies", null); showScore=(TextView)findViewById(R.id.show_score); client=new DefaultHttpClient(); searchButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String years=null,trems=null; years=InputYear.getText().toString(); trems=InputTrem.getText().toString(); System.out.println(years+trems); //输入信息的判断 if("春".equals(trems)) { trem="1"; mark1=1; System.out.println(trems+"\t"+trem); }else if("秋".equals(trems)) { trem="2"; mark1=1; }else { mark1=0; Toast.makeText(Score_find.this,"输入学期有误请重新输入!",Toast.LENGTH_SHORT).show(); } if("2014".equals(years)) { year="34"; mark2=1; System.out.println(years+"\t"+year); }else if("2015".equals(years)) { year="35"; mark2=1; } else { mark2=0; Toast.makeText(Score_find.this,"输入年份有误请重新输入!",Toast.LENGTH_SHORT).show(); } //如果两个信息都输入合法则提交请求 if(mark1==1&&mark2==1) { //是耗时操作都要放到新线程里执行 new Thread() { @Override public void run() { HttpResponse httpResponse; HttpPost httpPost = new HttpPost(url); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("year", year)); params.add(new BasicNameValuePair("term", trem)); params.add(new BasicNameValuePair("para", para)); params.add(new BasicNameValuePair("sortColumn", sortColumn)); params.add(new BasicNameValuePair("Submit", Submit)); System.out.println(params); httpPost.setHeader("Cookie", Cookies); try { httpPost.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); httpResponse = client.execute(httpPost); if (httpResponse.getStatusLine().getStatusCode() == 200) { StringBuffer stringBuffer = new StringBuffer(); String result = null; InputStream inputStream = httpResponse.getEntity().getContent(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); String data = ""; //读取得到的数据 while ((data = bufferedReader.readLine()) != null) { stringBuffer.append(data); stringBuffer.append("\n"); } result = stringBuffer.toString(); //判断是否获取到数据 if (result == null) { System.out.println("NULL!!!!"); } else { //这里使用jsoup开源的解析包进行html源码的解析 //获取要解析的网址或者文档或者网址 Document document = Jsoup.parse(result); //经过分析成绩保存在datalist这个Class中因此要定位到这个类中 Elements elements = document.getElementsByClass("datalist"); //获取他的第一个元素集合 Element element = elements.get(0); //再分析可以看到在tr标签下有成绩的详细信息 Elements elements1 = element.getElementsByTag("tr"); Element element2; Elements elements3; Element element3; Element element4; for (int i = 0; i < elements1.size(); i++) { //剥离每一个标签 element2 = elements1.get(i); //再重新定位td标签下的内容 elements3 = element2.getElementsByTag("td"); for (int j = 0; j < elements3.size(); j++) { //这里为了获取td标签中的子元素要进行一个循环 if (j == 0) { //我发现我要的课程名和成绩分别在elements3集合中的第5个元素和第11个元素 element3 = elements3.get(4); element4 = elements3.get(10); sb.append(element3.text()).append(":").append("\t\t").append(element4.text()).append("\n"); } else { break; } } } //数据获取完成通知组件重绘信息 handler.sendEmptyMessage(0); } } } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }.start(); } } }); } }最后我用红字强调一下做这个demo我一开始失败了好多次的地方:
1.Cookie信息获取要在获取验证码时同时获取。
2.如果线程操作封装成类的话会造成SharedPreferences绑定content失败。
3.对HTML源码进行解析的时候我尝试将解析方法写成一个函数,但是会出现一些未捕获的错误而导致程序崩溃,因此我把解析步骤也放到了新线程中。
希望对大家有所帮助,欢迎转载但要标明出处,谢谢!
有什么不足的地方可以留言给我我会尽快回复并改正!