@Andream
2017-12-20T22:05:56.000000Z
字数 3206
阅读 1146
课程表开发日志
个人信息、课表、考试、成绩四个部分的数据已经通过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.aspx
http://jxgl.cqu.edu.cn/js/md5.js
http://jxgl.cqu.edu.cn/_data/index_login.aspx
http://jxgl.cqu.edu.cn/js/md5.js
// 个人信息
http://jxgl.cqu.edu.cn/xsxj/Stu_MyInfo_RPT.aspx
http://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.js
// 课表
http://jxgl.cqu.edu.cn/znpk/Pri_StuSel.aspx
http://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.js
http://jxgl.cqu.edu.cn/XSCJ/Private/ind_PrintSet.js
http://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspx
http://jxgl.cqu.edu.cn/js/Print.js
http://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspx
// 考试
http://jxgl.cqu.edu.cn/KSSW/stu_ksap.aspx
http://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.js
http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
http://jxgl.cqu.edu.cn/KSSW/Private/list_xnxqkslc.aspx?id=20170&wd=220&vP=xnxqkslc&vT=stu
http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
http://jxgl.cqu.edu.cn/_help/Sorry.aspx?str=NO_DATA
http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
// 成绩
http://jxgl.cqu.edu.cn/xscj/Stu_MyScore.aspx
http://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.js
http://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspx
http://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspx
total request count: 49
aborted req count: 24
time 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: 5986
time in parseInfo: 6361
time in parseTable: 8418
time in parseGrade: 9163
time 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: 4373
time in parseInfo: 4766
time in parseTable: 5766
time in parseGrade: 7524
time in parseExam: 7916
尝到了异步的甜头,之后会考虑进一步重构代码,使得其尽可能的异步运行。
下一步考虑保留用户的Cookie,这样还能省下登录的4秒。虽然Cookie保持的时间也就个把小时,但这个效率提升的还是合算的。
最后希望得到的效果是能在3秒内完成所有解析工作。这样比起完全使用网络请求抓取数据,不仅速度相当,代码也更简洁易懂,真是极好滴~
明天任务:把Table数据解析完整,这样API功能就全了,就差存储到数据库里了。