node基于puppeteer模拟登录抓取页面的实现


Posted in Javascript onMay 09, 2018

关于热图

在网站分析行业中,网站热图能够很好的反应用户在网站的操作行为,具体分析用户的喜好,对网站进行针对性的优化,一个热图的例子(来源于ptengine)

node基于puppeteer模拟登录抓取页面的实现

上图中能很清晰的看到用户关注点在那,我们不关注产品中热图的功能如何,本篇文章就热图的实现做一下简单的分析和总结。

热图主流的实现方式

一般实现热图显示需要经过如下阶段:
1.获取网站页面
2.获取经过处理后的用户数据
3.绘制热图
 本篇主要聚焦于阶段1来详细的介绍一下主流的在热图中获取网站页面的实现方式
4.使用iframe直接嵌入用户网站
5.抓取用户页面保存到本地,通过iframe嵌入本地资源(所谓本地资源这里认为是分析工具这一端)

两种方式各有各的优缺点

首先第一种直接嵌入用户网站,这个有一定的限制条件,比如如果用户网站为了防止iframe劫持,不允许iframe嵌套(设置meta X-FRAME-OPTIONS 为sameorgin 或者直接设置http header ,甚至直接通过js来控制if(window.top !== window.self){ window.top.location = window.location;} ),这种情况下就需要客户网站做一部分工作才可以被分析工具的iframe加载,使用起来不一定那么方便,因为并不是所有的需要检测分析的网站用户都可以管理网站的。

第二种方式,直接抓取网站页面到本地服务器,然后浏览的是本机服务器上抓取的页面,这种情况下页面已经过来了,我们就可以为所欲为了,首先我们绕过了X-FRAME-OPTIONS 为sameorgin的问题,只需要解决js控制的问题,对于抓取的页面来说,我们可以通过特殊的对应来处理(比如移除对应的js控制,或者添加我们自己的js);但是这种方式也有很多的不足:1、无法抓取spa页面,无法抓取需要用户登录授权的页面,无法抓取用户设置了白明白的页面等等。

两种方式都存在https 和 http资源由于同源策略引起的另一个问题,https站无法加载http资源,所以如果为了最好的兼容性,热图分析工具需要被应用http协议,当然具体可以根据访问的客户网站而具体分站优化。

抓取网站页面如何优化

这里我们针对抓取网站页面遇到的问题基于puppeteer做一些优化,提高抓取成功的概率,主要优化以下两种页面:

1.spa页面

spa页面在当前页算是主流了,但是它总所周知的是其对搜索引擎的不友好;通常的页面抓取程序其实就是一个简单的爬虫,其过程通常都是发起一个http get 请求到用户网站(应该是用户网站服务器)。这种抓取方式本身就会有问题问题,首先,直接请求的是用户服务器,用户服务器对非浏览器的agent 应该会有很多限制,需要绕过处理;其次,请求返回的是原始内容,需要在浏览器中通过js渲染的部分无法获取(当然,在iframe嵌入后,js执行还是会再一定程度上弥补这个问题),最后如果页面是spa页面,那么此时获取的只是模板,在热图中显示效果非常不友好。

针对这种情况,如果基于puppeteer来做,流程就变成了

puppeteer启动浏览器打开用户网站-->页面渲染-->返回渲染后结果,简单的用伪代码实现如下:

const puppeteer = require('puppeteer');

async getHtml = (url) =>{
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto(url);
  return await page.content();
}

这样我们拿到的内容就是渲染后的内容,无论页面的渲染方式如何(客户端渲染抑或服务端)

需要登录的页面

对于需要登录页面其实分为多种情况:

需要登录才可以查看页面,如果没有登录,则跳转到login页面(各种管理系统)

对于这种类型的页面我们需要做的就是模拟登录,所谓模拟登录就是让浏览器去登录,这里需要用户提供对应网站的用户名和密码,然后我们走如下的流程:

访问用户网站-->用户网站检测到未登录跳转到login-->puppeteer控制浏览器自动登录后跳转到真正需要抓取的页面,可用如下伪代码来说明:

const puppeteer = require("puppeteer");
async autoLogin =(url)=>{
   const browser = await puppeteer.launch();
   const page =await browser.newPage();
   await page.goto(url);
   await page.waitForNavigation();

   //登录
   await page.type('#username',"用户提供的用户名");
   await page.type('#password','用户提供的密码');

   await page.click('#btn_login');

  //页面登录成功后,需要保证redirect 跳转到请求的页面
   await page.waitForNavigation();

   return await page.content();
}

登录与否都可以查看页面,只是登录后看到内容会所有不同 (各种电商或者portal页面)

这种情况处理会比较简单一些,可以简单的认为是如下步骤:

通过puppeteer启动浏览器打开请求页面-->点击登录按钮-->输入用户名和密码登录 -->重新加载页面

基本代码如下图:

const puppeteer = require("puppeteer");
async autoLoginV2 =(url)=>{
   const browser = await puppeteer.launch();
   const page =await browser.newPage();
   await page.goto(url);

   await page.click('#btn_show_login');

   //登录
   await page.type('#username',"用户提供的用户名");
   await page.type('#password','用户提供的密码');

   await page.click('#btn_login');

  //页面登录成功后,是否需要reload 根据实际情况来确定
   await page.reload();

   return await page.content();
}

总结

明天总结吧,今天下班了。

补充(还昨天的债):基于puppeteer虽然可以很友好的抓取页面内容,但是也存在这很多的局限

1.抓取的内容为渲染后的原始html,即资源路径(css、image、javascript)等都是相对路径,保存到本地后无法正常显示,需要特殊处理(js不需要特殊处理,甚至可以移除,因为渲染的结构已经完成)

2.通过puppeteer抓取页面性能会比直接http get 性能会差一些,因为多了渲染的过程

3.同样无法保证页面的完整性,只是很大的提高了完整的概率,虽然通过page对象提供的各种wait 方法能够解决这个问题,但是网站不同,处理方式就会不同,无法复用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript Array.prototype.slice使用说明
Oct 11 Javascript
js修改input的type属性及浏览器兼容问题探讨与解决
Jan 23 Javascript
jquery实现checkbox 全选/全不选的通用写法
Feb 22 Javascript
JS实现让网页背景图片斜向移动的方法
Feb 25 Javascript
深入理解angularjs过滤器
May 25 Javascript
JavaScript中for循环的几种写法与效率总结
Feb 03 Javascript
Restify中接入Socket.io报Error:Can’t set headers的错误解决
Mar 28 Javascript
详解基于Node.js的微信JS-SDK后端接口实现代码
Jul 15 Javascript
javascript 日期相减-在线教程(附代码)
Aug 17 Javascript
Node.Js中实现端口重用原理详解
May 03 Javascript
vue 左滑删除功能的示例代码
Jan 28 Javascript
vue-model实现简易计算器
Aug 17 Javascript
nuxt框架中路由鉴权之Koa和Session的用法
May 09 #Javascript
jQuery简单实现的HTML页面文本框模糊匹配查询功能完整示例
May 09 #jQuery
JS验证输入的是否是数字及保留几位小数问题
May 09 #Javascript
javaScript强制保留两位小数的输入数校验和小数保留问题
May 09 #Javascript
node puppeteer(headless chrome)实现网站登录
May 09 #Javascript
JS中移除非数字最多保留一位小数
May 09 #Javascript
JS关于刷新页面的相关总结
May 09 #Javascript
You might like
PHP iconv 函数转gb2312的bug解决方法
2009/10/11 PHP
php学习之 认清变量的作用范围
2010/01/26 PHP
慎用preg_replace危险的/e修饰符(一句话后门常用)
2013/06/19 PHP
Yii框架在页面输出执行sql语句以方便调试的实现方法
2014/12/24 PHP
php通过strpos查找字符串出现位置的方法
2015/03/17 PHP
PHP数学运算与数据处理实例分析
2016/04/01 PHP
javascript学习笔记(十四) window对象使用介绍
2012/06/20 Javascript
Javascript MVC框架Backbone.js详解
2014/09/18 Javascript
不同编码的页面表单数据乱码问题解决方法
2015/02/15 Javascript
AngularJS折叠菜单实现方法示例
2017/05/18 Javascript
微信小程序获取用户openId的实现方法
2017/05/23 Javascript
vue.js实现简单轮播图效果
2017/10/10 Javascript
使用JS获取SessionStorage的值
2018/01/12 Javascript
基于mpvue的小程序项目搭建的步骤
2018/05/22 Javascript
微信小程序左滑删除功能开发案例详解
2018/11/12 Javascript
Nuxt.js之自动路由原理的实现方法
2018/11/21 Javascript
vue下拉菜单组件(含搜索)的实现代码
2018/11/25 Javascript
vscode下vue项目中eslint的使用方法
2019/01/13 Javascript
Vue中props的详解
2019/05/16 Javascript
详解element上传组件before-remove钩子问题解决
2020/04/08 Javascript
[02:16]DOTA2英雄基础教程 干扰者
2014/01/15 DOTA
Python设计模式中单例模式的实现及在Tornado中的应用
2016/03/02 Python
Python的Asyncore异步Socket模块及实现端口转发的例子
2016/06/14 Python
深入解答关于Python的11道基本面试题
2017/04/01 Python
Python smtplib实现发送邮件功能
2018/05/22 Python
如何利用Boost.Python实现Python C/C++混合编程详解
2018/11/08 Python
pandas实现将dataframe满足某一条件的值选出
2019/06/12 Python
详解Python中的编码问题(encoding与decode、str与bytes)
2020/09/30 Python
详解CSS3 弹性布局快速入门
2019/06/06 HTML / CSS
Skyscanner波兰:廉价航班
2017/11/07 全球购物
英国婴儿及儿童产品商店:TigerParrot
2019/03/04 全球购物
就业协议书的作用
2014/04/11 职场文书
《灰椋鸟》教学反思
2014/04/27 职场文书
2014年学习委员工作总结
2014/11/14 职场文书
2015大学迎新晚会策划书
2015/07/16 职场文书
pytorch 权重weight 与 梯度grad 可视化操作
2021/06/05 Python