android——抓取网页的android课表
2014-06-24 08:59
176 查看
刚刚完成了一个课表程序。刚开始学android,得把这些记录下来,方便以后查看。
我要做的东西是把学校服务器的课程表抓取下来,然后再在本机显示。这个课表程序可以对某节课进行编辑删除,可以设置闹钟。
用android自带的httpclient来获取网页。httpclient非常强大,它可以模拟一个浏览器来对服务器进行访问。对于不需要进行验证的网页,只要用get方法就可以取得网页,而对于需要验证的网页就得用post方法。(看到一篇关于http协议的文章,很好:/article/1775657.html)
但是有时候你也许服务器也不会理你,因为你的http报头并没有满足它的要求。这时候怎么办呢?给个提示:去研究一下http协议。
还有,把网页抓取下来后的html还需要进行解析才行。由于我的目标网页结构简单,我就自己写了一个简单的解析操作。
有人问我如果目标网页是有框架的该怎么抓取呢?我发一张图你就明白了
这是CSDN的论坛页面,它是有框架的,左边页面和右边页面的网址不一样。所以你只需要填入你想要的页面的网址就可以了。
由于刚开始学,很笨,就用一个个的textview来拼成了课表,不过这样挺好的,可以让我后期做的时候方便一些。效果是这样的:
所有的东西都是textview,最左边的一列(第几节那列)是不随着右边的滑动而滑动的,这样浏览的时候更方便。下面是布局的嵌套关系
当响应操作时间比较长的时候就需要进度条对话框了。
其实说到它我最想记录的还是线程。开启了进度条对话框后常常与之对应的就是线程了。用以下的方法就可以开启一个线程
[java] view
plaincopy
//开启一个线程用于获取课表
new Thread(){
public void run() {
try {
//想要在线程里做的
} catch (InterruptedException e) {
handler.sendEmptyMessage(0x222);
} catch (Exception e) {
// TODO Auto-generated catch block
handler.sendEmptyMessage(0x333);
} finally {
//在后台程序完成后,必须关闭进度对话框,否则进度对话框永远无法关闭
pdialog.dismiss();
}
}
}.start();
上面代码里的handler.sendEmptyMessage给已经定义好的handler发消息,这样能够在线程执行过程中通知线程的执行状态,这样可根据发回来的消息来进行下一步操作。很有用!
[java] view
plaincopy
Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg)
{
if(msg.what==0x111)
{
}
if(msg.what==0x333)
{
}
}
};
用msg.what取得线程发回来的消息。
有时候要加密用户的信息来存储。可以参考下面的代码。(这是从别人那儿看来的,出处找不到了,真不好意思!)
[java] view
plaincopy
public class Encrypt {
public static String encrypt(String txt, String key)
throws InvalidKeySpecException, InvalidKeyException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
StringBuffer sb = new StringBuffer();
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory skeyFactory = null;
Cipher cipher = null;
try {
skeyFactory = SecretKeyFactory.getInstance("DES");
cipher = Cipher.getInstance("DES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey deskey = skeyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, deskey);
byte[] cipherText = cipher.doFinal(txt.getBytes());
for (int n = 0; n < cipherText.length; n++) {
String stmp = (java.lang.Integer.toHexString(cipherText
& 0XFF));
if (stmp.length() == 1) {
sb.append("0" + stmp);
} else {
sb.append(stmp);
}
}
return sb.toString();
}
public static String decrypt(String txt, String key)
throws InvalidKeyException, InvalidKeySpecException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory skeyFactory = null;
Cipher cipher = null;
try {
skeyFactory = SecretKeyFactory.getInstance("DES");
cipher = Cipher.getInstance("DES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey deskey = skeyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, deskey);
byte[] btxts = new byte[txt.length() / 2];
for (int i = 0, count = txt.length(); i < count; i += 2) {
btxts[i / 2] = (byte) Integer.parseInt(txt.substring(i, i + 2), 16);
}
return (new String(cipher.doFinal(btxts)));
}
}
包括了加密和解密。但要注意使用相同的Key
文件操作一google一大堆,但有的用不了,中文会出现乱码,我是用InputStreamReader和BufferedReader来读取才能读出中文的
[java] view
plaincopy
InputStreamReader isr=new InputStreamReader(new FileInputStream(file),"utf-8");
BufferedReader br=new BufferedReader(isr);
接下来只要用br.readLine()就可以按行读取了。
刚开始还没学会activity,显示与切换界面全用的是setContentView()。后来果然有很多的不方便啊,比如界面要经常切换而且它们之间要传递信息的时候写的代码就很乱。和我一样懒的还是好好学activity吧。来记录一个很有用的:我的MainActivity需要传递信息给UpdateActivity,后者也要返回信息。
在MainActivity里这么写:
[java] view
plaincopy
Bundle data=new Bundle();
data.putString("info", 要传的信息);
Intent intent = new Intent();
intent.setClass(MainActivity.this,UpdateActivity.class)
intent.putExtras(data);
startActivityForResult(intent,0);//这样就可以 等待另一个activity返回信息
在UpdateActivity里这么写:
[java] view
plaincopy
String str= this.getIntent().getExtras().getString("info");//获得MainActivity传来的名为info的消息
//返回给main activity信息
Intent intent = getIntent();
Bundle bundle = new Bundle();
bundle.putString("backinfo",回传的消息);
intent.putExtras(bundle);
setResult(RESULT_OK,intent);
finish();
在MainActivity里重写接收回传信息的方法
[java] view
plaincopy
//update activity返回信息后会执行这个方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (resultCode)
{
//收到updateactivity发回来的消息要对表格和文件进行更新
case RESULT_OK:
Bundle bunde = data.getExtras();
String back = bunde.getString("lessoninfo");//获得名为lessoninfo的返回信息接下来就对返回信息进行操作
}
}
我之间也说过我的表格全是用的textview来做的,像这样android:id="@+id/tv1"是我第一个textview。那么当我要操作那么多个的textview时不可能一个个地写它们对应的事件吧。所以用到了下面的方法。
//这个方法可以根据id对应的string返回在R.java里记录资源的id
[java] view
plaincopy
Resources res=getResources();
int j=res.getIdentifier("tv"+String.valueOf(i),"id",getPackageName());
再啰嗦一下,就是有tv1,tv2,tv3,……上面的i就可以是1,2,3
可参考:http://blog.csdn.net/jeethongfei/article/details/6767826
我要说的是怎么设置多个闹钟。关键是PendingIntent pi=PendingIntent.getActivity(MainActivity.this, i, intent, 0);里的i。把i设置为不同的值就可以设置不同的闹钟了。只需设置这个地方而已!不需其它!
[java] view
plaincopy
Intent intent=new Intent(MainActivity.this,AlarmActivity.class);
Bundle data=new Bundle();
PendingIntent pi=PendingIntent.getActivity(MainActivity.this, i, intent, 0);
ring=(AlarmManager)getSystemService(Service.ALARM_SERVICE);//我是用另一个activity来做闹铃的
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);
如果你设置响铃的时间是在当前时间之前,那你一设置好闹钟就会响。可以按照下面的设置
[java] view
plaincopy
Calendar c = Calendar.getInstance();
int cweek=c.get(Calendar.DAY_OF_WEEK);
int chour=c.get(Calendar.HOUR_OF_DAY);
int cminute=c.get(Calendar.MINUTE);
if(week<cweek||(week==cweek&&(hour<chour||minute<cminute)))
{
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);
}
else
{
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);
}
ring是AlarmManager的实例,calendar是Calendar实例。week是想要设置的是星期几,hour是想要设置的小时,minute是分钟。要注意用get(Calendar.DAY_OF_WEEK)返回的星期天是1,星期一是2,依此类推。600*100*60*24*7,是一个星期的时间。
那为什么要像上面那么设置呢?如果今天是星期六(cweek就是7),想要设置的是星期1响(week就是2),那么如果用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);这样能够让下个星期一响。如果用ring.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi); 这样会让下下个星期一响,不符合设想。如果想要让下个星期五(week就是6)响,用用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);闹钟会立即响,而用ring.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);就能让下个星期五响。有点明白了吧,得好好想想。
比较两个字符串一定要用equals方法。String的split方法不会修改它本身,而是产生一个新的修改过的字符串!当要用系统资源时要加上权限!记得在AndroidManifest.xml中注册Activity!
由于涉及一些信息,我就不 上传源码了。
[java] view
plaincopy
<pre></pre>
<pre></pre>
<pre></pre>
我要做的东西是把学校服务器的课程表抓取下来,然后再在本机显示。这个课表程序可以对某节课进行编辑删除,可以设置闹钟。
1.读取学校教务处网页:
用android自带的httpclient来获取网页。httpclient非常强大,它可以模拟一个浏览器来对服务器进行访问。对于不需要进行验证的网页,只要用get方法就可以取得网页,而对于需要验证的网页就得用post方法。(看到一篇关于http协议的文章,很好:/article/1775657.html)但是有时候你也许服务器也不会理你,因为你的http报头并没有满足它的要求。这时候怎么办呢?给个提示:去研究一下http协议。
还有,把网页抓取下来后的html还需要进行解析才行。由于我的目标网页结构简单,我就自己写了一个简单的解析操作。
有人问我如果目标网页是有框架的该怎么抓取呢?我发一张图你就明白了
这是CSDN的论坛页面,它是有框架的,左边页面和右边页面的网址不一样。所以你只需要填入你想要的页面的网址就可以了。
2.接下来就是表格显示
由于刚开始学,很笨,就用一个个的textview来拼成了课表,不过这样挺好的,可以让我后期做的时候方便一些。效果是这样的:所有的东西都是textview,最左边的一列(第几节那列)是不随着右边的滑动而滑动的,这样浏览的时候更方便。下面是布局的嵌套关系
3.进度条对话框和线程
当响应操作时间比较长的时候就需要进度条对话框了。
其实说到它我最想记录的还是线程。开启了进度条对话框后常常与之对应的就是线程了。用以下的方法就可以开启一个线程
[java] view
plaincopy
//开启一个线程用于获取课表
new Thread(){
public void run() {
try {
//想要在线程里做的
} catch (InterruptedException e) {
handler.sendEmptyMessage(0x222);
} catch (Exception e) {
// TODO Auto-generated catch block
handler.sendEmptyMessage(0x333);
} finally {
//在后台程序完成后,必须关闭进度对话框,否则进度对话框永远无法关闭
pdialog.dismiss();
}
}
}.start();
4.handler
上面代码里的handler.sendEmptyMessage给已经定义好的handler发消息,这样能够在线程执行过程中通知线程的执行状态,这样可根据发回来的消息来进行下一步操作。很有用![java] view
plaincopy
Handler handler=new Handler()
{
@Override
public void handleMessage(Message msg)
{
if(msg.what==0x111)
{
}
if(msg.what==0x333)
{
}
}
};
用msg.what取得线程发回来的消息。
5.DES加密
有时候要加密用户的信息来存储。可以参考下面的代码。(这是从别人那儿看来的,出处找不到了,真不好意思!)[java] view
plaincopy
public class Encrypt {
public static String encrypt(String txt, String key)
throws InvalidKeySpecException, InvalidKeyException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
StringBuffer sb = new StringBuffer();
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory skeyFactory = null;
Cipher cipher = null;
try {
skeyFactory = SecretKeyFactory.getInstance("DES");
cipher = Cipher.getInstance("DES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey deskey = skeyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.ENCRYPT_MODE, deskey);
byte[] cipherText = cipher.doFinal(txt.getBytes());
for (int n = 0; n < cipherText.length; n++) {
String stmp = (java.lang.Integer.toHexString(cipherText
& 0XFF));
if (stmp.length() == 1) {
sb.append("0" + stmp);
} else {
sb.append(stmp);
}
}
return sb.toString();
}
public static String decrypt(String txt, String key)
throws InvalidKeyException, InvalidKeySpecException,
NoSuchPaddingException, IllegalBlockSizeException,
BadPaddingException {
DESKeySpec desKeySpec = new DESKeySpec(key.getBytes());
SecretKeyFactory skeyFactory = null;
Cipher cipher = null;
try {
skeyFactory = SecretKeyFactory.getInstance("DES");
cipher = Cipher.getInstance("DES");
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
SecretKey deskey = skeyFactory.generateSecret(desKeySpec);
cipher.init(Cipher.DECRYPT_MODE, deskey);
byte[] btxts = new byte[txt.length() / 2];
for (int i = 0, count = txt.length(); i < count; i += 2) {
btxts[i / 2] = (byte) Integer.parseInt(txt.substring(i, i + 2), 16);
}
return (new String(cipher.doFinal(btxts)));
}
}
包括了加密和解密。但要注意使用相同的Key
6.文件操作
文件操作一google一大堆,但有的用不了,中文会出现乱码,我是用InputStreamReader和BufferedReader来读取才能读出中文的[java] view
plaincopy
InputStreamReader isr=new InputStreamReader(new FileInputStream(file),"utf-8");
BufferedReader br=new BufferedReader(isr);
接下来只要用br.readLine()就可以按行读取了。
7.Activity界面通信
刚开始还没学会activity,显示与切换界面全用的是setContentView()。后来果然有很多的不方便啊,比如界面要经常切换而且它们之间要传递信息的时候写的代码就很乱。和我一样懒的还是好好学activity吧。来记录一个很有用的:我的MainActivity需要传递信息给UpdateActivity,后者也要返回信息。在MainActivity里这么写:
[java] view
plaincopy
Bundle data=new Bundle();
data.putString("info", 要传的信息);
Intent intent = new Intent();
intent.setClass(MainActivity.this,UpdateActivity.class)
intent.putExtras(data);
startActivityForResult(intent,0);//这样就可以 等待另一个activity返回信息
在UpdateActivity里这么写:
[java] view
plaincopy
String str= this.getIntent().getExtras().getString("info");//获得MainActivity传来的名为info的消息
//返回给main activity信息
Intent intent = getIntent();
Bundle bundle = new Bundle();
bundle.putString("backinfo",回传的消息);
intent.putExtras(bundle);
setResult(RESULT_OK,intent);
finish();
在MainActivity里重写接收回传信息的方法
[java] view
plaincopy
//update activity返回信息后会执行这个方法
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
switch (resultCode)
{
//收到updateactivity发回来的消息要对表格和文件进行更新
case RESULT_OK:
Bundle bunde = data.getExtras();
String back = bunde.getString("lessoninfo");//获得名为lessoninfo的返回信息接下来就对返回信息进行操作
}
}
8.由资源id对应的名称获得资源id
我之间也说过我的表格全是用的textview来做的,像这样android:id="@+id/tv1"是我第一个textview。那么当我要操作那么多个的textview时不可能一个个地写它们对应的事件吧。所以用到了下面的方法。//这个方法可以根据id对应的string返回在R.java里记录资源的id
[java] view
plaincopy
Resources res=getResources();
int j=res.getIdentifier("tv"+String.valueOf(i),"id",getPackageName());
再啰嗦一下,就是有tv1,tv2,tv3,……上面的i就可以是1,2,3
9.设置闹钟
可参考:http://blog.csdn.net/jeethongfei/article/details/6767826我要说的是怎么设置多个闹钟。关键是PendingIntent pi=PendingIntent.getActivity(MainActivity.this, i, intent, 0);里的i。把i设置为不同的值就可以设置不同的闹钟了。只需设置这个地方而已!不需其它!
[java] view
plaincopy
Intent intent=new Intent(MainActivity.this,AlarmActivity.class);
Bundle data=new Bundle();
PendingIntent pi=PendingIntent.getActivity(MainActivity.this, i, intent, 0);
ring=(AlarmManager)getSystemService(Service.ALARM_SERVICE);//我是用另一个activity来做闹铃的
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);
10.一设置闹钟就会响的问题
如果你设置响铃的时间是在当前时间之前,那你一设置好闹钟就会响。可以按照下面的设置[java] view
plaincopy
Calendar c = Calendar.getInstance();
int cweek=c.get(Calendar.DAY_OF_WEEK);
int chour=c.get(Calendar.HOUR_OF_DAY);
int cminute=c.get(Calendar.MINUTE);
if(week<cweek||(week==cweek&&(hour<chour||minute<cminute)))
{
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);
}
else
{
ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);
}
ring是AlarmManager的实例,calendar是Calendar实例。week是想要设置的是星期几,hour是想要设置的小时,minute是分钟。要注意用get(Calendar.DAY_OF_WEEK)返回的星期天是1,星期一是2,依此类推。600*100*60*24*7,是一个星期的时间。
那为什么要像上面那么设置呢?如果今天是星期六(cweek就是7),想要设置的是星期1响(week就是2),那么如果用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);这样能够让下个星期一响。如果用ring.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi); 这样会让下下个星期一响,不符合设想。如果想要让下个星期五(week就是6)响,用用ring.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),600*100*60*24*7,pi);闹钟会立即响,而用ring.setRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis()+600*100*60*24*7,600*100*60*24*7,pi);就能让下个星期五响。有点明白了吧,得好好想想。
11.一定要注意!
比较两个字符串一定要用equals方法。String的split方法不会修改它本身,而是产生一个新的修改过的字符串!当要用系统资源时要加上权限!记得在AndroidManifest.xml中注册Activity!由于涉及一些信息,我就不 上传源码了。
[java] view
plaincopy
<pre></pre>
<pre></pre>
<pre></pre>
相关文章推荐
- android——抓取网页的android课表
- Android抓取CSDN首页极客头条内容--网页数据抓取
- java/android抓取网页(含js)内容…
- Android 网页抓取(实现新闻客户端)
- Android中抓取妹子图网页数据并展示给界面
- Jsoup抓取网页数据完成一个简易的Android新闻APP
- Android中使用jsoup对网页数据的抓取和修改
- 转载自android 开发--抓取网页解析网页内容的若干方法(网络爬虫)(正则表达式)
- Android自由开发者 通过Jsoup抓取网页数据
- android:获取富文本图片和使用Jsoup抓取腾讯新闻网页数据
- Android 网页抓取乱码问题解决方案
- Android App使用Jsoup抓取网页数据显示
- android 开发--抓取网页解析网页内容的若干方法(网络爬虫)(正则表达式)
- 【Android+OkHttp3+Jsoup】 模拟登录教务系统 抓取课表和成绩
- Android抓取CSDN首页极客头条内容--网页数据抓取
- Android开发_Jsoup抓取网页信息
- 【Android 我的博客APP】1.抓取博客首页文章列表内容——网页数据抓取
- Android 网页抓取(实现新闻客户端)
- Android中网页数据的抓取和修改
- Android开发 - 抓取并解析网页数据(xml解析、html解析)