使用 Puppeteer 自动化 chrome / chromium 操作

更新日期: 2019-09-19 阅读次数: 28767 字数: 911 分类: Browser

Puppeteer 名词,傀儡师的意思。不禁想起了火影忍者。。。

基本介绍

Puppeteer 是 Google 官方的一个开源项目

https://github.com/GoogleChrome/puppeteer

Puppeteer 提供了一套 nodejs 的类库用于操作 chrome / chromium,基于 DevTools 协议。

优点、缺点

优点

  • Google 官方支持,作为亲儿子,Puppeteer 肯定会比 Selenium 获得更多的接口支持。

缺点

  • 目前只有 nodejs 语言支持
  • 我非常不喜欢 nodejs ...

典型应用场景

  • 生成网页截图、PDF
  • 爬取 SPA (单页网页应用)页面的内容。我觉得这个功能是给 Google 爬虫用来处理 vuejs, reactjs,angular 的 SPA 页面的。
  • 从网页中提取内容。对于动态渲染的页面内容,非常有优势
  • 自动化表单提交,UI 测试,模拟键盘输入等
  • 自动化测试

在线调试工具

https://try-puppeteer.appspot.com/

我想做个手机版。。。或者微信小程序

安装

node 版本需要 v7.6.0 以上。

Node 多版本共存参考 Ubuntu 上使用 NVM 安装 Node.js 指定版本

nvm install v9.5.0

安装完 nodejs 之后,项目根目录下执行

npm i --save puppeteer
% npm i --save puppeteer

> puppeteer@1.0.0 install /home/zhongwei/work/puppeteer/node_modules/puppeteer
> node install.js

ERROR: Failed to download Chromium r526987! Set "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" env variable to skip download.

从报错信息看,应该是被方校长祝福了。

由于我的 Ubuntu 开发机上已经安装了最新的 Chromium,所以我觉得没有必要再重新下载。

env PUPPETEER_SKIP_CHROMIUM_DOWNLOAD="true" npm i --save puppeteer

> puppeteer@1.0.0 install /home/zhongwei/work/puppeteer/node_modules/puppeteer
> node install.js

**INFO** Skipping Chromium download. "PUPPETEER_SKIP_CHROMIUM_DOWNLOAD" environment variable was found.
npm WARN saveError ENOENT: no such file or directory, open '/home/zhongwei/work/puppeteer/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/home/zhongwei/work/puppeteer/package.json'
npm WARN puppeteer No description
npm WARN puppeteer No repository field.
npm WARN puppeteer No README data
npm WARN puppeteer No license field.

+ puppeteer@1.0.0
added 1 package in 0.924s

但是,这样做并不行。。。执行测试程序还是会报错

node run.js 
(node:25615) UnhandledPromiseRejectionWarning: AssertionError [ERR_ASSERTION]: Chromium revision is not downloaded. Run "yarn install" or "npm install"

我将代码中的 puppeteer 指定了路径依然报错

const browser = await puppeteer.launch({executablePath: '/usr/bin/chromium-browser'});

报错信息

node run.js 
(node:29853) UnhandledPromiseRejectionWarning: Error: Timed out after 30000 ms while trying to connect to Chrome! The only Chrome revision guaranteed to work is r526987

看来,必须指定版本的 chromium 才能使用 puppeteer。看来 puppeteer 1.0 release 说明,只有 65 r526987 版本才支持,而我的本地版本是 59 ...

突然发现 cnpm 可以代理下载 Chromium r526987。。。马云爸爸真不是白叫的!

cnpm install puppeteer
✔ Installed 1 packages
✔ Linked 39 latest versions
Downloading Chromium r526987 - 97.1 Mb [====================] 100% 0.0s 
Chromium downloaded to /home/zhongwei/work/puppeteer/node_modules/_puppeteer@1.0.0@puppeteer/.local-chromium/linux-526987
✔ Run 1 scripts
✔ All packages installed (41 packages installed from npm registry, used 17s, speed 27.52kB/s, json 45(68.11kB), tarball 406.4kB)

Hello world! - 给网页截图

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://www.sunzhongwei.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();

报错

async () => {
       ^
SyntaxError: Unexpected token

原来,我 ubuntu 上默认装的是 node 6.10.0 版本,不支持 async 关键字,需要切换到 9.5.0

nvm use 9.5.0

执行之后,就会发现网页截图已经生成!

执行报错:error while loading shared libraries: libX11-xcb.so.1: cannot open shared object file: No such file or directory

sudo apt install -y gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget

puppeteer 截图中文乱码

安装中文字体即可解决

$ sudo apt-get install language-pack-zh-hant language-pack-zh-hans
$ sudo apt-get install fonts-droid-fallback ttf-wqy-zenhei ttf-wqy-microhei fonts-arphic-ukai fonts-arphic-uming

禁用 JS 的执行

在 goto 前加上

  await page.setRequestInterception(true);
  page.on('request', request => {
    if (request.resourceType() === 'script')
      request.abort();
    else
      request.continue();
  });

截取当前网页的整个界面

fullPage: true

例如:

await page.screenshot({path: 'rank.png', fullPage: true});

关于作者 🌱

我是来自山东烟台的一名开发者,有敢兴趣的话题,或者软件开发需求,欢迎加微信 zhongwei 聊聊, 查看更多联系方式