android jsoup 课程表,使用jsoup爬取数据实现android课程表
說明:只是爬蟲的一個(gè)實(shí)現(xiàn)案例,所以沒有多做功能,只做了登錄跟課表功能,課表有修改周次,單擊課程顯示課程詳細(xì)信息等功能。
開發(fā)平臺:Android Studio
界面
使用TimetableView android課程表控件,需要添加依賴
jsoup依賴:compile 'org.jsoup:jsoup:1.10.2'
界面控件依賴: compile 'com.zhuangfei:TimetableView:2.0.6'
界面效果如下:
實(shí)現(xiàn)原理(爬蟲實(shí)現(xiàn))
使用jsoup爬取教務(wù)系統(tǒng)數(shù)據(jù),connect()方法連接教務(wù)系統(tǒng),直接利用cookie連接學(xué)生課表頁面,解析返回的HTML頁面獲取該用戶的全部課程并封裝為java對象,再存入數(shù)據(jù)庫,可以使用java后臺服務(wù)器傳json數(shù)據(jù)給android客戶端,android客戶端解析json數(shù)據(jù),然后將課表數(shù)據(jù)展示出來。由于本案例小而單一,因此沒有配置服務(wù)器,改為將爬取的數(shù)據(jù)直接存入到android本地?cái)?shù)據(jù)庫SQLite,課表的展示去本地?cái)?shù)據(jù)庫查詢即可。
(注:各個(gè)學(xué)校學(xué)院的教務(wù)系統(tǒng)不盡相同,具體如何爬取數(shù)據(jù)詳見jsoup官方網(wǎng)站jsoup)
實(shí)體類:
public class MySubject implements ScheduleEnable {
public static final String EXTRAS_ID="extras_id";
public static final String EXTRAS_NUMBER="extras_number";//小班號
public static final String EXTRAS_AD_URL="extras_ad_url";//廣告Url
private int id;
/*
* 班號
* */
private String number;
/**
* 課程名
*/
private String name;
//無用數(shù)據(jù)
private String time;
/**
* 教室
*/
private String room;
/**
* 教師
*/
private String teacher;
/**
* 第幾周至第幾周上
*/
private List weekList;
/**
* 開始上課的節(jié)次
*/
private int start;
/**
* 上課節(jié)數(shù)
*/
private int step;
/**
* 周幾上
*/
private int day;
private String term;
/**
* 一個(gè)隨機(jī)數(shù),用于對應(yīng)課程的顏色
*/
private int colorRandom = 0;
private String url;
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return url;
}
public MySubject() {
// TODO Auto-generated constructor stub
}
public void setTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
public void setTerm(String term) {
this.term = term;
}
public String getTerm() {
return term;
}
public MySubject(String term,String name, String room, String teacher, List weekList, int start, int step, int day, int colorRandom,String time,String number) {
super();
this.term=term;
this.name = name;
this.room = room;
this.teacher = teacher;
this.weekList=weekList;
this.start = start;
this.step = step;
this.day = day;
this.colorRandom = colorRandom;
this.time = time;
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getRoom() {
return room;
}
public void setRoom(String room) {
this.room = room;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public void setWeekList(List weekList) {
this.weekList = weekList;
}
public List getWeekList() {
return weekList;
}
public int getStart() {
return start;
}
public void setStart(int start) {
this.start = start;
}
public int getStep() {
return step;
}
public void setStep(int step) {
this.step = step;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
public int getColorRandom() {
return colorRandom;
}
public void setColorRandom(int colorRandom) {
this.colorRandom = colorRandom;
}
@Override
public Schedule getSchedule() {
Schedule schedule=new Schedule();
schedule.setDay(getDay());
schedule.setName(getName());
schedule.setRoom(getRoom());
schedule.setStart(getStart());
schedule.setStep(getStep());
schedule.setTeacher(getTeacher());
schedule.setWeekList(getWeekList());
schedule.setColorRandom(2);
schedule.putExtras(EXTRAS_ID,getId());
schedule.putExtras(EXTRAS_AD_URL,getUrl());
schedule.putExtras(EXTRAS_NUMBER,getNumber());
return schedule;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
}
課表界面,對依賴的課表控件進(jìn)行了部分修改
數(shù)據(jù)源改為本地?cái)?shù)據(jù)庫查詢,實(shí)現(xiàn)了動態(tài)獲取數(shù)據(jù)源:mySubjects = getLocalSubjectData();
課表界面:
/**
* 課表Activity
*
*/
public class BaseFuncActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "BaseFuncActivity";
//控件
TimetableView mTimetableView;
WeekView mWeekView;
Button moreButton;
LinearLayout layout;
TextView titleTextView;
List mySubjects;
//記錄切換的周次,不一定是當(dāng)前周
int target = -1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_base_func);
moreButton = (Button) findViewById(R.id.id_more);
moreButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
showPopmenu();
}
});
//數(shù)據(jù)源
// mySubjects = SubjectRepertory.loadDefaultSubjects2();
//獲取本地?cái)?shù)據(jù)庫數(shù)據(jù)
mySubjects = getLocalSubjectData();
//mySubjects.addAll(SubjectRepertory.loadDefaultSubjects());
titleTextView = (TextView) findViewById(R.id.id_title);
layout = (LinearLayout) findViewById(R.id.id_layout);
layout.setOnClickListener(this);
initTimetableView();//初始化
setMonthWidth();//設(shè)置月份寬度
// showTime();//顯示時(shí)間
buildItemText();//設(shè)置課程文本內(nèi)容
}
//獲取本地?cái)?shù)據(jù)庫數(shù)據(jù)
private List getLocalSubjectData() {
List data = DaoImpl.GetCourceData();
if(!GetSaveStatus()){//非登錄狀態(tài)
DaoImpl.ClearCource();//清空表格
}
return data;
}
//判斷登錄狀態(tài)
private boolean GetSaveStatus() {
SharedPreferences Preferences=getSharedPreferences("userInfo",MODE_PRIVATE);
String username=Preferences.getString("username","");
String password=Preferences.getString("password","");
if(username.equals("")&&password.equals("")){
return false;
}else{
return true;
}
}
/**
* 初始化課程控件
*/
private void initTimetableView() {
//獲取控件
mWeekView = (WeekView) findViewById(R.id.id_weekview);
mTimetableView = (TimetableView) findViewById(R.id.id_timetableView);
//設(shè)置周次選擇屬性
mWeekView.source(mySubjects)
.curWeek(getLocalWeek())
.callback(new IWeekView.OnWeekItemClickedListener() {
@Override
public void onWeekClicked(int week) {
int cur = mTimetableView.curWeek();
//更新切換后的日期,從當(dāng)前周cur->切換的周week
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, week);
mTimetableView.changeWeekOnly(week);
}
})
.callback(new IWeekView.OnWeekLeftClickedListener() {
@Override
public void onWeekLeftClicked() {
onWeekLeftLayoutClicked();
}
})
.isShow(false)//設(shè)置隱藏,默認(rèn)顯示
.showView();
mTimetableView.source(mySubjects)
.curWeek(getLocalWeek())
.curTerm("")
.maxSlideItem(16)
.monthWidthDp(30)
//透明度
//日期欄0.1f、側(cè)邊欄0.1f,周次選擇欄0.6f
//透明度范圍為0->1,0為全透明,1為不透明
// .alpha(0.1f, 0.1f, 0.6f)
.callback(new ISchedule.OnItemClickListener() {
@Override
public void onItemClick(View v, List scheduleList) {
display(scheduleList);
}
})
.callback(new ISchedule.OnItemLongClickListener() {
@Override
public void onLongClick(View v, int day, int start) {
Toast.makeText(BaseFuncActivity.this,
"長按:周" + day + ",第" + start + "節(jié)",
Toast.LENGTH_SHORT).show();
}
})
.callback(new ISchedule.OnWeekChangedListener() {
@Override
public void onWeekChanged(int curWeek) {
titleTextView.setText("第" + curWeek + "周");
}
})
//旗標(biāo)布局點(diǎn)擊監(jiān)聽
.callback(new ISchedule.OnFlaglayoutClickListener() {
@Override
public void onFlaglayoutClick(int day, int start) {
mTimetableView.hideFlaglayout();
Toast.makeText(BaseFuncActivity.this,
"點(diǎn)擊了旗標(biāo):周" + (day + 1) + ",第" + start + "節(jié)",
Toast.LENGTH_SHORT).show();
}
})
.showView();
}
/*
獲取周數(shù)
* */
private int getLocalWeek() {
int weekResult = -1;
SharedPreferences preferences=getSharedPreferences("userInfo",MODE_PRIVATE);
int startWeek = preferences.getInt("weekNumber",-1);//,起始周從SharedPreferences獲取
//華軟教務(wù)系統(tǒng)總共17周
if(startWeek<=17){
String startDay=preferences.getString("date","");//起始周的星期一,從SharedPreferences獲取,為yyyy-MM-dd格式
// String endDay= CalendarUtil.getMondayOfWeek();//當(dāng)前周的星期一,為yyyy-MM-dd格式
String endDay= CalendarUtil.getMondayOfWeek2();//當(dāng)前周的星期一,為yyyy-MM-dd格式
SharedPreferences.Editor editor=getSharedPreferences("userInfo",MODE_PRIVATE).edit();
editor.putString("current", endDay);
editor.commit();
int tempweek=CalendarUtil.getTwoDay(endDay,startDay);//間隔天數(shù)
int week=tempweek/7;
if(week==0){
weekResult=startWeek;
}else{
int finalweek=startWeek+week;
if(finalweek>17){
weekResult=17;
}else{
weekResult=finalweek;
}
}
return weekResult;
}else{
weekResult=17;
return weekResult;
}
}
/**
* 周次選擇布局的左側(cè)被點(diǎn)擊時(shí)回調(diào)
* 對話框修改當(dāng)前周次
*/
protected void onWeekLeftLayoutClicked() {
final String items[] = new String[20];
int itemCount = mWeekView.itemCount();
for (int i = 0; i < itemCount; i++) {
items[i] = "第" + (i + 1) + "周";
}
target = -1;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("設(shè)置當(dāng)前周");
builder.setSingleChoiceItems(items, mTimetableView.curWeek() - 1,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
target = i;
}
});
builder.setPositiveButton("設(shè)置為當(dāng)前周", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (target != -1) {
mWeekView.curWeek(target + 1).updateView();
mTimetableView.changeWeekForce(target + 1);
//把修改的周次存入SharedPreferences中
SharedPreferences.Editor editor=getSharedPreferences("userInfo",MODE_PRIVATE).edit();
editor.putInt("weekNumber",target+1);
// editor.putString("date", CalendarUtil.getMondayOfWeek());
editor.putString("date", CalendarUtil.getMondayOfWeek2());
editor.commit();
// Log.d("info",Integer.toString(target+1));
}
}
});
builder.setNegativeButton("取消", null);
builder.create().show();
}
/**
* 顯示內(nèi)容
*
* @param beans
*/
protected void display(List beans) {
String str = "";
for (Schedule bean : beans) {
str += bean.getName() + ","+bean.getWeekList().toString()+","+bean.getTeacher()+",["+bean.getExtras().get(MySubject.EXTRAS_NUMBER)+"]\n";
}
Toast.makeText(this, str, Toast.LENGTH_SHORT).show();
}
/**
* 顯示彈出菜單
*/
private void showPopmenu() {
PopupMenu popup = new PopupMenu(this, moreButton);
popup.getMenuInflater().inflate(R.menu.popmenu_base_func, popup.getMenu());
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
/*case R.id.top1:
addSubject();
break;
case R.id.top2:
deleteSubject();
break;*/
case R.id.top4:
hideNonThisWeek();
break;
case R.id.top5:
showNonThisWeek();
break;
/*case R.id.top6:
setMaxItem(8);
break;
case R.id.top7:
setMaxItem(10);
break;
case R.id.top8:
setMaxItem(12);
break;*/
case R.id.top9:
showTime();
break;
case R.id.top10:
hideTime();
break;
case R.id.top11:
showWeekView();
break;
case R.id.top12:
hideWeekView();
break;
/*case R.id.top13:
setMonthWidth();
break;
case R.id.top16:
resetMonthWidth();
break;*/
case R.id.top14:
hideWeekends();
break;
case R.id.top15:
showWeekends();
break;
case R.id.top17:
exitCurrentAccount();
break;
default:
break;
}
return true;
}
});
popup.show();
}
/**
* 退出當(dāng)前賬號
* 清空本地賬號及SharedPreferences
*/
private void exitCurrentAccount() {
//清空數(shù)據(jù)庫
DaoImpl.ClearCource();
//清空SharedPreferences
SharedPreferences.Editor editor=getSharedPreferences("userInfo",MODE_PRIVATE).edit();
editor.putInt("weekNumber",-1);
editor.putString("date", "");
editor.putString("username","");
editor.putString("password","");
editor.putString("current", "");
editor.commit();
Intent intent=new Intent(BaseFuncActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
/**
* 刪除課程
* 內(nèi)部使用集合維護(hù)課程數(shù)據(jù),操作集合的方法來操作它即可
* 最后更新一下視圖(全局更新)
*/
protected void deleteSubject() {
int size = mTimetableView.dataSource().size();
int pos = (int) (Math.random() * size);
if (size > 0) {
mTimetableView.dataSource().remove(pos);
mTimetableView.updateView();
}
}
/**
* 添加課程
* 內(nèi)部使用集合維護(hù)課程數(shù)據(jù),操作集合的方法來操作它即可
* 最后更新一下視圖(全局更新)
*/
protected void addSubject() {
List dataSource = mTimetableView.dataSource();
int size = dataSource.size();
if (size > 0) {
Schedule schedule = dataSource.get(0);
dataSource.add(schedule);
mTimetableView.updateView();
}
}
/**
* 隱藏非本周課程
* 修改了內(nèi)容的顯示,所以必須更新全部(性能不高)
* 建議:在初始化時(shí)設(shè)置該屬性
*
* updateView()被調(diào)用后,會重新構(gòu)建課程,課程會回到當(dāng)前周
*/
protected void hideNonThisWeek() {
mTimetableView.isShowNotCurWeek(false).updateView();
}
/**
* 顯示非本周課程
* 修改了內(nèi)容的顯示,所以必須更新全部(性能不高)
* 建議:在初始化時(shí)設(shè)置該屬性
*/
protected void showNonThisWeek() {
mTimetableView.isShowNotCurWeek(true).updateView();
}
/**
* 設(shè)置側(cè)邊欄最大節(jié)次,只影響側(cè)邊欄的繪制,對課程內(nèi)容無影響
*
* @param num
*/
protected void setMaxItem(int num) {
mTimetableView.maxSlideItem(num).updateSlideView();
}
/**
* 顯示時(shí)間
* 設(shè)置側(cè)邊欄構(gòu)建監(jiān)聽,TimeSlideAdapter是控件實(shí)現(xiàn)的可顯示時(shí)間的側(cè)邊欄
*/
protected void showTime() {
String[] times = new String[]{
"09:00", "10:20", "10:40", "12:00",
"12:30", "13:50", "14:00", "15:20",
"15:30", "16:50", "17:00", "18:20",
"19:00", "20:20", "20:30", "21:50"
};
OnSlideBuildAdapter listener = (OnSlideBuildAdapter) mTimetableView.onSlideBuildListener();
listener.setTimes(times)
.setTimeTextColor(Color.BLACK);
mTimetableView.updateSlideView();
}
/**
* 隱藏時(shí)間
* 將側(cè)邊欄監(jiān)聽置Null后,會默認(rèn)使用默認(rèn)的構(gòu)建方法,即不顯示時(shí)間
* 只修改了側(cè)邊欄的屬性,所以只更新側(cè)邊欄即可(性能高),沒有必要更新全部(性能低)
*/
protected void hideTime() {
mTimetableView.callback((ISchedule.OnSlideBuildListener) null);
mTimetableView.updateSlideView();
}
/**
* 顯示W(wǎng)eekView
*/
protected void showWeekView() {
mWeekView.isShow(true);
}
/**
* 隱藏WeekView
*/
protected void hideWeekView() {
mWeekView.isShow(false);
}
/**
* 設(shè)置月份寬度
*/
private void setMonthWidth() {
mTimetableView.monthWidthDp(50).updateView();
}
/**
* 設(shè)置月份寬度,默認(rèn)40dp
*/
private void resetMonthWidth() {
mTimetableView.monthWidthDp(40).updateView();
}
/**
* 修改顯示的文本
*/
public void buildItemText() {
mTimetableView.callback(new OnItemBuildAdapter() {
@Override
public String getItemText(Schedule schedule, boolean isThisWeek) {
if (isThisWeek) return schedule.getName()+"["+schedule.getExtras().get(MySubject.EXTRAS_NUMBER)+"]"+"\n@"+schedule.getRoom();
return "[非本周]" + schedule.getName()+"["+schedule.getExtras().get(MySubject.EXTRAS_NUMBER)+"]"+"\n@"+schedule.getRoom();
}
})
.updateView();
}
/**
* 隱藏周末
*/
private void hideWeekends() {
mTimetableView.isShowWeekends(false).updateView();
}
/**
* 顯示周末
*/
private void showWeekends() {
mTimetableView.isShowWeekends(true).updateView();
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.id_layout:
//如果周次選擇已經(jīng)顯示了,那么將它隱藏,更新課程、日期
//否則,顯示
if (mWeekView.isShowing()) {
mWeekView.isShow(false);
titleTextView.setTextColor(getResources().getColor(R.color.app_course_textcolor_blue));
int cur = mTimetableView.curWeek();
mTimetableView.onDateBuildListener()
.onUpdateDate(cur, cur);
mTimetableView.changeWeekOnly(cur);
} else {
mWeekView.isShow(true);
titleTextView.setTextColor(getResources().getColor(R.color.app_red));
}
break;
}
}
}
登錄:
如果勾選記住登錄,則將學(xué)號以及密碼存入SharedPreferences中,下次打開跳過登錄直接顯示課表,實(shí)現(xiàn)了離線操作,避免了需要再次聯(lián)網(wǎng)登錄爬取數(shù)據(jù)。
附:源碼詳見github
總結(jié)
以上是生活随笔為你收集整理的android jsoup 课程表,使用jsoup爬取数据实现android课程表的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 就在今天!当当科技品类日满100减50
- 下一篇: 12.05计算机导论课后总结