随笔

Try Puppeteer

昨天刚说到“爬虫定制”,今天正好看下无头浏览器怎么用,主要目标是获取远程内容,并输出到文件中,然后截图确保可追溯。

Puppeteer 是一个基于 Chromium 无头浏览器提供的工具,具备更高级的 API。

Install

Puppeteer 提供了两个库,一个是 puppeteer,一个是puppeteer-core,很明显core是没有自带Chromium浏览器的,如果你的电脑已经安装了Chromium或者想手动下载安装Chromium来执行,那么运行npm i puppeteer-core,否则执行npm i puppeteer.

Open URL

这里借用官方最基础版本

  • 启动一个浏览器
  • 开启新 Tab 页面
  • 访问 https://example.com
  • 截图并保存到 example.png
  • 关闭浏览器
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();
})();

More config

但是现实明显不能如此简单,比如 goto 之后可以立刻截图吗?这时候很可能是一个空白页或者加载页面。

goto 的第二个参数可以配置 { waitUntil: 'networkidle0' },500ms 内没有网络请求才会完成这个动作,但是测试发现没啥用,所以又用了page.waitForTimeout(ms),一半等待个三五秒就可以了。

最后截图也很有意思,我的截图代码是在获取 content 下面的,但是某些情况下 content 的内容比截图看到的内容更多,内容已经有了,但截图还是空数据加载时候的状态,具体原因未知,之后解决办法是在截图前在输出 PDF 或者加一个事件await page.hover('body');,所以猜测原因是需要事件触发更新重新渲染。

部分代码如下,完整代码点这里

async function generateContent(pagePath) {
    log('start -> ', pagePath);
    await page.goto(BASE_URL + pagePath,{ waitUntil: 'networkidle0' });
    log(`wait ${WAIT_PAGE_LOAD}ms`);
    await page.waitForTimeout(WAIT_PAGE_LOAD);

    fs.mkdirSync(path.resolve(__dirname, `./screenshot/${pagePath}`), { recursive: true });
    fs.mkdirSync(path.resolve(__dirname, `./cache/${pagePath}`), { recursive: true });

    const pageContent = await page.content();
    const filePath = path.resolve(__dirname, `./cache/${pagePath}/index.html`);
    fs.writeFileSync(filePath, pageContent, {  flag: 'w+' });
    log(`write html`);

    await page.hover('body');

    log(`screenshot`);
    await page.screenshot({ 
      path: `./screenshot/${pagePath}/${new Date().toLocaleString().replaceAll('/', '-')}.png`,
      fullPage: true,
    });
    log('end -> ', pagePath);
  }

实际效果如下图

截屏2022-02-28 18.04.50.png

截屏2022-02-28 18.05.04.png

本文链接:https://note.lilonghe.net/post/try-puppeteer.html

-- EOF --