实战Puppeteer-使用原生代码与页面交互

yumo6662周前 (04-23)技术文章10

概述

我们前面已经介绍不少Puppeteer实现操控页面对象的例子。现在我们应该停下来,好好思考一下,Puppeteer实现的原理是什么?

我们不需要通过Puppeteer源码来理解Puppeteer,只需要通过Puppeteer提供的API,我们透过这些API来探索性分析其实现机理。

原理初探

Puppeteer提供的对象模型中,最核心的对象就是page对象和EventHandler对象。我们仔细思考下,page对象可以看做是整个网页在浏览器中构建出的dom树一个包装对象,因此page对象有点类似JavaScript中的document,而EventHandler则是整个dom树按照某种筛选条件形成的dom节点包装对象。

我们通过page对象和EventHandler对象提供的方法也可以感知其对象模型设计的基本背景。

而从我们编写代码角度,我们要基于这些对象编程,就需要熟记这些对象模型提供的方法。我们对原生的网页编程技术更加熟悉的话,Puppeteer提供一种机制,让我们直接进入到原生的编程模式。

page对象和EventHandler对象都提供了通过原生编程的模式支持。

那么Puppeteer为何会提供这样的机制出来?我的理解可能是这样的:

  • 我们大多数人对原生的网页编程非常熟悉,如果能够直接使用原生编程,对于这些开发者而言,就显得如鱼得水。
  • 既然page和eventhandler都是对dom数据的对象包装,包装出来的对象提供的各种能力可能就存在欠缺,索性就开放一个机制,让开发者直接在原生态下进行操作dom树。这样既可以满足开发者需求,又可能确保puppeteer框架稳定及演进。


page原生编程

page提供一个方法evaluate,允许开发者直接进入整个网页中,采取原生的js编程技术,对整个网页的dom对象进行灵活的操作。

下面我们先给出一个简单的例子,来说明下这个方法的基本使用:

let page = await browser.newPage()
await page.goto("http://www.xxx.com")

let r = await page.evaluate(()=>{
  //在这个方法体内,实际上就进入该网页的<script></script>环境中,我们可以对整个网页进行操作
  //如我们对网页背景色进行设置
  document.body.backgroundColor = "red"
})

我们可以把page提供的这个方法看做是一个门,进入这个门后,我们就可以按照传统的原生js编程技术,对整个网页的dom树进行操作,这个就和我们传统的网页编程一样了。

当然,有一点点区别还是存在的。

  • 第一个就是这个方法体可以有返回值,如果没有显式返回值,就认为返回值为null,如上述代码中,evaluate执行完成后,r=null
  • evaluate参数是一个函数,这个函数可以是传统的函数定义,也可以采取闭包模式。闭包好处是可以直接引入外部变量,不用显式提供函数参数值,如果是传统的函数定义,则需要传递函数参数值。我们通过如下两段代码来说明:
let page = await browser.newPage()
await page.goto("http://www.xxx.com")
let myColor = "red"
let r = await page.evaluate(()=>{
  //在这个方法体内,实际上就进入该网页的<script></script>环境中,我们可以对整个网页进行操作
  //如我们对网页背景色进行设置
  document.body.backgroundColor = myColor   //这里引用外部变量
})

上述代码说明闭包函数定义的优势地方。

let page = await browser.newPage()
await page.goto("http://www.xxx.com")

let r = await page.evaluate((myColor)=>{
  //在这个方法体内,实际上就进入该网页的<script></script>环境中,我们可以对整个网页进行操作
  //如我们对网页背景色进行设置
  document.body.backgroundColor = myColor   //这里是函数参数变量
}, ‘red’) //注意这里的'red'就是前面定义的函数参数值,需要传递过去的。

当我们进入到原生的网页内部,我们可以利用js+css技术,对网页进行任意的操作,我们就不展开了,就举一个场景:一般网页打开后,会很长,尤其是新闻类网站,可能我们需要进行滚动,那么我们就可能利用window提供的scroll方法,操作网页进行滚动。

page还提供与此相关的几个方法,大家可以参考其官方文档,并利用自己搭建的学习环境来研究下。

EventHandler原生编程

与Page主要的差别是EventHandler是整个网页的dom子树,而后者是整个dom树。同样,EventHandler也提供机制让我们进入到这个dom子树,采取原生网页技术进行操作这个dom子树。

EventHandler主要使用$eval或$eval让开发者进入到原生编程模式。这两个方法page对象也提供的,

我们通过一个例子来说明一下,假设一个网页的dom结构如下:

<html>
  <head></head>
  <body>
    <form id="order">
      <input name="name" />
      <input name="age" />
      <input name="sex" />    
    </form>  
  </body>
</heml>

先给出一个$eval例子

let eh = await page.$("#order")  //eh就是一个EventHandler
await eh.$eval("input[name=name]",node=>{
  node.value = "小明"
})

我们前面介绍到,$选择器,返回一个满足此查询条件的dom节点,上述$eval第一个参数就是css选择器,将会选择第一个input节点,作为dom节点对象,作为$eval函数的第二个参数,也是一个函数的参数值,也就是下面函数的参数node的值进行传递:

node => {
  node.value = "小明"
}


$eval返回的是一个数组。我们还是从一个例子出发,来说明下:

let eh = await page.$("#order")  //eh就是一个EventHandler
await eh.$eval("input",nodes=>{
  nodes.map(node=>node.value = "小明")
})

上述dom子树中,有三个input节点。它们作为一个数组,传递给后续的那个闭包参数nodes。通过map操作,把所有的输入框中都输入“小明”。

最终的dom树是这样的:

<html>
  <head></head>
  <body>
    <form id="order">
      <input name="name" value="小明"/>
      <input name="age"  value="小明"/>
      <input name="sex"  value="小明"/>    
    </form>  
  </body>
</heml>

小结

page和eventhandler提供的这种原生编程模式,为我们操作dom打开自由之门,大家可以多做点小测验,加深理解这样原生操作模式能够带给我们怎样的好处。

相关文章

最好用的滚动条美化插件——jQuery.NiceScroll

nicescroll 滚动条插件是一个非常强大的基于 jQuery 的滚动条插件,不需要增加额外的css,几乎全浏览器兼容。ie6+,实现只需要一段代码,侵入性非常小,样式可完全自定义,支持触摸事件,...

20 个让人惊叹的 JavaScript 单行代码技巧,效率瞬间提升

掌握一些简洁有力的单行代码技巧可以大幅提高编码效率,让JavaScript代码更加优雅。分享 20 个实用且令人惊叹的 JavaScript 单行代码,可能为你的工作带来便利。1. 数组去重const...

小程序学习日志7:可滚动的视图区域组件(滚动条)

导读经过这段日子的学习,大家对小程序的了解多了多少呢?我们这段时间学了很多组件的用法,以及这些组件的属性和属性的合法值,图片、文字、块,还了解了不少的样式代码和小程序的基本知识。我们今天来讲一个可滚动...

WINCC如何制作滚动播放的文字

首先,我们先看一下文字播放的效果。如视频所示,文字呈跑马灯的形式进来左右来回滚动,那么在wincc中,如果制作这样的效果呢?带着赖工,学会它!第一步:首先在页面合适的位置,新建一个,静态文本,属性见图...

Selenium 滚动页面至元素可见的方法,看完直接可上手了!

今天为大家带来的内容是:Selenium 滚动页面至元素可见的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧!滚动页面  在...

selenium:操作滚动条的方法(8)

selenium支持几种操作滚动条的方法,主要介绍如下:使用ActionChains 类模拟鼠标滚轮操作使用函数ActionChains.send_keys发送按键Keys.PAGE_DOWN往下滑动...