puppeteer使用浅析

250 阅读2分钟

  今天来介绍一下puppeteer的使用。首先介绍一下puppeteer,我们可以用它来做网络爬虫和端到端测试。从下图我们可以看到它的实现原理:最底层是Headless Chrome,无头Chrome。这里说的“无头”其实是指没有UI交互。那么一个Chrome浏览器如果没有UI交互进程,我们又怎样来操作呢?就是靠CDP这一层,即Chrome DevTools protocol来操作Chrome的。而与Headless Chrome之间通过CDP连接的就是puppeteer这个库,而puppeteer这个库就是我们在node脚本中需要引入的。

截屏2022-05-24 下午10.57.50.png

  关于puppeteer的使用,我们先来安装:

npm i puppeteer

  这个命令会同时安装Chromium浏览器,也就是Chrome的内核版本。

  下面来看一下我们的node脚本,在脚本中我们通过puppeteer.launch()启动了一个浏览器,并且通过browser.newPage()启动了一个浏览器的进程,之后我们设定了视口的属性并且打开了一个百度的页面。之后,我们引入了selector.js文件,通过page.evaluateHandle()来在浏览器进程中执行引入的文件,在下面分析完selector.js之后,我们再回来看下面的逻辑:

const puppeteer = require('puppeteer');
const fs = require('fs');
const path = require('path');

let defaultViewport = {
  width:1200, 
  height: 600
};

(async () => {
    const browser = await puppeteer.launch({defaultViewport, headless: false});
    const page = await browser.newPage();
    page.setViewport({width: 1200, height:600, deviceScaleFactor:1})
    await page.goto('http://www.baidu.com');
  
    const fileContent = fs.readFileSync(path.join(__dirname, './selector.js'), 'utf-8');
    await page.evaluateHandle(fileContent);
    
    let node = await page.evaluateHandle(async () => {
      let result = await window.TextSelector.queryText('地图');
      return result;
    });
    await node.click();
})();

  在selector.js中,我们定义了Selector对象,然后挂载到window的TextSelector对象之中。在Selector对象之中,我们定义了两个函数,queryTextAll函数获取到了页面中所有的包含形参text的节点。queryText默认返回第一个节点。然后再回去看,在浏览器进程中完成了window函数挂载之后,又通过page.evaluateHandle()函数来执行window.TextSelector.queryText()函数,来寻找“地图”的这个链接按钮,把这个链接按钮作为ElementHandle对象返回之后,调用click()方法来对元素点击。

let Selector = {

  queryText: (text, index=0) => {
    const nodeList = Selector.queryTextAll(text);
    return nodeList[index];
  },

  queryTextAll: (text) => {
    const targets = [];
    const nodeList = document.querySelectorAll('*');
    targets.push(...Array.from(nodeList).filter(node => {
      const containTextNode = Array.from(node.childNodes).find(node => {
        if(node.nodeName === '#text') {
          return node.wholeText === text;
        }
      });
      if(containTextNode) {
        return true
      }
    }));
    console.log('target', targets)
    return targets;
  }
  
}

window.TextSelector = Selector;