puppeteer库入门初探


Posted in Javascript onJanuary 09, 2019

puppeteer 是一个Chrome官方出品的headless Chrome node库。它提供了一系列的API, 可以在无UI的情况下调用Chrome的功能, 适用于爬虫、自动化处理等各种场景

根据官网上描述,puppeteer 具有以下作用:

  • 生成页面截图和 PDF
  • 自动化表单提交、UI 测试、键盘输入等
  • 创建一个最新的自动化测试环境。使用最新的 JavaScript 和浏览器功能,可以直接在最新版本的 Chrome 中运行测试。
  • 捕获站点的时间线跟踪,以帮助诊断性能问题。
  • 爬取 SPA 页面并进行预渲染(即'SSR')

以下就来阐述 puppeteer 的这几个作用

1.初始化项目

注: 这里我们会使用到 es6/7 的新特性,所以用 typescript 来编译代码

npm install puppeteer typescript @types/puppeteer

tsconfig.json 配置如下:

{
 "compileOnSave": true,
 "compilerOptions": {
  "target": "es5",
  "lib": [
   "es6", "dom"
  ],
  "types": [
   "node"
  ],
  "outDir": "./dist/",
  "sourceMap": true,
  "module": "commonjs",
  "watch": true,
  "moduleResolution": "node",
  "isolatedModules": false,
  "experimentalDecorators": true,
  "declaration": true,
  "suppressImplicitAnyIndexErrors": true
 },
 "include": [
  "./examples/**/*",
 ]
}

puppeteer 模块提供一个方法启动一个 Chromium 实例。

import * as puppeteer from 'puppeteer'

(async () => {
 await puppeteer.launch()
})()

上述代码通过 puppeteer 的 launch 方法生成一个 browser 实例,launch 方法可以接收一些配置项。较为常用的有:

  • headless [boolean]: 是否以 headless 模式启动浏览器
  • slowMo [number]: 减缓 puppeteer 的操作。这样就很方便的可以看到正在发生的事情
  • args[Array[string]]: 要传给浏览器实例的额外参数

2.生成页面截图

这里我们以 https://example.com/ 为例

(async () => {
 const browser = await puppeteer.launch(); //生成browser实例
 const page = await browser.newPage();   //解析一个新的页面。页面是在默认浏览器上下文创建的
 await page.goto("https://example.com/"); //跳转到 https://example.com/
 await page.screenshot({          //生成图片
  path: 'example.png'
 })
})()

在这里需要注意的是,截图默认截取的是打开网页可视区的内容,如果要获取完整的可滚动页面的屏幕截图,需要添加 fullPage: true

执行 node dist/screenshot.js ,即可在根目录下生成 example.png

puppeteer 默认将页面大小设置为 800*600,可以通过 page.setViewport() 来改变页面大小。

不仅如此,puppeteer 还可以模拟手机

import * as puppeteer from "puppeteer"; 
import * as devices from "puppeteer/DeviceDescriptors"; 
const iPhone = devices["iPhone 6"];

(async () => {
 const browser = await puppeteer.launch({
  headless: false
 });
 const page = await browser.newPage();
 await page.emulate(iPhone);
 await page.goto("https://baidu.com/");
 await browser.close();
})();

3.生成 pdf

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.goto("https://example.com/");
 await page.pdf({
  displayHeaderFooter: true,
  path: 'example.pdf',
  format: 'A4',
  headerTemplate: '<b style="font-size: 30px">Hello world<b/>',
  footerTemplate: '<b style="font-size: 30px">Some text</b>',
  margin: {
   top: "100px",
   bottom: "200px",
   right: "30px",
   left: "30px",
  }
 });
 await browser.close();
})()

执行 node dist/pdf.js 即可。

4.自动化表单提交, 输入

在这里我们模拟一下京东的登录, 为了能更好的看到整个过程, 我们使用 headless: false 来关闭 headless 模式,看一下整个的登录流程

(async () => {
 const browser = await puppeteer.launch({
  headless: false
 });
 const page = await browser.newPage();
 await page.goto("https://github.com/login");
 await page.waitFor(1000)  //延迟1秒输入
 await page.type("#login_field", "1137060420@qq.com"); //立即输入
 await page.type("#password", "bian1992518", {
  delay: 100
 }) //模拟用户输入
 await page.click("input[type=submit]"); //点击登录按钮
})()

5.站点时间线追踪

可以很方便的使用 tracking.starttracking.stop 创建一个可以在 chrome devtools 打开的跟踪文件

(async () => {
 const broswer = await puppeteer.launch();
 const page = await broswer.newPage();
 await page.tracing.start({
  path: "trace.json"
 });
 await page.goto("https://example.com/");
 await page.tracing.stop();
 broswer.close();
})();

执行 node dist/trace.js 会生成一个 trace.json 文件, 然后我们打开 chrome devtools -> Performance, 然后把该文件直接拖进去即可。该功能便于我们对网站进行性能分析, 进而优化性能

6.爬虫和 SSR

现在大多数开发用 react、vue、angular 来构建 SPA 网站, SPA 固有很多的优点, 比方开发速度快、模块化、组件化、性能优等。但其缺点还是很明显的, 首先就是首屏渲染问题, 其次不利于 SEO, 对爬虫不友好。

以 https://preview.pro.ant.design/#/dashboard/analysis 为例, 我们点击右键, 查看源代码, 发现其 body 里面只有 <div id="root"></div> ,假如想把门店销售额排名情况给爬下来,存到数据库进行数据分析(如下图)

puppeteer库入门初探 

此时我们以传统爬虫的方式去爬的话是拿不到网页内容的。

如 python

# -*- coding : UTF-8 -*-
from bs4 import BeautifulSoup 
import urllib2


def spider(): 
  html = urllib2.urlopen('https://preview.pro.ant.design/#')
  html = html.read()
  soup = BeautifulSoup(html, 'lxml')
  print(soup.prettify())


if __name__ == '__main__': 
  spider()

执行 python py/index.py , 得到的结果如下图:

puppeteer库入门初探 

body 里面并没有页面相关的 dom,因此我们想通过 python 去爬取 SPA 页面的内容是不可行的。

nodejs

import axios from "axios";

(async () => {
 const res = await axios.get("https://preview.pro.ant.design/#");
 console.log(res.data);
})();

执行 node dist/node-spider.js , 得到和上面例子一样的结果。

puppeteer库入门初探

puppeteer

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.goto("https://preview.pro.ant.design/#");
 console.log(await page.content());
})();

执行 node dist/spider.js , 得到如下:

puppeteer库入门初探 

此时我们可以惊奇的发现可以抓到页面所有的 dom 节点了。此时我们可以把它保存下来做 SSR,也可以爬取我们想要的内容了。

(async () => {
 const browser = await puppeteer.launch();
 const page = await browser.newPage();
 await page.goto("https://preview.pro.ant.design/#");
 const RANK = ".rankingList___11Ilg li";
 await page.waitForSelector(RANK);
 const res = await page.evaluate(() => {
  const getText = (v, selector) => {
   return v.querySelector(selector) && v.querySelector(selector).innerText;
  };
  const salesRank = Array.from(
   document.querySelectorAll(".rankingList___11Ilg li")
  );
  const data = [];
  salesRank.map(v => {
   const obj = {
    rank: getText(v, "span:nth-child(1)"),
    address: getText(v, "span:nth-child(2)"),
    sales: getText(v, "span:nth-child(3)")
   };
   data.push(obj);
  });
  return {
   data
  };
 });
 console.log(res);
 await browser.close();
})();

执行 node dist/spider.js , 得到如下:

puppeteer库入门初探

此时,我们已经利用 puppeteer 把我们所需要的数据给爬下来了。

到此,我们就把 puppeteer 基本的功能点给实现了一遍,本文示例代码可在 github 上获取。

参考

https://github.com/GoogleChrome/puppeteer
https://pptr.dev/#?product=Puppeteer&version=v1.6.0

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

Javascript 相关文章推荐
JObj预览一个JS的框架
Mar 13 Javascript
如何使用Jquery获取Form表单中被选中的radio值
Aug 09 Javascript
JS图片无缝、平滑滚动代码
Mar 11 Javascript
微信公众平台开发教程(六)获取个性二维码的实例
Dec 02 Javascript
BootStrap中
Dec 10 Javascript
浅谈React组件之性能优化
Mar 02 Javascript
微信小程序学习笔记之获取位置信息操作图文详解
Mar 29 Javascript
如何根据业务封装自己的功能组件
Apr 19 Javascript
JS浮点数运算结果不精确的Bug解决
Aug 01 Javascript
Vue CLI项目 axios模块前后端交互的使用(类似ajax提交)
Sep 01 Javascript
vue实现吸顶、锚点和滚动高亮按钮效果
Oct 21 Javascript
原生js实现自定义消息提示框
Nov 19 Javascript
node.js的Express服务器基本使用教程
Jan 09 #Javascript
JavaScript学习笔记之基于定时器实现图片无缝滚动功能详解
Jan 09 #Javascript
element-ui 时间选择器限制范围的实现(随动)
Jan 09 #Javascript
JavaScript学习笔记之DOM基础操作实例小结
Jan 09 #Javascript
如何解决webpack-dev-server代理常切换问题
Jan 09 #Javascript
JavaScript学习笔记之数组基本操作示例
Jan 09 #Javascript
浅谈webpack性能榨汁机(打包速度优化)
Jan 09 #Javascript
You might like
简单的php新闻发布系统教程
2014/05/09 PHP
PHP常量使用的几个需要注意的地方(谨慎使用PHP中的常量)
2014/09/12 PHP
PHP中的类型约束介绍
2015/05/11 PHP
JS 参数传递的实际应用代码分析
2009/09/13 Javascript
De facto standard 世界上不可思议的事实标准
2010/08/29 Javascript
JavaScript 类型的包装对象(Typed Wrappers)
2011/10/27 Javascript
jQuery 图片切换插件(代码比较少)
2012/05/07 Javascript
JavaScript高级程序设计(第3版)学习笔记13 ECMAScript5新特性
2012/10/11 Javascript
javascript实现按回车键切换焦点
2015/02/09 Javascript
使用jQuery+EasyUI实现CheckBoxTree的级联选中特效
2015/12/06 Javascript
理解Javascript文件动态加载
2016/01/29 Javascript
JS中使用变量保存arguments对象的方法
2016/06/03 Javascript
浅析jQuery 3.0中的Data
2016/06/14 Javascript
bootstrap读书笔记之CSS组件(上)
2016/10/17 Javascript
JavaScript构建自己的对象示例
2016/11/29 Javascript
Angular 4.x 动态创建表单实例
2017/04/25 Javascript
浅谈JsonObject中的key-value数据解析排序问题
2017/12/06 Javascript
解决Vue中引入swiper,在数据渲染的时候,发生不滑动的问题
2018/09/27 Javascript
Vue注册组件命名时不能用大写的原因浅析
2019/04/25 Javascript
[14:21]VICI vs EG (BO3)
2018/06/07 DOTA
Python牛刀小试密码爆破
2011/02/03 Python
通过Pandas读取大文件的实例
2018/06/07 Python
Linux下python制作名片示例
2018/07/20 Python
python统计中文字符数量的两种方法
2019/01/31 Python
Django基础三之视图函数的使用方法
2019/07/18 Python
利用python实现汉字转拼音的2种方法
2019/08/12 Python
HTML5标签与HTML4标签的区别示例介绍
2013/07/18 HTML / CSS
中专毕业个人的自荐信格式
2013/09/21 职场文书
《要下雨了》教学反思
2014/02/17 职场文书
小学生操行评语
2014/04/22 职场文书
销售口号大全
2014/06/11 职场文书
竞选班长演讲稿400字
2014/08/22 职场文书
2014年药店店长工作总结
2014/11/17 职场文书
幼师小班个人总结
2015/02/12 职场文书
Python使用OpenCV和K-Means聚类对毕业照进行图像分割
2021/06/11 Python
python微信智能AI机器人实现多种支付方式
2022/04/12 Python