[关闭]
@Andream 2017-12-20T22:05:56.000000Z 字数 3206 阅读 1150

拦截不必要的网络请求

课程表开发日志


个人信息、课表、考试、成绩四个部分的数据已经通过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打印出来,进一步分析优化:

  1. // 登录
  2. http://jxgl.cqu.edu.cn/_data/index_login.aspx
  3. http://jxgl.cqu.edu.cn/js/md5.js
  4. http://jxgl.cqu.edu.cn/_data/index_login.aspx
  5. http://jxgl.cqu.edu.cn/js/md5.js
  6. // 个人信息
  7. http://jxgl.cqu.edu.cn/xsxj/Stu_MyInfo_RPT.aspx
  8. http://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.js
  9. // 课表
  10. http://jxgl.cqu.edu.cn/znpk/Pri_StuSel.aspx
  11. http://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.js
  12. http://jxgl.cqu.edu.cn/XSCJ/Private/ind_PrintSet.js
  13. http://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspx
  14. http://jxgl.cqu.edu.cn/js/Print.js
  15. http://jxgl.cqu.edu.cn/znpk/Pri_StuSel_rpt.aspx
  16. // 考试
  17. http://jxgl.cqu.edu.cn/KSSW/stu_ksap.aspx
  18. http://jxgl.cqu.edu.cn/include/scr/ind_HTML_hr.js
  19. http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
  20. http://jxgl.cqu.edu.cn/KSSW/Private/list_xnxqkslc.aspx?id=20170&wd=220&vP=xnxqkslc&vT=stu
  21. http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
  22. http://jxgl.cqu.edu.cn/_help/Sorry.aspx?str=NO_DATA
  23. http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
  24. http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
  25. http://jxgl.cqu.edu.cn/KSSW/stu_ksap_rpt.aspx
  26. // 成绩
  27. http://jxgl.cqu.edu.cn/xscj/Stu_MyScore.aspx
  28. http://jxgl.cqu.edu.cn/include/Scr/ind_HTML_hr.js
  29. http://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspx
  30. http://jxgl.cqu.edu.cn/xscj/Stu_MyScore_rpt.aspx
  31. total request count: 49
  32. aborted req count: 24
  33. time in millons: 12614

分析发现ind_HTML_hr.jsind_PrintSet.js只和界面有关,也拦截了,最后减少到19个请求。但是时间并没有减少多少,还是需要13秒左右。

接下来还能优化吗?可以利用JS异步执行的能力!

这四个操作是同步进行的。但实际上这四个操作并没有前后顺序要求,完全可以异步进行。将await的同步代码很容易地改写为Promise的异步代码:

  1. parseInfo(page).then(info => {
  2. console.log(info);
  3. });
  4. browser.newPage().then(page => {
  5. return parseTable(page);
  6. }).then(table => {
  7. console.log(table);
  8. });
  9. browser.newPage().then(page => {
  10. return parseExam(page);
  11. }).then(examTable => {
  12. console.log(examTable);
  13. });
  14. browser.newPage().then(page => {
  15. return parseGrade(page);
  16. }).then(gradeTable => {
  17. console.log(gradeTable);
  18. });

这次只要5秒就能完成同步操作,之后只需等待四个操作完成回调。实验结果显示,最慢的一个操作也在9秒左右完成了。比起最初了15秒,减少了40%。

  1. time in login: 5986
  2. time in parseInfo: 6361
  3. time in parseTable: 8418
  4. time in parseGrade: 9163
  5. time in parseExam: 9514

还有一个恼人的问题:弹框

不让课程查询页面弹出教材弹框:
课程页面在onload的时候会调用popOpen函数,打开教材页面,这是无谓的消耗,不能让他打开啊!

一开始想要覆盖popOpen方法,使用了

  1. page.waitForFunction('popOpen', () => {
  2. function popOpen(){}
  3. });

但并不奏效,不知为何。后来:

禁用JavaScript:

  1. await page.setJavaScriptEnabled(false);
  2. await page.goto(HOST + '/znpk/Pri_StuSel.aspx');
  3. // 选择排序方式:按课程/环节(0) or 按时间(1)
  4. await page.select('select[name=px]', '0');
  5. await page.select('select[name=Sel_XNXQ]', '20170');
  6. await page.$eval(':root', root => { // 选择学期:如2017-2018第一学期:20170; 2017-2018第二学期20171
  7. // 检索
  8. form.action="Pri_StuSel_rpt.aspx";
  9. form.method="post";
  10. form.target="frmRpt";
  11. form.submit();
  12. });

大功告成!现在不到8秒就能完成四个操作。

  1. time in login: 4373
  2. time in parseInfo: 4766
  3. time in parseTable: 5766
  4. time in parseGrade: 7524
  5. time in parseExam: 7916

再进一步?

尝到了异步的甜头,之后会考虑进一步重构代码,使得其尽可能的异步运行。

下一步考虑保留用户的Cookie,这样还能省下登录的4秒。虽然Cookie保持的时间也就个把小时,但这个效率提升的还是合算的。

最后希望得到的效果是能在3秒内完成所有解析工作。这样比起完全使用网络请求抓取数据,不仅速度相当,代码也更简洁易懂,真是极好滴~

明天任务:把Table数据解析完整,这样API功能就全了,就差存储到数据库里了。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注