第二次结对作业:班级成绩表

作业要求 班级成绩表
作业目标 通过网络爬虫来获得自己想要的信息,而并非读取本地的html文件
作业源代码 我们的码云仓库
林佳森 211806327
林俊威 211806329

时间记录

  • 代码129行
  • 分析时间1.5小时
  • 编码时间4小时
    本次作业相对轻松,因为在上次个人作业中,我们都已经熟悉了Jsoup的基本功能,当看到需要存储三个变量时,也立即想到了对象数组,就是IO流模块还不是很熟练,参考了我们上学期作业中的例子,也勉强做出来了,其它遇到的一些像获取的活动地址的再处理学号的获取存在退出云班课的人排序时,对象数组不能存在空对象等等难题,也被我们仔细地观察、分析,一一解决了。实现过程相对于上次个人作业来说,相对轻松,或许这就是结对编程的魅力吧。

结对感受

一回生,二回熟,你搬张椅子,我放首歌,结对编程开始了。我们相互讨论,提出彼此的想法,获得更好的方法。我们彼此分工,我来获取内容,并存储数据;你来对数据进行处理,并进行文件输出。我写完,轮到你写,结对编程,真快乐。


搭档评价

  • 我对于我搭档的评价:林俊威同学,现实中他唯唯诺诺,键盘上他重拳出击,与我在中国特色社会主义的领导下完成了此次作业,好!
  • 我搭档对于我的评价:林佳森同学代码写的又好,人长的又帅,以先富带动后富的精神,指导了我在完成作业过程中很多不太理解的地方,好!

需求分析

1、cookie的导入

  • 首先这次与第一次编程作业有些不同,上次作业中是由本地的html文件中获取,而本次中需要从在线的网站中获取,于是便需要导入cookie,好在有老师的发的网站中便能轻松完成。
  • 为了避免将“config.properties”乱放的情况,所以我们在这里使用了方法来获取它在项目中的路径来导入url和cookie
String path = Team.class.getResource("config.properties").getPath();
Properties config = new Properties();
config.load(new FileInputStream(path));
Document doc = Jsoup.connect(config.getProperty("url")).header("Cookie", config.getProperty("cookie")).timeout(10000).get();

2、从网页中获取课堂完成部分的div小块来获取每个课堂完成部分的网址并保存

  • 通过getElementsByClass("interaction-row")获得一个个活动的div小块,再通过contains("课堂完成")来获得课堂完成部分的所有div小块

    而我们需要的网址其实就在其中,也就是data-url中的内容
  • 获取data-url中的网址
    但其实从中取出来的网址是不能直接用的,眼尖的话,你可以来试试看找出下面这两个网址的区别哦
https://www.mosoteach.cn/web/index.php?c=interaction_homework&m=homework_result_list&clazz_course_id=9E603F91-4AF8-11EA-9C7F-98039B1848C6&id=2C12ADF1-FACD-2647-69C5-961046B2E302&order_item=group
https://www.mosoteach.cn/web/index.php?c=interaction_homework&m=homework_result_list&clazz_course_id=9E603F91-4AF8-11EA-9C7F-98039B1848C6&id=2C12ADF1-FACD-2647-69C5-961046B2E302&order_item=group

当你打开第一个网址时一定是如下图所示,它去了火星

而你打开第二个网址就会是正常的网址

这两个之中最关键的地方就在于“&”,应该把它改成“&”,才能正常打开,这也是我们一番观察之后才发现的,关于这个问题,我们猜想可能是编码问题
所以我们使用了正则表达式"data-url=\"(\\S+)\"",然后用replace()方法来替换成为正确的网址,并存入之前创建好的足够大的用来存放网址的数组

String url = sort(p, "data-url=\"(\\S+)\"");
webPages[webNum] = url.replace("&", "&");
webNum++;

此处的sort()为我们定义的用正则表达式来获取需要内容的方法,因为后面经常用到,就写成了方法,方便直接调用


3、遍历访问每个网站,并获取所有人的个人信息和经验值,并保存

  • 从数组中读取各个网址,再次通过Jsoup来获取我们需要的值

(康康这里!!!)

本来进行到这里很顺利,但当我们测试输出所获取的内容时,嗯?怎么缺斤少两了?云班课上的人怎么就变那么点了?
通过搜索,我们才知道原来是通过Jsoup所获取的内容一但超过1024KB,数据就只有预期得到数据的前1024KB字节了。

所以要在解析的地方加上一句
maxBodySize(0)

Document student = Jsoup.connect(webPages[i]).header("Cookie", config.getProperty("cookie")).timeout(10000).maxBodySize(0).get();
  • 同样,我们还是可以通过getElementsByClass()方法来区分开每一个人
Elements stuExp = student.getElementsByClass("homework-item");
  • 获取经验值,我们可以使用正则表达式
int exp = Integer.parseInt(sort(p, "(\\d+) 分</span>"));
  • 获取姓名,我们还是可以通过正则表达式
String name = sort(p, "color: #333;\">(\\S+)</span>");
  • 获取学号,最麻烦的来了,这也让我们头疼了好一阵子

    灾难发生了,前后都有换行符,这种情况,正则表达式,我们不会使用了
    而我们也想过使用"211"开头的学号来获取,结果看了眼云班课,心都凉了半截,有乱填学号的!!!

    但好在天无绝人之路,我们又发现了新的突破点

    再次调用getElementsByClass()来打破僵局,学号就是最后一个div下的内容,所以
p = p.getElementsByClass("member-message").first();
String id = p.select("div").last().text();

4、创建一个全局对象数组来存储学号、姓名、经验值

  • 创建一个对象数组类Student类,设置三个私有变量"姓名、学号、经验值",并自动生成各种方法,并写两种构造方法(无参数、带三个参数),重写equals()、toString()方法,并完成Comparable接口,方便后面排序
  • 获取成员数目(该数据在第一次作业中samll.html所在的那个网页),来改变数组的大小,否则后续排序时,会因为后面部分的空对象数组,而无法进行
    同样,这个成员数目,也不是特别好处理

    我们直接通过select方法来获取了“(73)”,就只要去掉这个括号就可以了
    正巧我们最近在学习的PHP中有一个方法substr()方法,通过联想,我们竟然真的在API中找到了类似方法substring()

    所以如下处理后,成员的数目就得到了
String memNum = doc.getElementById("menu-content-box").select("a").get(1).select("span").get(1).text();
stuNum = Integer.parseInt(memNum.substring(1, memNum.length() - 1));
stuList =new Student[stuNum];
  • 剩下的就是简单的存入数据了,但其中还是存在着一些问题,会有人退出云班课!!!而且每次作业中都会有他们原先的作业记录


    这也就意味着你获取的成员数会比能爬取获得出来的总人数来得少,会导致数组溢出。
    但写了那么久,我们实在不想换成集合来实现了。真要实现的话,我想那应该是我们中了彩票后,数钱数到无聊的时候,就会去做吧
    所以我们通过观察发现,在我们获得的数据中,退出的人是没有姓名的,因为不符合我们的正则表达式"color: #333;\">(\\S+)</span>"

    所以只需要在存储过程中加入一个if(!name.equals(""))判断,就可以过滤掉他们了。

5、处理出需要的数据,并输出保存成txt文件

  • 排序,只需要重写Student类中的compareTo()方法,再调用Arrays.sort()方法就可以完成了
public int compareTo(Student o) {
      int sort1 = o.exp-this.exp;
      int sort2 = sort1 == 0 ? this.id-o.id : sort1;
      return sort2;
}
  • 打印输出,通过PrintWriter类,写入,最后关闭,就可以在项目文件夹中生成一个“score.txt”文件了
File txt = new File("score.txt");
PrintWriter out = new PrintWriter(new FileWriter(txt));
String first = "最高经验值为:"+stuList[0].getExp()+","+"最低经验值为:"+stuList[stuNum-1].getExp()+","+"平均经验值为:"+aver/stuNum;
out.print(first+"\n");
for(Student stu:stuList)
      out.print(stu.toString()+"\n");
out.close();


我们班级中真的有经验值为0的人(下图为云班课的总经验值,非单独课堂完成部分的经验值)


优化过程

  • 在获取分中,如果按照我们之前的正则表达式,会获得助教评的分数,而非最终分数,正常时候,这两个是会相同的,但有时候是会存在不同的

    而优化也很简单只需要将我原本的正则表达式中的if改为while,这样,就能匹配到最终分数

作业小结

此次作业让我们收获了很多,不仅学会了如何从在线网站中获取我们需要的信息,还让我们好好地复习、巩固了以前的一些知识,也让我们看到
了自身的弊端——对于IO流的应用掌握情况几乎为0,我们也决定对这一方面的相关知识好好恶补一番,书到用时方恨少啊。


参考文献

导入cookie:https://www.cnblogs.com/jamaler/p/11645569.html
Jsoup获取响应内容不完整:https://blog.csdn.net/qq_40180411/article/details/103746271
JsoupAPI:https://jsoup.org/apidocs/org/jsoup/nodes/Element.html
jdk api 1.8(我们参考下载在本地的API,没法提供网址)

posted @ 2020-09-29 01:05  _DerrickHoudini  阅读(485)  评论(6编辑  收藏  举报