实现-超级课程表——校园登录(2)

课表是在一张table里的,提取table里的内容进行解析,解析方法不止一种,我在解析过程中也尝试了多种方法,直接看代码把

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/**
      * 根据网页返回结果解析课程并保存
      *
      * @param content
      * @return
      */
     public String parseCourse(String content) {
         StringBuilder result = new StringBuilder();
         Document doc = Jsoup.parse(content);
  
         Elements semesters = doc.select(option[selected=selected]);
         String[] years=semesters.get( 0 ).text().split(-);
         int startYear=Integer.parseInt(years[ 0 ]);
         int endYear=Integer.parseInt(years[ 1 ]);
         int semester=Integer.parseInt(semesters.get( 1 ).text());
  
  
  
         Elements elements = doc.select(table#Table1);
         Element element = elements.get( 0 ).child( 0 );
         //移除一些无用数据
  
         element.child( 0 ).remove();
         element.child( 0 ).remove();
         element.child( 0 ).child( 0 ).remove();
         element.child( 4 ).child( 0 ).remove();
         element.child( 8 ).child( 0 ).remove();
         int rowNum = element.childNodeSize();
         int [][] map = new int [ 11 ][ 7 ];
         for ( int i = 0 ; i < rowNum - 1 ; i++) {
             Element row = element.child(i);
             int columnNum = row.childNodeSize() - 2 ;
             for ( int j = 1 ; j < columnNum; j++) {
                 Element column = row.child(j);
                 int week = fillMap(column, map, i);
                 //填充map,获取周几,第几节至第几节
                 //作用:弥补不能获取这些数据的格式
                 if (column.hasAttr(rowspan)) {
                     try {
                         System.out.println(周+ week+  第+ (i + 1 )+ 节-第+ (i + Integer.parseInt(column.attr(rowspan))) + 节);
                         splitCourse(column.html(), startYear,endYear,semester,week, i + 1 ,i + Integer.parseInt(column.attr(rowspan)));
                     } catch (NumberFormatException e) {
                         e.printStackTrace();
                     }
                 }
             }
         }
  
         return result.toString();
     }
  
  
  
     /**
      * 根据传进来的课程格式转换为对应的实体类并保存
      * @param sub
      * @param startYear
      * @param endYear
      * @param semester
      * @param week
      * @param startSection
      * @param endSection
      * @return
      */
     private Course storeCourseByResult(String sub, int startYear, int endYear, int semester, int week,
             int startSection, int endSection) {
         // 周二第1,2节{第4-16周}      二,1,2,4,16,null
         // {第2-10周|3节/周}            null,null,null,2,10,3节/周
         // 周二第1,2节{第4-16周|双周}   二,1,2,4,16,双周
         // 周二第1节{第4-16周}            二,1,null,4,16,null
         // 周二第1节{第4-16周|双周}         二,1,null,4,16,双周
         // str格式如上,这里只是简单考虑每个课都只有两节课,实际上有三节和四节,模式就要改动,其他匹配模式请自行修改
  
         String reg = 周?(.)?第?(\d{ 1 , 2 })?,?(\d{ 1 , 2 })?节?\{第(\d{ 1 , 2 })-(\d{ 1 , 2 })周\|?((.*周))?\};
  
         String splitPattern =
;
         String[] temp = sub.split(splitPattern);
         Pattern pattern = Pattern.compile(reg);
         Matcher matcher = pattern.matcher(temp[ 1 ]);
         matcher.matches();
         Course course = new Course();
         //课程开始学年
         course.setStartYear(startYear);
         //课程结束学年
         course.setEndYear(endYear);
         //课程学期
         course.setSemester(semester);
  
         //课程名
         course.setCourseName(temp[ 0 ]);
         //课程时间,冗余字段
         course.setCourseTime(temp[ 1 ]);
         //教师
         course.setTeacher(temp[ 2 ]);
  
         try {
             // 数组可能越界,即没有教室
             course.setClasssroom(temp[ 3 ]);
         } catch (ArrayIndexOutOfBoundsException e) {
             course.setClasssroom(无教室);
         }
         //周几,可能为空,此时使用传进来的值
         if ( null != matcher.group( 1 )){
             course.setDayOfWeek(getDayOfWeek(matcher.group( 1 )));
         } else {
             course.setDayOfWeek(getDayOfWeek(week+));
         }
         //课程开始节数,可能为空,此时使用传进来的值
         if ( null != matcher.group( 2 )){
             course.setStartSection(Integer.parseInt(matcher.group( 2 )));
         } else {
             course.setStartSection(startSection);
         }
  
         //课程结束时的节数,可能为空,此时使用传进来的值
         if ( null != matcher.group( 3 )){
             course.setEndSection(Integer.parseInt(matcher.group( 3 )));
         } else {
             course.setEndSection(endSection);
         }
  
         //起始周
         course.setStartWeek(Integer.parseInt(matcher.group( 4 )));
         //结束周
         course.setEndWeek(Integer.parseInt(matcher.group( 5 )));
         //单双周
         String t = matcher.group( 6 );
         setEveryWeekByChinese(t, course);
         save(course);
         return course;
     }
  
  
  
  
     /**
      * 提取课程格式,可能包含多节课
      * @param str
      * @param startYear
      * @param endYear
      * @param semester
      * @param week
      * @param startSection
      * @param endSection
      * @return
      */
     private int splitCourse(String str, int startYear, int endYear, int semester, int week, int startSection,
             int endSection) {
         String pattern =
 
;
         String[] split = str.split(pattern);
         if (split.length > 1 ) { // 如果大于一节课
             for ( int i = 0 ; i < split.length; i++) {
                 if (!(split[i].startsWith(
) && split[i].endsWith(
))) {
                     storeCourseByResult(split[i], startYear,endYear,semester,week, startSection,
                             endSection); // 保存单节课
                 } else {
                     //
文化地理(网络课程)
周日第 10 节{第 17 - 17 周}
李宏伟
 
                     // 以上格式的特殊处理,此种格式在没有教师的情况下产生,即教室留空后
依旧存在
                     int brLength =
.length();
                     String substring = split[i].substring(brLength,
                             split[i].length() - brLength);
                     storeCourseByResult(substring, startYear,endYear,semester,week, startSection,
                             endSection); // 保存单节课
                 }
             }
             return split.length;
         } else {
             storeCourseByResult(str, startYear,endYear,semester,week, startSection, endSection); // 保存
             return 1 ;
         }
     }
  
     /**
      * 填充map,获取周几,第几节课至第几节课
      * @param childColumn
      * @param map
      * @param i
      * @return 周几
      */
     public static int fillMap(Element childColumn, int map[][], int i) {
         //这个函数的作用自行领悟,总之就是返回周几,也是无意中发现的,于是就这样获取了,作用是双重保障,因为有些课事无法根据正则匹配出周几第几节到第几节
         boolean hasAttr = childColumn.hasAttr(rowspan);
         int week = 0 ;
         if (hasAttr) {
             for ( int t = 0 ; t < map[ 0 ].length; t++) {
                 if (map[i][t] == 0 ) {
                     int r = Integer.parseInt(childColumn.attr(rowspan));
                     for ( int l = 0 ; l < r; l++) {
                         map[i + l][t] = 1 ;
                     }
                     week = t + 1 ;
                     break ;
                 }
             }
  
         } else {
             if (childColumn.childNodes().size() > 1 ) {
                 childColumn.attr(rowspan, 1 );
             }
             for ( int t = 0 ; t < map[ 0 ].length; t++) {
                 if (map[i][t] == 0 ) {
                     map[i][t] = 1 ;
                     week = t + 1 ;
                     break ;
                 }
             }
         }
         return week;
     }
     /**
      * 设置单双周
      * @param week
      * @param course
      */
     public void setEveryWeekByChinese(String week, Course course) {
         // 1代表单周,2代表双周
         if (week != null ) {
             if (week.equals(单周))
                 course.setEveryWeek( 1 );
             else if (week.equals(双周))
                 course.setEveryWeek( 2 );
         }
         // 默认值为0,代表每周
     }
  
     /**根据中文数字一,二,三,四,五,六,日,转换为对应的阿拉伯数字
      * @param day
      * @return int
      */
     public int getDayOfWeek(String day) {
         if (day.equals(一))
             return 1 ;
         else if (day.equals(二))
             return 2 ;
         else if (day.equals(三))
             return 3 ;
         else if (day.equals(四))
             return 4 ;
         else if (day.equals(五))
             return 5 ;
         else if (day.equals(六))
             return 6 ;
         else if (day.equals(日))
             return 7 ;
         else
             return 0 ;
     }


以上代码是提取课程的关键代码,课程的格式是在一个table里,tr里有很多td,td里就是课程,一个td里可能不止一节课

\

td有rowspan属性,代表占了几行,2代表占了两行,也就是两节课,有些课不是两节课的,rowspan的值也就对应改变,课程的节数有1,2,3,4节都有。我们可以根据课程的时间提取该课程是周几上课,第几节课到第几节课,有了这些信息就可以在界面上显示出来了,但是,有些格式,如第2-10周|3节/周是没办法提取时间的,这时候就用一定的技巧提取它,这里使用了fillmap函数对一个7*12的数组进行填充,原理是扫描一行,如果具有rowspan值,则填充该行该列为1,如果rowspan大于等于2,则该列下面几行对应的列也填充为1,等到扫描下一行的时候,该位置不会有课程,且不会有td,则如果是一个空td,则填充该行第一个为0的位置。技巧有点难以理解,具体细节稍微自己琢磨领悟下,这样课程的信息就都提取出来了,当然提取方式不止一种。这种方法也不一定能提取所有格式的课程。

提取完毕后进行显示,本来呢是使用LinearLayout简单达到超级课程表的效果的,后来稍微暴力的使用了下自定义ViewGroup,注意了,这个自定义ViewGroup不具有现实使用意义,只是为了展示效果,里面的代码都太暴力了。。。所以看过一遍就无视吧,简直不忍直视

首先是自定义属性

?
1
2
3
4
5
6
7
8
9
10
<!--?xml version= 1.0 encoding=utf- 8 ?-->
<resources><linknode><linknode>
     <declare-styleable name= "CourseView" >
         
         
         
         
     </attr></attr></attr></attr></declare-styleable>
     
</linknode></linknode></resources>
然后是课程自定义View,继承Button,增加一些课程信息而已。

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package cn.lizhangqu.kb.view;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.widget.Button;
import cn.lizhangqu.kb.R;
 
public class CourseView extends Button {
     private int courseId;
     private int startSection;
     private int endSection;
     private int weekDay;
 
     public CourseView(Context context) {
         this (context, null );
     }
 
     public CourseView(Context context, AttributeSet attrs) {
         this (context, attrs, 0 );
     }
     public CourseView(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
         TypedArray array=context.obtainStyledAttributes(attrs, R.styleable.CourseView);
         courseId = array.getInt(R.styleable.CourseView_courseId, 0 );
         startSection=array.getInt(R.styleable.CourseView_startSection, 0 );
         endSection=array.getInt(R.styleable.CourseView_endSection, 0 );
         weekDay=array.getInt(R.styleable.CourseView_weekDay, 0 );
         array.recycle();
     }
     public int getCourseId() {
         return courseId;
     }
 
     public void setCourseId( int courseId) {
         this .courseId = courseId;
     }
 
     public int getStartSection() {
         return startSection;
     }
 
     public void setStartSection( int startSection) {
         this .startSection = startSection;
     }
 
     public int getEndSection() {
         return endSection;
     }
 
     public void setEndSection( int endSection) {
         this .endSection = endSection;
     }
     public int getWeek() {
         return weekDay;
     }
 
     public void setWeek( int week) {
         this .weekDay = week;
     }
}
最后是自定义布局,简单暴力,注意了,这个布局没有处理重复时间的课程,也就是说没有处理单双周的情况,只是用来简单显示

 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package cn.lizhangqu.kb.view;
 
import java.util.ArrayList;
import java.util.List;
 
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
 
public class CourseLayout extends ViewGroup {
     private List<courseview><linknode><linknode> courses = new ArrayList<courseview>();
     private int width; //布局宽度
     private int height; //布局高度
     private int sectionHeight; //每节课高度
     private int sectionWidth; //每节课宽度
     private int sectionNumber = 12 ; //一天的节数
     private int dayNumber = 7 ; //一周的天数
     private int pideWidth = 2 ; //分隔线宽度,dp
     private int pideHeight = 2 ; //分隔线高度,dp
     public CourseLayout(Context context) {
         this (context, null );
     }
 
     public CourseLayout(Context context, AttributeSet attrs) {
         this (context, attrs, 0 );
     }
 
     public CourseLayout(Context context, AttributeSet attrs, int defStyle) {
         super (context, attrs, defStyle);
         width = getScreenWidth(); //默认宽度全屏
         height = dip2px( 600 ); //默认高度600dp
         pideWidth = dip2px( 2 ); //默认分隔线宽度2dp
         pideHeight = dip2px( 2 ); //默认分隔线高度2dp
     }
 
     @Override
     protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec) {
         setMeasuredDimension(width, height);
     }
 
     @Override
     protected void onLayout( boolean changed, int l, int t, int r, int b) {
         courses.clear(); //清除
         sectionHeight = (getMeasuredHeight() - pideWidth * sectionNumber)/ sectionNumber; //计算每节课高度
         sectionWidth = (getMeasuredWidth() - pideWidth * dayNumber)/ dayNumber; //计算每节课宽度
 
         int count = getChildCount(); //获得子控件个数
         for ( int i = 0 ; i < count; i++) {
             CourseView child = (CourseView) getChildAt(i);
             courses.add(child); //增加到list中
             
 
             int week = child.getWeek(); //获得周几
             int startSection = child.getStartSection(); //开始节数
             int endSection = child.getEndSection(); //结束节数
 
             int left = sectionWidth * (week - 1 ) + (week) * pideWidth; //计算左边的坐标
             int right = left + sectionWidth; //计算右边坐标
             int top = sectionHeight * (startSection - 1 ) + (startSection) * pideHeight; //计算顶部坐标
             int bottom = top + (endSection - startSection + 1 ) * sectionHeight+ (endSection - startSection) * pideHeight; //计算底部坐标
 
             child.layout(left, top, right, bottom);
         }
     }
 
     public int dip2px( float dip) {
         float scale = getContext().getResources().getDisplayMetrics().density;
         return ( int ) (dip * scale + 0 .5f);
     }
 
     public int getScreenWidth() {
         WindowManager manager = (WindowManager) getContext().getSystemService(
                 Context.WINDOW_SERVICE);
         DisplayMetrics displayMetrics = new DisplayMetrics();
         manager.getDefaultDisplay().getMetrics(displayMetrics);
         return displayMetrics.widthPixels;
     }
 
}
</courseview></linknode></linknode></courseview>

使用控件,记得增加命名空间

 

 

?
1
2
3
4
5
6
7
8
9
<linearlayout android:layout_height= "match_parent" android:layout_width= "match_parent" android:orientation= "vertical" tools:context= "${relativePackage}.${activityClass}" xmlns:android= "https://schemas.android.com/apk/res/android" xmlns:custom= "https://schemas.android.com/apk/res/cn.lizhangqu.kb" xmlns:tools= "https://schemas.android.com/tools" ><linknode><linknode>
 
     <linearlayout android:id= "@+id/weekend" android:layout_height= "30dp" android:layout_width= "match_parent" android:orientation= "horizontal" ><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周一" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周二" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周三" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周四" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周五" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周六" android:textcolor= "#2e94da" android:textsize= "12sp" ></button><button android:background= "@drawable/kb0" android:layout_height= "match_parent" android:layout_weight= "1" android:layout_width= "0dp" android:text= "周日" android:textcolor= "#2e94da" android:textsize= "12sp" >
     
     <scrollview android:id= "@+id/scrollview" android:layout_height= "wrap_content" android:layout_width= "wrap_content" android:scrollbars= "none" >
 
         <cn.lizhangqu.kb.view.courselayout android:background= "#e8e8e8" android:id= "@+id/courses" android:layout_height= "wrap_content" android:layout_width= "wrap_content" >
         </cn.lizhangqu.kb.view.courselayout>
     </scrollview></button></linearlayout></linknode></linknode></linearlayout>


 

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package cn.lizhangqu.kb.activity;
 
import java.util.List;
 
import android.app.Activity;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.Gravity;
import cn.lizhangqu.kb.R;
import cn.lizhangqu.kb.model.Course;
import cn.lizhangqu.kb.service.CourseService;
import cn.lizhangqu.kb.util.CommonUtil;
import cn.lizhangqu.kb.view.CourseLayout;
import cn.lizhangqu.kb.view.CourseView;
 
/**
  * @author lizhangqu
  * @date 2015-2-1
  */
public class CourseActivity extends Activity {
 
     //某节课的背景图,用于随机获取
     private int [] bg={R.drawable.kb1,R.drawable.kb2,R.drawable.kb3,R.drawable.kb4,R.drawable.kb5,R.drawable.kb6,R.drawable.kb7};
     private CourseService courseService;
     private CourseLayout layout;
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.activity_course);
         initValue();
         initView();
     }
 
     /**
      * 初始化变量
      */
     private void initValue() {
         courseService=CourseService.getCourseService();
     }
     /**
      * 初始化视图
      */
     private void initView() {
         //这里有逻辑问题,只是简单的显示了下数据,数据并不一定是显示在正确位置
         //课程可能有重叠
         //课程可能有1节课的,2节课的,3节课的,因此这里应该改成在自定义View上显示更合理
         List<course><linknode><linknode> courses=courseService.findAll(); //获得数据库中的课程
         layout=(CourseLayout) findViewById(R.id.courses);
         Course course= null ;
         //循环遍历
         for ( int i = 0 ; i < courses.size(); i++) {
             course=courses.get(i);
             CourseView view= new CourseView(getApplicationContext());
             view.setCourseId(course.getId());
             view.setStartSection(course.getStartSection());
             view.setEndSection(course.getEndSection());
             view.setWeek(course.getDayOfWeek());
             int bgRes=bg[CommonUtil.getRandom(bg.length- 1 )]; //随机获取背景色
             view.setBackgroundResource(bgRes);
             view.setText(course.getCourseName()+@+course.getClasssroom());
             view.setTextColor(Color.WHITE);
             view.setTextSize( 12 );
             view.setGravity(Gravity.CENTER);
             layout.addView(view);
             }
     }
}
</linknode></linknode></course>

背景图使用的是shape,这里贴出其中一个,其余的就只是颜色不同

 

 

?
1
2
3
4
5
6
7
<!--?xml version= 1.0 encoding=utf- 8 ?-->
<shape android:shape= "rectangle" xmlns:android= "https://schemas.android.com/apk/res/android" ><linknode><linknode>
     <solid android:color= "#ef9ea0/" >
     <stroke android:color= "#ef9ea0" android:width= "1dp" >
     <corners android:radius= "5dp/" >
  
</corners></stroke></solid></linknode></linknode></shape>

至此,超级课程表的一键提取课表功能就完成了。显示最终效果见下方

 

 

\

整个过程可简单概括为抓包分析,数据提取,数据显示,其中关键的一步就是数据的提取。这个过程中有个注意点就是抓课程数据的时候header请求头信息里的referer信息请务必设置为登录成功后的网址,即https://***.***.***.***/xs_main.aspx?xh=XH,否则抓数据的时候页面会被循环重定向,将抓不到数据,程序也会报异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值