puppeteer优化小技巧

6,576 阅读2分钟

Puppeteer是一个Node库,它提供了高级API来通过DevTools协议控制Chromium或Chrome。

通过puppeteer我们可以编写脚本模拟浏览器的相关行为,实现以下功能:

  • 网页截图并保存为图片或 pdf 。
  • 模拟表单提交,键盘输入,按钮点击,滑块移动等dom操作。
  • 实现UI的自动化测试。
  • 作为抓包工区对网页性能进行调试和分析。
  • 编写定制化爬虫,解决传统HTTP抓取SPA页面难以处理异步请求的问题。

在使用puppeteer实现以上功能,我们通过几个小技巧提升pupeteer程序的效率。

过滤请求

当我们使用puppeteer对页面异步渲染的dom结构进行解析时,往往需要等待页面完成渲染完成之后,才能使用脚本进行操作。但页面渲染过程中也包含了许多静态资源如:图片/音频/视频/样式文件等。此时我们可以通过page.setRequestInterception方法,对网页请求进行过滤,拦截静态资源的请求,加快页面渲染速度。代码示例如下:

    // 开启请求拦截功能
    page.setRequestInterception(true);
    
    page.on('request', async req => {
        // 根据请求类型过滤
        const resourceType = req.resourceType();
        if (resourceType === 'image') {
            req.abort();
        else {
            req.continue();
        }
    });

推荐拦截的请求类型:

const blockedResourceTypes = [
    'image',
    'media',
    'font',
    'texttrack',
    'object',
    'beacon',
    'csp_report',
    'imageset',
];

const skippedResources = [
    'quantserve',
    'adzerk',
    'doubleclick',
    'adition',
    'exelator',
    'sharethrough',
    'cdn.api.twitter',
    'google-analytics',
    'googletagmanager',
    'google',
    'fontawesome',
    'facebook',
    'analytics',
    'optimizely',
    'clicktale',
    'mixpanel',
    'zedo',
    'clicksor',
    'tiqcdn',
];

代理请求

除了过滤请求之外,我们也可用代理网页渲染过程中发出的请求。在某些爬虫项目达到不被发爬的目的,代码示例如下:

page.on('request', async req => {
    // 代理请求
    const response = await fetch({
        url: req.url(),
        method: req.method(),
        headers: req.headers(),
        body: req.postData(),
        proxy: getProxyIp(),
        resolveWithFullResponse: true,
    });
    // 响应请求
    req.respond({
        status: response.statusCode,
        contentType: response.headers['content-type'],
        headers: response.headers || req.headers(),
        body: response.body,
    });
});

复用browser

使用puppeteer.connectpuppeteer.launch启动一个浏览器实例要快很多(参考),所以当我们需要开启多个broswer实例时,可以通过缓存wsEndpoint来达到复用的目的,代码实例如下:

let wsEndpoint = await cache.get(Parser.WS_KEY);
let broswer;
try {
    browser = !wsEndpoint
        ? await puppeteer.launch(config)
        : await puppeteer.connect({
              browserWSEndpoint: this.wsEndpoint,
          });
} catch (err) {
    browser = await puppeteer.launch(config);
} finally {
    wsEndpoint = this.browser.wsEndpoint();
    await cache.set(Parser.WS_KEY, 60 * 60 * 1000, this.wsEndpoint);
}

禁用浏览器多余功能

puppeteer为我们提供了完善浏览器环境,但实际开发中,有很多默认开启的功能是项目本身不需要的,此时我们可以设置浏览器启动参数来禁用额外的功能:

    puppeteer.launch({
        args: [
            '--no-sandbox',                    // 沙盒模式
            '--disable-setuid-sandbox',        // uid沙盒
            '--disable-dev-shm-usage',         // 创建临时文件共享内存
            '--disable-accelerated-2d-canvas', // canvas渲染
            '--disable-gpu',                   // GPU硬件加速
        ]
    });

参考资料