基于puppeteer模拟登陆抓取页面

关于热图

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

ptengine点击热图

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

热图主流的实现方式

通常实现热图显示须要通过以下阶段:html

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

两种方式各有各的优缺点,首先第一种直接嵌入用户网站,这个有必定的限制条件,好比若是用户网站为了防止iframe劫持,不容许iframe嵌套(设置meta X-FRAME-OPTIONS 为sameorgin 或者直接设置http header ,甚至直接经过js来控制java

if(window.top !== window.self){ window.top.location = window.location;}

),这种状况下就须要客户网站作一部分工做才能够被分析工具的iframe加载,使用起来不必定那么方便,由于并非全部的须要检测分析的网站用户均可以管理网站的。浏览器

第二种方式,直接抓取网站页面到本地服务器,而后浏览的是本机服务器上抓取的页面,这种状况下页面已通过来了,咱们就能够随心所欲了,首先咱们绕过了X-FRAME-OPTIONS 为sameorgin的问题,只须要解决js控制的问题,对于抓取的页面来讲,咱们能够经过特殊的对应来处理(好比移除对应的js控制,或者添加咱们本身的js);可是这种方式也有不少的不足:一、没法抓取spa页面,没法抓取须要用户登陆受权的页面,没法抓取用户设置了白明白的页面等等。服务器

两种方式都存在https 和 http资源因为同源策略引发的另外一个问题,https站没法加载http资源,因此若是为了最好的兼容性,热图分析工具须要被应用http协议,固然具体能够根据访问的客户网站而具体分站优化。async

抓取网站页面如何优化

这里咱们针对抓取网站页面遇到的问题基于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 方法可以解决这个问题,可是网站不一样,处理方式就会不一样,没法复用。
相关文章
相关标签/搜索