puppeteer基本用法(puppeteer教程)1、Puppeteer 简介Puppeteer 是一个node库,由Chrome官方团队进行维护。提供了一组用来操纵Chrome的API, 通俗来说就是一个 headless chrome浏览器 (当然你也可以配置成有UI的,默认是没有的)。 既然是浏览器,那么我们手工可以在浏览器上做的事情 Puppeteer 都能胜任, 另外,Puppeteer 翻译成中文是”木偶”意思,所以听名字就知道,操纵起来很方便,你可以很方便的操纵她去实现:
2、运行环境查看 Puppeteer 的官方 API 你会发现满屏的 async, await 之类,这些都是 ES7 的规范,所以你需要:
npm install puppeteer --save复制代码 3、基本用法先开看看官方的入门的 DEMO const puppeteer = require('puppeteer'); (async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto('https://'); await page.screenshot({path: 'example.png'}); await browser.close(); })();复制代码 上面这段代码就实现了网页截图,先大概解读一下上面几行代码:
是不是觉得好简单? 反正我是觉得比 PhantomJS 简单,至于跟 selenium-webdriver 比起来, 那更不用说了。下面就介绍一下 puppeteer 的常用的几个 API。 3.1 puppeteer.launch(options)使用 puppeteer.launch() 运行 puppeteer,它会 return 一个 promise。这一点需要特殊说明一下,Puppeteer 几乎所有的操作都是 异步的。 3.2 Browser 对象当 Puppeteer 连接到一个 Chrome 实例的时候就会创建一个 Browser 对象,有以下两种方式: Puppeteer.launch 和 Puppeteer.connect. 下面这个 DEMO 实现断开连接之后重新连接浏览器实例 const puppeteer = require('puppeteer'); puppeteer.launch().then(async browser => { // 保存 Endpoint,这样就可以重新连接 Chromium const browserWSEndpoint = browser.wsEndpoint(); // 从Chromium 断开连接 browser.disconnect(); // 使用endpoint 重新和 Chromiunm 建立连接 const browser2 = await puppeteer.connect({browserWSEndpoint}); // Close Chromium await browser2.close(); });复制代码 Browser 对象 API
4. 一些api4.1 获取元素Page 对象提供了2个 API 来获取页面元素 (1). Page.$(selector) 获取单个元素,底层是调用的是 document.querySelector() , 所以选择器的 selector 格式遵循 css 选择器规范 let inputElement = await page.$("#search", input => input); //下面写法等价 let inputElement = await page.$('#search');复制代码 (2). Page.$$(selector) 获取一组元素,底层调用的是 document.querySelectorAll(). 返回 Promise(Array(ElemetHandle)) 元素数组. const links = await page.$$("a"); //下面写法等价 const links = await page.$$("a", links => links);复制代码 最终返回的都是 ElemetHandle 对象 4.2 获取元素属性Puppeteer 获取元素属性跟我们平时写前段的js的逻辑有点不一样,按照通常的逻辑,应该是先获取元素,然后再获取元素的属性。但是上面我们知道 获取元素的 API 最终返回的都是 ElemetHandle 对象,而你去查看 ElemetHandle 的 API 你会发现,它并没有获取元素属性的 API. 事实上 Puppeteer 专门提供了一套获取属性的 API, Page.$eval() 和 Page.$$eval() (1). Page.$$eval(selector, pageFunction[, …args]), 获取单个元素的属性,这里的选择器 selector 跟上面 Page.$(selector) 是一样的。 const value = await page.$eval('input[name=search]', input => input.value); const href = await page.$eval('#a", ele => ele.href); const content = await page.$eval('.content', ele => ele.outerHTML);复制代码 4.3 执行自定义的 JS 脚本Puppeteer 的 Page 对象提供了一系列 evaluate 方法,你可以通过他们来执行一些自定义的 js 代码,主要提供了下面三个 API
const result = await page.evaluate(() => { return Promise.resolve(8 * 7); }); console.log(result); // prints "56"复制代码 这个方法很有用,比如我们在获取页面的截图的时候,默认是只截图当前浏览器窗口的尺寸大小,默认值是800x600,那如果我们需要获取整个网页的完整 截图是没办法办到的。Page.screenshot() 方法提供了可以设置截图区域大小的参数,那么我们只要在页面加载完了之后获取页面的宽度和高度就可以解决 这个问题了。 (async () => { const browser = await puppeteer.launch({headless:true}); const page = await browser.newPage(); await page.goto('https://jr.'); await page.setViewport({width:1920, height:1080}); const documentSize = await page.evaluate(() => { return { width: document.documentElement.clientWidth, height : document.body.clientHeight, } }) await page.screenshot({path:"example.png", clip : {x:0, y:0, width:1920, height:documentSize.height}}); await browser.close(); })();复制代码 (2). Page.evaluateHandle(pageFunction, …args) 在 Page 上下文执行一个 pageFunction, 返回 JSHandle 实体 const aWindowHandle = await page.evaluateHandle(() => Promise.resolve(window)); aWindowHandle; // Handle for the window object. const aHandle = await page.evaluateHandle('document'); // Handle for the 'document'.复制代码 从上面的代码可以看出,page.evaluateHandle() 方法也是通过 Promise.resolve 方法直接把 Promise 的最终处理结果返回, 只不过把最后返回的对象封装成了 JSHandle 对象。本质上跟 evaluate 没有什么区别。 下面这段代码实现获取页面的动态(包括js动态插入的元素) HTML 代码. const aHandle = await page.evaluateHandle(() => document.body); const resultHandle = await page.evaluateHandle(body => body.innerHTML, aHandle); console.log(await resultHandle.jsonValue()); await resultHandle.dispose();复制代码 (3). page.evaluateOnNewDocument(pageFunction, …args), 在文档页面载入前调用 pageFunction, 如果页面中有 iframe 或者 frame, 则函数调用 的上下文环境将变成子页面的,即iframe 或者 frame, 由于是在页面加载前调用,这个函数一般是用来初始化 javascript 环境的,比如重置或者 初始化一些全局变量。 4.4 Page.exposeFunction除此上面三个 API 之外,还有一类似的非常有用的 API, 那就是 Page.exposeFunction,这个 API 用来在页面注册全局函数,非常有用: 因为有时候需要在页面处理一些操作的时候需要用到一些函数,虽然可以通过 Page.evaluate() API 在页面定义函数,比如: const docSize = await page.evaluate(()=> { function getPageSize() { return { width: document.documentElement.clientWidth, height : document.body.clientHeight, } } return getPageSize(); });复制代码 但是这样的函数不是全局的,需要在每个 evaluate 中去重新定义,无法做到代码复用,在一个就是 nodejs 有很多工具包可以很轻松的实现很复杂的功能 比如要实现 md5 加密函数,这个用纯 js 去实现就不太方便了,而用 nodejs 却是几行代码的事情。 下面代码实现给 Page 上下文的 window 对象添加 md5 函数: const puppeteer = require('puppeteer'); const crypto = require('crypto'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); page.on('console', msg => console.log(msg.text)); await page.exposeFunction('md5', text => crypto.createHash('md5').update(text).digest('hex') ); await page.evaluate(async () => { // use window.md5 to compute hashes const myString = 'PUPPETEER'; const myHash = await window.md5(myString); console.log(`md5 of ${myString} is ${myHash}`); }); await browser.close(); });复制代码 可以看出,Page.exposeFunction API 使用起来是很方便的,也非常有用,在比如给 window 对象注册 readfile 全局函数: const puppeteer = require('puppeteer'); const fs = require('fs'); puppeteer.launch().then(async browser => { const page = await browser.newPage(); page.on('console', msg => console.log(msg.text)); await page.exposeFunction('readfile', async filePath => { return new Promise((resolve, reject) => { fs.readFile(filePath, 'utf8', (err, text) => { if (err) reject(err); else resolve(text); }); }); }); await page.evaluate(async () => { // use window.readfile to read contents of a file const content = await window.readfile('/etc/hosts'); console.log(content); }); await browser.close(); });复制代码 5、Page.emulate 修改模拟器(客户端)运行配置Puppeteer 提供了一些 API 供我们修改浏览器终端的配置
page.setViewport({width:1920, height:1080}); //设置视窗大小为 1920x1080 page.setUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'); page.emulateMedia('print'); //设置打印机媒体样式复制代码 除此之外我们还可以模拟非 PC 机设备, 比如下面这段代码模拟 iPhone 6 访问google const puppeteer = require('puppeteer'); const devices = require('puppeteer/DeviceDescriptors'); const iPhone = devices['iPhone 6']; puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.emulate(iPhone); await page.goto('https://www.google.com'); // other actions... await browser.close(); });复制代码 Puppeteer 支持很多设备模拟仿真,比如Galaxy, iPhone, IPad 等,想要知道详细设备支持,请戳这里 DeviceDescriptors.js. 6、键盘和鼠标键盘和鼠标的API比较简单,键盘的几个API如下:
page.keyboard.press("Shift"); //按下 Shift 键 page.keyboard.sendCharacter('嗨'); page.keyboard.type('Hello'); // 一次输入完成 page.keyboard.type('World', {delay: 100}); // 像用户一样慢慢输入复制代码 鼠标操作:
7、另外几个有用的 APIPuppeteer 还提供几个非常有用的 API, 比如: 7.1 Page.waitFor 系列 API
比如我想获取某个通过 js 异步加载的元素,那么直接获取肯定是获取不到的。这个时候就可以使用 page.waitForSelector 来解决: await page.waitForSelector('.gl-item'); //等待元素加载之后,否则获取不到异步加载的元素 const links = await page.$$eval('.gl-item > .gl-i-wrap > .p-img > a', links => { return links.map(a => { return { href: a.href.trim(), name: a.title } }); });复制代码 其实上面的代码就可以解决我们最上面的需求,抓取京东的产品,因为是异步加载的,所以使用这种方式。 7.2 page.getMetrics()通过 page.getMetrics() 可以得到一些页面性能数据, 捕获网站的时间线跟踪,以帮助诊断性能问题。
|
|