@Andream
2017-12-20T14:05:56.000000Z
字数 3206
阅读 1317
课程表开发日志
个人信息、课表、考试、成绩四个部分的数据已经通过puppeteer抓取到服务端了,但是有个很大的问题:速度太慢。尽管已经通过直接访问url等方式绕开了一些按钮,但还是发出了大量的网络请求,其中有很大一部分是获取图片、css这些数据。这些数据只是用于页面渲染,并不是我感兴趣的数据,能不能不发送这些请求呢?这样能节省很多资源。
答案是肯定的,puppeteer提供了拦截Request的函数:
page.setRequestInterception(true);
经过统计,在没有拦截Request的时候,完成一次完整的数据抓取,需要81次网络请求,耗时15.906秒。
在初步拦截了png, jpg, css, gif, ico, MAINFRM.aspx之后,发送了49次网络请求,拦截了24次,实际发送25次,耗时12.614秒
接下来把未拦截的url打印出来,进一步分析优化:
// 登录http://jxgl.cqu.edu.cn/_data/index_login.aspxhttp://jxgl.cqu.edu.cn/js/md5.jshttp://jxgl.cqu.edu.cn/_data/index_login.aspxhttp://jxgl.cqu.edu.cn/js/md5.js// 个人信息http://jxgl.cqu.edu.cn/xsxj/Stu_MyInfo_RPT.aspxhttp://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.js// 课表http://jxgl.cqu.edu.cn/znpk/Pri_StuSel.aspxhttp://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.jshttp://jxgl.cqu.edu.cn/XSCJ/Private/ind_PrintSet.jshttp://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspxhttp://jxgl.cqu.edu.cn/js/Print.jshttp://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspx// 考试http://jxgl.cqu.edu.cn/KSSW/stu_ksap.aspxhttp://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.jshttp://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspxhttp://jxgl.cqu.edu.cn/KSSW/Private/list_xnxqkslc.aspx?id=20170&wd=220&vP=xnxqkslc&vT=stuhttp://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspxhttp://jxgl.cqu.edu.cn/_help/Sorry.aspx?str=NO_DATAhttp://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspxhttp://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspxhttp://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx// 成绩http://jxgl.cqu.edu.cn/xscj/Stu_MyScore.aspxhttp://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.jshttp://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspxhttp://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspxtotal request count: 49aborted req count: 24time in millons: 12614
分析发现ind_HTML_hr.js和ind_PrintSet.js只和界面有关,也拦截了,最后减少到19个请求。但是时间并没有减少多少,还是需要13秒左右。
这四个操作是同步进行的。但实际上这四个操作并没有前后顺序要求,完全可以异步进行。将await的同步代码很容易地改写为Promise的异步代码:
parseInfo(page).then(info => {console.log(info);});browser.newPage().then(page => {return parseTable(page);}).then(table => {console.log(table);});browser.newPage().then(page => {return parseExam(page);}).then(examTable => {console.log(examTable);});browser.newPage().then(page => {return parseGrade(page);}).then(gradeTable => {console.log(gradeTable);});
这次只要5秒就能完成同步操作,之后只需等待四个操作完成回调。实验结果显示,最慢的一个操作也在9秒左右完成了。比起最初了15秒,减少了40%。
time in login: 5986time in parseInfo: 6361time in parseTable: 8418time in parseGrade: 9163time in parseExam: 9514
不让课程查询页面弹出教材弹框:
课程页面在onload的时候会调用popOpen函数,打开教材页面,这是无谓的消耗,不能让他打开啊!
一开始想要覆盖popOpen方法,使用了
page.waitForFunction('popOpen', () => {function popOpen(){}});
但并不奏效,不知为何。后来:
禁用JavaScript:
await page.setJavaScriptEnabled(false);await page.goto(HOST + '/znpk/Pri_StuSel.aspx');// 选择排序方式:按课程/环节(0) or 按时间(1)await page.select('select[name=px]', '0');await page.select('select[name=Sel_XNXQ]', '20170');await page.$eval(':root', root => { // 选择学期:如2017-2018第一学期:20170; 2017-2018第二学期20171// 检索form.action="Pri_StuSel_rpt.aspx";form.method="post";form.target="frmRpt";form.submit();});
大功告成!现在不到8秒就能完成四个操作。
time in login: 4373time in parseInfo: 4766time in parseTable: 5766time in parseGrade: 7524time in parseExam: 7916
尝到了异步的甜头,之后会考虑进一步重构代码,使得其尽可能的异步运行。
下一步考虑保留用户的Cookie,这样还能省下登录的4秒。虽然Cookie保持的时间也就个把小时,但这个效率提升的还是合算的。
最后希望得到的效果是能在3秒内完成所有解析工作。这样比起完全使用网络请求抓取数据,不仅速度相当,代码也更简洁易懂,真是极好滴~
明天任务:把Table数据解析完整,这样API功能就全了,就差存储到数据库里了。