昨天刚说到“爬虫定制”,今天正好看下无头浏览器怎么用,主要目标是获取远程内容,并输出到文件中,然后截图确保可追溯。
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);
}
实际效果如下图