专栏首页Serverless+在 SCF 中运行 Puppeteer
原创

在 SCF 中运行 Puppeteer

Puppeteer 是一个 Node.js 库, 提供了一组封装良好的接口, 使你可以通过 DevTools 协议控制 Chrome. 本文介绍如何在 SCF 中使用 Puppeteer.

一个截图的例子

我们使用官方仓库里的截图例子

const puppeteer = require('puppeteer');

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

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

将其改造一下, 使其可以在 SCF 上运行

// index.js
'use strict';

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

exports.main_handler = async (event, context, callback) => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.screenshot({path: '/tmp/example.png'});
    await browser.close();

    let img = fs.readFileSync('/tmp/example.png');
    let data = {
        isBase64Encoded: true,
        statusCode: 200,
        headers: {'content-type': 'image/png'},
        body: img.toString('base64'),
    };
    return data;
};

为了可以看到截图的效果, 我们可以添加一个 API 网关触发器, 并将图片以 Base64 编码的格式返回.

至此, 我们期望这个函数可以在 SCF 上正确运行.

运行函数

在本地创建一个新项目, 把依赖装完后, 将代码打包上传至 COS, 创建一个新的 SCF 函数, 引用这个 COS 文件(由于打包生成的代码超过 50 MB, 你需要使用这种方式上传代码)

$ npm init
$ npm install puppeteer
$ tree -L 1 .
.
|-- index.js
|-- node_modules
|-- package.json
`-- package-lock.json

第一次运行

在控制台上点击测试, 你可能会看到如下错误:

Failed to launch chrome!
[0405/090101.405444:FATAL:zygote_host_impl_linux.cc(116)] No usable sandbox! Update your kernel or see https://chromium.googlesource.com/chromium/src/+/master/docs/linux_suid_sandbox_development.md for more information on developing with the SUID sandbox. If you want to live dangerously and need an immediate workaround, you can try using --no-sandbox.

我们按照提示来修复这个错误, 添加启动参数:

const browser = await puppeteer.launch({args: ['--no-sandbox']});

第二次运行

这一次, 你会遇到不一样的错误:

Error: Failed to launch chrome!
/var/user/node_modules/puppeteer/.local-chromium/linux-641577/chrome-linux/chrome: error while loading shared libraries: libXss.so.1: cannot open shared object file: No such file or directory

作为一个有经验的程序员, 你知道这是运行环境里缺少了必要的动态链接库, 你也发现机器上没有这个动态链接库, 搜索发现, 可以这样解决

$ yum install libXScrnSaver

安装完后, 你把 libXss.so.1/lib64 目录拷贝到项目到目录里, 并在代码中将项目的目录追加到 LD_LIBRARY_PATH 环境变量中.

// index.js
'use strict';

process.env['LD_LIBRARY_PATH'] += ';' + __dirname;

操作完后, 你想看看 Chrome 还依赖哪些动态链接库, 于是你执行了以下命令:

$ ldd node_modules/puppeteer/.local-chromium/linux-641577/chrome-linux/chrome

你会发现, Chrome 依赖多达 107 个动态链接库, 你可以选择把这些库都拷贝到当前目录, 这样就可以一劳永逸地解决依赖的问题.

你没有选择这样做, 因为这会使代码包变大许多, 你打包了代码, 再次运行.

第三次运行

问题不大, 你已经知道如何解决了

Error: Failed to launch chrome!
/var/user/node_modules/puppeteer/.local-chromium/linux-641577/chrome-linux/chrome: error while loading shared libraries: libatk-bridge-2.0.so.0: cannot open shared object file: No such file or directory

拷贝缺失的库到当前目录, 再次打包上传

第 X 次运行

你并没有崩溃 (:, 反复执行这个过程后, 你终于把缺失的动态库都补齐了

$ ls lib*
libatk-1.0.so.0         libatspi.so.0          libepoxy.so.0  libgtk-3.so.0           libwayland-egl.so.1  libXss.so.1
libatk-bridge-2.0.so.0  libcairo-gobject.so.2  libgdk-3.so.0  libwayland-cursor.so.0  libxkbcommon.so.0

函数终于可以正常运行了

{"isBase64Encoded":true,"statusCode":200,"headers":{"content-type":"image/png"},"body":"iVBORw0KGgoAAAANSUhEUgAAAyAAAAJYCAYAAACadoJwAAAAAXNSR0IArs4c6QAAIABJREFUeJzs3Xd8Tff/B/DXzZCIaOy9Y48vpdSmVFXtEETs0dqKIChVlEao1pbYKyFmrVBao5TG1qZ2iBBChiA7ef/+8HB+rqx7k3vPjdvX8/E4j0dy7uee8z7n8/mce95naqKiogVEREREREQqsDB1AERERERE9N/BBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBISIiIiIiFTDBI..."}

总结

本文介绍了如何解决在 SCF 中运行 Puppeteer 缺少动态链接库的问题. 缺失的库包括:

libatk-1.0.so.0         libatspi.so.0          libepoxy.so.0  libgtk-3.so.0           libwayland-egl.so.1  libXss.so.1
libatk-bridge-2.0.so.0  libcairo-gobject.so.2  libgdk-3.so.0  libwayland-cursor.so.0  libxkbcommon.so.0

完整的示例代码如下:

// index.js
'use strict';

process.env['LD_LIBRARY_PATH'] += ';' + __dirname;

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

exports.main_handler = async (event, context, callback) => {
    const browser = await puppeteer.launch({args: ['--no-sandbox']});
    const page = await browser.newPage();
    await page.goto('https://example.com');
    await page.screenshot({path: '/tmp/example.png'});
    await browser.close();

    let img = fs.readFileSync('/tmp/example.png');
    let data = {
        isBase64Encoded: true,
        statusCode: 200,
        headers: {'content-type': 'image/png'},
        body: img.toString('base64'),
    };
    return data;
};

你想通过 API 网关 看看效果, 没有如你所愿, 截图上的文本没有被正确显示, 但是聪明的你一定想到了, 这是字体的问题. 这个问题就留给读者自行解决啦!

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 基于 Alpine 的 Docker 镜像编译的程序无法在云函数环境运行

    最近有一个用户反馈, 他使用 golang:1.13.1-alpine3.10 这个镜像来编译的可执行程序无法在云函数的环境运行, 报错信息如下:

    ritchiechen
  • 云函数安装依赖

    使用 Python, Node.js 等开发云函数时, 可能遇到的一个问题就是依赖安装. 由于操作系统版本, 系统库版本及语言版本不一致, 有时在本地环境可以运...

    ritchiechen
  • 一种Android App在Native层动态加载so库的方案

    这篇文章通过实战案例,介绍了一种有条理的组织Native层代码层级结构的方法。并且,在良好的代码层级、作用分工的基础上,实现了动态的按需加载、卸载so库。文章...

    QQ音乐技术团队
  • Activity 基础知识

    类加载方案需要重启App后让ClassLoader重新加载新的类,为什么需要重启,因为类是无法卸载的,要想重新加载类就需要重启App,因此采用类加载方案的热修复...

    Yif
  • iOS开发之使用XMPPFramework实现即时通信(二)

    上篇的博客iOS开发之使用XMPPFramework实现即时通信(一)只是本篇的引子,本篇博客就给之前的微信加上即时通讯的功能,主要是对XMPPFramewor...

    lizelu
  • linux动态库和静态库

    http://blog.163.com/xychenbaihu@yeah/blog/static/13222965520101023104745738/

    bear_fish
  • 如何使用图片级类别标注对像素级分割任务进行训练之MIL Loss详解

    图片级别标注,指的是知道图片中有哪些物体,仅此而已,而需要完成的任务是什么呢?利用这简单的图片类别信息分割出对应物体的区域,进行像素级别分割的任务,哇!是不是觉...

    AI深度学习求索
  • 10分钟掌握ConcurrentHashMap 3分钟清楚和HashMap、Hashtable的区别

    ConcurrentHashMap顾名思义就是同步的HashMap,也就是线程安全的HashMap,所以本篇介绍的ConcurrentHashMap和HashM...

    猿天地
  • 10分钟掌握ConcurrentHashMap 3分钟清楚和HashMap、Hashtable的区别

    ConcurrentHashMap顾名思义就是同步的HashMap,也就是线程安全的HashMap,所以本篇介绍的ConcurrentHashMap和HashM...

    Java技术江湖
  • [计算机视觉论文速递] 2018-03-06

    通知:此推文有12篇论文速递信息,涉及目标检测、实例分割、特征描述、姿态估计和GAN等方向。 PS:由于今天小编出差,坐8个多小时的高铁,所以整理文章较为匆...

    Amusi

扫码关注云+社区

领取腾讯云代金券