puppeteer 官网:https://pptr.dev/ Puppeteer 中文文档 (与官
Puppeteer 中文文档 :https://learnku.com/docs/puppeteer/3.1.0
Puppeteer v1.5.0 中文翻peteer api 与 教程:https://pptr.dev/#?product=Puppeteer&version=v1.6.0&show=api-class-puppeteer
github 地址 以及 doc :https://github.com/GoogleChrome/puppeteer
Puppeteer的入门教端神器安利之 Puppeteer:https://jeffjade.com/2017/12/17/134-kinds-of-toss-using-puppeteer/ Puppeteer初探--爬取并生成《ES6标准入
详解 Puppeteer 入门教程:https://www.jb51.net/article/139808.htm
puppete
爬虫利器 Puppeteer 实战:https://www.jianshu.com/p/a9a55c03f768 puppeteer 爬虫
Puppeteer之爬虫入门:https://www.e-learn.cn/content/qita/845998 Puppeteer 实战
puppeteer实战之网页爬虫,模拟操作
puppeteer破解滑动验证码方法:http://www.php.cn/js-tutorial-387019.html Node:使用Puppeteer完成一次复杂的爬虫:https://www.jianshu.com/p/97eeffa3bf3a
API 文档
Puppeteer 出现的背景
Chrome59 (linux、macos)、 Chrome60(windows)之后,Chrome自带headless(无界面)模式很方便做自动化测试或者爬虫。但是如何和 headless 模式的 Chrome 交互则是一个问题。通过启动 Chrome 时的命令行参数仅能实现简易的启动时初始化操作。Selenium、Webdriver 等是一种解决方案,但是往往依赖众多,不够扁平。
Puppeteer 是谷歌官方出品的一个通过 DevTools 协议 控制 headless Chrome 的 Node 库。可以通过 Puppeteer 的提供的 api 直接控制 Chrome 模拟大部分用户操作来进行 UI Test 或者 作为 爬虫 访问页面 来 收集数据。
Puppeteer(中文翻译”木偶”) 是 Google Chrome 团队官方的无界面(Headless)Chrome 工具,它是一个 Node
库,提供了一个高级的 API 来控制 DevTools协议上的无头版 Chrome 。也可以配置为使用完整(非无头)的 Chrome。Chrome
素来在浏览器界稳执牛耳,因此,Chrome Headless 必将成为 web 应用自动化测试的行业标杆。使用 Puppeteer
,相当于同时具有 Linux 和 Chrome 双端的操作能力,应用场景可谓非常之多。此仓库的建立,即是尝试各种折腾使用 GoogleChrome Puppeteer;以期在好玩的同时,学到更多有意思的操作。
Puppeteer 是什么,以及能做些什么
Puppeteer is a Node library which provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It can also be configured to use full (non-headless) Chrome or Chromium.
简而言之,这货是一个提供高级API的node库,能够通过devtool控制headless模式的chrome或者chromium,它可以在headless模式下模拟任何的人为操作。
你可以在浏览器中手动完成的大部分事情都可以使用 Puppeteer
完成!你可以从以下几个示例开始:
- 生成页面的截图和PDF。
- 抓取SPA并生成预先呈现的内容(即“SSR”)。
- 从网站抓取你需要的内容。
- 自动表单提交,UI测试,键盘输入等
- 创建一个最新的自动化测试环境。使用最新的JavaScript和浏览器功能,直接在最新版本的Chrome中运行测试。
- 捕获您的网站的时间线跟踪,以帮助诊断性能问题。
总之:chrome 浏览器能干的事情 puppeteer 都能干。puppeteer 通俗来说就是一个 headless chrome浏览器 (当然你也可以配置成有UI的,默认是没有的)。既然是浏览器,那么我们手工可以在浏览器上做的事情 Puppeteer 都能胜任, 另外,Puppeteer 翻译成中文是”木偶”意思,所以听名字就知道,操纵起来很方便,你可以很方便的操纵她去实现:
1) 生成网页截图或者 PDF
2) 高级爬虫,可以爬取大量异步渲染内容的网页
3) 模拟键盘输入、表单自动提交、登录网页等,实现 UI 自动化测试
4) 捕获站点的时间线,以便追踪你的网站,帮助分析网站性能问题如果你用过 PhantomJS 的话,你会发现她们有点类似,但Puppeteer是Chrome官方团队进行维护的,用俗话说就是”有娘家的人“,前景更好。
备注: 鉴于 Puppeteer
需要 Chromium
,但,即便处于 Science 上网的姿态, 也会遇到 Chromium 无法成功下载的问题;所以在最新的修改中,已经其替换为 puppeteer-core (默认情况下不下载 Chromium,使用时需要确保您安装的 puppeteer-core
版本与您要连接的浏览器兼容)。在实际使用时候,即便已然按照说明操作,但依旧会报如下错误:
Error: Chromium revision is not downloaded. Run “npm install” or “yarn install”
因此只好采取手动下载 Chromium
的方式解决;因此在运行此仓库时候,您需要在 Puppeteer API Tip-Of-Tree 根据指定 Puppeteer 下载对应 Chromium,然后放置到项根目录即可(项目中已对各不同系统做了适配,国内用户可以在 Taobao Mirrors 根据系统按需下载)
Puppeteer 架构图
架构图:
- Puppeteer 通过 devTools 与 browser 通信
- Browser 一个可以拥有多个页面的浏览器(chroium)实例
- Page 至少含有一个 Frame 的页面
- Frame 至少还有一个用于执行 javascript 的执行环境,也可以拓展多个执行环境
环境和安装
Puppetee r本身依赖 6.4 以上的Node,但是为了异步超级好用的 async/await,推荐使用7.6版本以上的Node。另外headless Chrome本身对服务器依赖的库的版本要求比较高,centos服务器依赖偏稳定,v6很难使用headless Chrome,提升依赖版本可能出现各种服务器问题(包括且不限于无法使用ssh),最好使用高版本服务器。
要在项目中使用 Puppeteer
,只需要运行如下命令安装即可;不过要注意的是:Puppeteer
至少需要 Node v6.4.0,如要使用 async / await,只有 Node v7.6.0 或更高版本才支持;另外,安装 Puppeteer
时,它会下载最新版本的 Chromium(〜71Mb Mac,〜90Mb Linux,〜110Mb Win),保证与 API 协同工作。
Puppeteer 因为是一个 npm 的包,所以安装很简单:npm i puppeteer 或者 yarn add puppeteer
Puppeteer 安装时自带一个最新版本的Chromium,可以通过设置环境变量或者 npm config 中的PUPPETEER_SKIP_CHROMIUM_DOWNLOAD 跳过下载。如果不下载的话,启动时可以通过 puppeteer.launch([options]) 配置项中的 executablePath 指定 Chromium 的位置。
Puppeteer 轻松入门
运行环境查看 Puppeteer 的官方 API 你会发现满屏的 async, await 之类,这些都是 ES7 的规范,所以你需要: Nodejs 的版本不能低于 v7.6.0, 需要支持 async, await.
需要最新的 chrome driver,
基本用法先开看看官方的入门的 DEMO
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://example.com');
await page.screenshot({ path: 'example.png' });
await browser.close();
})();
上面这段代码就实现了网页截图,先大概解读一下上面几行代码:
- 1. 先通过 puppeteer.launch() 创建一个浏览器实例 Browser 对象
- 2. 然后通过 Browser 对象创建页面 Page 对象
- 3. 然后 page.goto() 跳转到指定的页面
- 4. 调用 page.screenshot() 对页面进行截图
下面就介绍一下 puppeteer 的常用的几个 API。
示例 1
Puppeteer 类似其他框架,通过操作 Browser 实例 来操作浏览器作出相应的反应。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://rennaiqian.com');
await page.screenshot({path: 'example.png'});
await page.pdf({path: 'example.pdf', format: 'A4'});
await browser.close();
})();
上述代码通过puppeteer的launch方法生成了一个browser的实例,对应于浏览器,launch方法可以传入配置项,比较有用的是在本地调试时传入{headless:false}可以关闭headless模式。
const browser = await puppeteer.launch({headless:false})
browser.newPage方法可以打开一个新选项卡并返回选项卡的实例page,通过page上的各种方法可以对页面进行常用操作。上述代码就进行了截屏和打印pdf的操作。
一个很强大的方法是 page.evaluate(pageFunction, ...args),可以向页面注入我们的函数,这样就有了无限可能。
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://rennaiqian.com');
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio
};
});
console.log('Dimensions:', dimensions);
await browser.close();
})();
需要注意的是evaluate方法中是无法直接使用外部的变量的,需要作为参数传入,想要获得执行的结果也需要return出来。因为是一个开源一个多月的项目,现在项目很活跃,所以使用时自行查找api才能保证参数、使用方法不会错。
示例 2
对于如何使用 Puppeteer
,这非常之容易;如下简易的示例,即实现了:导航到 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'); // 页面访问地址 http://example.com
await page.screenshot({ path: 'example.png' }); // 页面截图 example.png
await browser.close(); // 关闭浏览器
})();
Puppeteer 设置浏览器页面为 800像素 x 600像素, 屏幕截图也依据这个大小. 如你需要调整页面大小,可以通过 Page.setViewport().
更多示例可参考 GoogleChrome Puppeteer Usage;在略为熟悉 Puppeteer的 Api 之后,即可用来她操纵浏览器,来为你做些你想搞的事儿;不过值得一提的是,她现在还处于开发阶段,随着版本的更替,Api 接口也有可能会跟着略有变动。Toss Puppeteer,这是在 Github 创建的一个仓库,以承载尝试使用 GoogleChrome Puppeteer 做的各种的折腾,具体如下:
示例 3
举例 - 创建PDF.
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://news.ycombinator.com', { waitUntil: 'networkidle2' });
await page.pdf({ path: 'hn.pdf', format: 'A4' });
await browser.close();
})();
上例中waitUntil表示等待的时长,参数定义在这里waitUntil - 搜索waitUntil
Page.pdf() 访问这里有更多关于创建PDF的信息.
示例 4
举例 - 通过页面上下文 (context) 获取页面信息
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
// Get the "viewport" of the page, as reported by the page.
const dimensions = await page.evaluate(() => { // 通过evaluate执行页面js
return {
width: document.documentElement.clientWidth, // 页面宽度
height: document.documentElement.clientHeight, // 页面高度
deviceScaleFactor: window.devicePixelRatio // 设备像素比
};
});
console.log('Dimensions:', dimensions);
await browser.close();
})();
访问 Page.evaluate() 获得更多关于 evaluate
和相关功能例如 evaluateOnNewDocument
and exposeFunction
的介绍。
调试技巧
- 显示界面 - 最直观的调试方法就是看到界面上发生了什么. 通过创建完整浏览器来实现,选项
headless: false
:
const browser = await puppeteer.launch({headless: false});
- 让执行慢下来 -
slowMo
选项 可以指定毫秒值,让 Puppeteer 的执行慢下来 ,也对调试有帮助
const browser = await puppeteer.launch({
headless: false,
slowMo: 250 // slow down by 250ms
});
- 获取Console的输出 - 你既可以监听
console
事件, 也可以通过 page.evaluate()
来打印。
page.on('console', msg => console.log('PAGE LOG:', ...msg.args));
await page.evaluate(() => console.log(`url is ${location.href}`));
- 启用详细日志 - 所有API调用和内部协议交互都会被记录在
puppeteer
名字空间的 debug 模式下.
# 所有详细的日志
env DEBUG="puppeteer:*" node script.js
# 通过名字空间来控制调试日志的输出
env DEBUG="puppeteer:*,-puppeteer:protocol" node script.js # 除了protocol外的所有消息
env DEBUG="puppeteer:session" node script.js # 只需要protocol session 消息
env DEBUG="puppeteer:mouse,puppeteer:keyboard" node script.js # 只输出鼠标和键盘日志
# Protocol 的交互消息会很多. 这里的例子说明了如何过滤掉所有Netwok的消息。
env DEBUG="puppeteer:*" env DEBUG_COLORS=true node script.js 2>&1 | grep -v '"Network'
自定义运行的 Chromium
默认情况下, Puppeteer 会选择自行选择下载 Chromium 来确保其API 在当前环境下正常运行. 如果确认需要运行不同版本的 Chromium, 在创建浏览器的时候传入executablePath参数,值为目标浏览器的可执行路径:
const browser = await puppeteer.launch({executablePath: '/path/to/Chrome'});
额外的例子
这些例子从 Issue 页面归纳而来,如果有额外的需要请留言。
- 如何模拟页面点击?
通过以下page的接口, 相关 issue
page.mouseMoved(x, y, options = {})
page.mousePressed(x, y, options = {})
page.mouseReleased(x, y, options = {})
page.tap(x, y, options = {})
page.touchmove()
page.touchend()
- 如何上下翻动页面?
通过调用page.evaluate中的 window.scrollBy来实现, 相关 issue
page.evaluate(_ => { window.scrollBy(0, window.innerHeight);
- 避免页面ssl认证错误信息
通过puppeteer option ignoredHTTPErrors 实现 - page.evalute 能否返回page DOM?
你可以传入 ObjectHandle到page.evaluate中成为DOM元素,但当DOM被返回的时候则成 为对应的 ObjectHandle. issue 如果需要返回,也可以返回实际需要的值,例如:
const list = await page.evaluateHandle(() => {
return Array.from(document.getElementsByTagName('a')).map(a => a.href);
});
console.log(await list.jsonValue());
相关 iusse
- 如何读取和设置cookies?
通过page.setCookie 和 page.cookies 接口。 目前有一些关于该功能的使用问题, 相关 issue - 如何上传文件?
通过elementHandle.uploadFile(...filePaths) 接口。 目前只支持 input type="file" 类
型的文件提交。 相关issue - 如何获得页面html代码?
通过 page.content() - 如何关闭javascript弹框
通过 dialog.accept, 相关 issue
page.on('dialog', dialog => {
dialog.accept('test');
});
- 如何监控页面的网络请求?
const page = await browser.newPage();
await page.setRequestInterceptionEnabled(true);
page.on('request', request => {
request.continue(); // pass it through.
});
page.on('response', response => {
const req = response.request();
console.log(req.method, response.status, req.url);
});
- 如何输入内容?
方法1 page.type
// ...
await page.focus('#lst-ib')
page.type('China')
// ...
方法2 page.evaluate 后 element.value =
await page.evaluate((a, b) => {
document.querySelector('#a').value = a;
document.querySelector('#b').value = b;
document.querySelector('#c').click();
}, a, b);
- 如何在页面中不同的Frame中切换
通过page.frames()获得frame的数组,使用 iframe.$ 来获得对应frame中的handle
例如:
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.setContent('<iframe></iframe>');
// the page.frames()[0] is always a main frame.
const iframe = page.frames()[1];
// fetch the body element of the iframe
const body = await iframe.$('body');
// ...
// do something with `body`..
// ...
browser.close();
- 获取element中的自定义属性值
通过page.evaluate 然后使用object.getAttribute
await page.evaluate( (obj) => {
return obj.getAttribute('data-src');
}, imgurlEle);
爬虫实践
很多网页通过user-agent来判断设备,可以通过page.emulate(options)来进行模拟。options有两个配置项,一个为userAgent,另一个为viewport可以设置宽度(width)、高度(height)、屏幕缩放(deviceScaleFactor)、是否是移动端(isMobile)、有无touch事件(hasTouch)。
const puppeteer = require('puppeteer');
const devices = require('puppeteer/DeviceDescriptors');
const iPhone = devices['iPhone 6'];
puppeteer.launch().then(async browser => {
const page = await browser.newPage();
await page.emulate(iPhone);
await page.goto('https://www.example.com');
// other actions...
await browser.close();
});
上述代码则模拟了iPhone6访问某网站,其中devices是puppeteer内置的一些常见设备的模拟参数。
很多网页需要登录,有两种解决方案:
- 让puppeteer去输入账号密码
常用方法:点击可以使用page.click(selector[, options])方法,也可以选择聚焦page.focus(selector)。
输入可以使用page.type(selector, text[, options])输入指定的字符串,还可以在options中设置delay缓慢输入更像真人一些。也可以使用keyboard.down(key[, options])来一个字符一个字符的输入。 - 如果是通过cookie判断登录状态的可以通过page.setCookie(...cookies),想要维持cookie可以定时访问。
Tip:有些网站需要扫码,但是相同域名的其他网页却有登录,就可以尝试去可以登录的网页登录完利用cookie访问跳过扫码。
简单例子
示例代码:
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('https://baidu.com');
await page.type('#kw', 'puppeteer', {delay: 100});
page.click('#su')
await page.waitFor(1000);
const targetLink = await page.evaluate(() => {
return [...document.querySelectorAll('.result a')].filter(item => {
return item.innerText && item.innerText.includes('Puppeteer的入门和实践')
}).toString()
});
await page.goto(targetLink);
await page.waitFor(1000);
browser.close();
})()
运行截图:
标签:const,Nodejs,await,selenium,Puppeteer,puppeteer,page,browser From: https://blog.51cto.com/csnd/5956119