【Puppeteer】
Puppeteer 概要】
- Puppeteer 是一个 Node 库,它提供了一个高级 API 来通过 DevTools 协议控制 Chromium 或 Chrome。 Puppeteer API 是分层次的,反映了浏览器结构。
- Puppeteer 使用 DevTools 协议 与浏览器进行通信。
- Browser 实例可以拥有浏览器上下文。
- BrowserContext 实例定义了一个浏览会话并可拥有多个页面。
- Page 至少有一个框架:主框架。 可能还有其他框架由 iframe 或 框架标签 创建。
- frame 至少有一个执行上下文 - 默认的执行上下文 - 框架的 JavaScript 被执行。 一个框架可能有额外的与 扩展 关联的执行上下文。
- Worker 具有单一执行上下文,并且便于与 WebWorkers 进行交互。
【Puppeteer 错误处理】
Error handling
- 如果
Puppeteer
方法无法执行一个请求,就会抛出一个错误。例如,page.waitForSelector(selector[, options])
选择器如果在给定的时间范围内无法匹配节点,就会失败。 对于某些类型的错误,Puppeteer
使用特定的错误类处理。这些类可以通过require('puppeteer/Errors')
获得。 支持的类列表:
TimeoutError
一个处理超时错误的例子:
const { TimeoutError } = require('puppeteer/Errors') // ...
try {
await page.waitForSelector('.foo')
} catch (e) {
if (e instanceof TimeoutError) {
// 如果超时,做一些处理。
}
}
Puppeteer Working with Chrome Extensions
Puppeteer
可以用来测试Chrome
扩展
注意
Chrome / Chromium
扩展当前只能在非无头模式下使用。
下面的代码用来处理扩展的 background page
,该扩展的代码在 ./my-extension
:
const puppeteer = require('puppeteer'); (async() = > {
const pathToExtension = require('path').join(__dirname, 'my-extension');
const browser = puppeteer.launch({
headless: false,
args: [`--disable - extensions - except = $ {
pathToExtension
}`, `--load - extension = $ {
pathToExtension
}`]
});
const targets = await browser.targets();
const backgroundPageTarget = targets.find(target = >target.type() === 'background_page');
const backgroundPage = await backgroundPageTarget.page(); // 像处理任何其他页面一样测试背景页面。 await browser.close();})();
【Puppeteer class:puppeteer】
Puppeteer
模块提供了一种启动Chromium
实例的方法。 下面就是使用Puppeteer
进行自动化的一个典型示例:
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser = >{
const page = await browser.newPage();
await page.goto('https://www.google.com'); // 其他操作... await browser.close();});
Methods
- puppeteer.connect(options)v0.9.0
- puppeteer.createBrowserFetcher([options])v0.9.0
- puppeteer.defaultArgs([options])v0.9.0
- puppeteer.executablePath()v0.9.0
- puppeteer.launch([options])v0.9.0
Methods
puppeteer.connect(options)v0.9.0
options
<Object>
browserWSEndpoint
<string>
一个 浏览器 websocket 端点链接。ignoreHTTPSErrors
<boolean>
是否在导航期间忽略 HTTPS 错误. 默认是false。
defaultViewport
<?Object> 为每个页面设置一个默认视口大小。默认是800x600
。如果为 null 的话就禁用视图口。width
<number>
页面宽度像素。height
<number>
页面高度像素。deviceScaleFactor
<number>
设置设备的缩放(可以认为是 dpr)。默认是1。
isMobile
<boolean>
是否在页面中设置了meta viewport
标签。默认是false。
hasTouch``<boolean>
指定 viewport 是否支持触摸事件。默认是false。
isLandscape
<boolean>
指定视口是否处于横向模式。默认是false。
slowMo
<number>
将 Puppeteer 操作减少指定的毫秒数。这样你就可以看清发生了什么,这很有用。returns
:<Promise<Browser>>
此方法将Puppeteer
添加到已有的Chromium
实例。
puppeteer.createBrowserFetcher([options])v0.9.0
options
<Object>
host
<string>
要使用的下载主机. 默认是https://storage.googleapis.com。
path
<string>
下载文件夹的路径. 默认是<root>
/.local-chromium,<root>
是 puppeteer 的包根目录。platform
<string>
可能的值有:mac, win32, win64, linux
。默认是当前平台
。returns
:<BrowserFetcher>
puppeteer.defaultArgs([options])v0.9.0
options
<Object>
设置浏览器可选项。有一下字段:headless
<boolean>
是否在 无头模式 下运行浏览器。默认是true
除非devtools
选项是true。
args
<Array<string>>
传递给浏览器实例的其他参数。可以 在这 找到 Chromium 标志列表。userDataDir
<string>
用户数据目录 的路径。devtools
<boolean>
是否为每个选项卡自动打开DevTools
面板。如果这个选项是true
的话,headless
选项将被设置为false。
returns
:<Array<string>>
Chromium 启动时使用的默认参数。
puppeteer.executablePath()v0.9.0
returns
:<string>
Puppeteer 希望找到绑定的Chromium
的路径。- 如果使用
PUPPETEER_SKIP_CHROMIUM_DOWNLOAD
跳过下载,则Chromium
可能不存在。
puppeteer.launch([options])v0.9.0
options
<Object>
在浏览器上设置的一组可配置选项。 有以下字段:ignoreHTTPSErrors
<boolean>
是否在导航期间忽略HTTPS
错误. 默认是false。
headless
<boolean>
是否以 无头模式 运行浏览器。默认是true,除非
devtools
选项是true。
executablePath
<string>
可运行 Chromium 或 Chrome 可执行文件的路径,而不是绑定的Chromium。如果
executablePath
是一个相对路径,那么他相对于 当前工作路径 解析。slowMo
<number>
将 Puppeteer 操作减少指定的毫秒数。这样你就可以看清发生了什么,这很有用。defaultViewport
<?Object>
为每个页面设置一个默认视口大小。默认是800x600
。如果为 null 的话就禁用视图口。width
<number>
页面宽度像素。height
<number>
页面高度像素。deviceScaleFactor
<number>
设置设备的缩放(可以认为是dpr
)。默认是1
。isMobile
<boolean>
是否在页面中设置了meta viewport
标签。默认是false。
hasTouch``<boolean>
指定 viewport 是否支持触摸事件。默认是false。
isLandscape
<boolean>
指定视口是否处于横向模式。默认是false。
args
<Array<string>>
传递给浏览器实例的其他参数。 这些参数可以参考 这里。- ignoreDefaultArgs <(boolean|
<Array<string>>
)> 如果是true
,那就不要使用puppeteer.defaultArgs()
。 如果给出了数组,则过滤掉给定的默认参数。这个选项请谨慎使用。默认为false
。 handleSIGINT
<boolean>
Ctrl-C 关闭浏览器进程。默认是true
。handleSIGTERM
<boolean>
关闭 SIGTERM 上的浏览器进程。默认是true
。handleSIGHUP
<boolean>
关闭 SIGHUP 上的浏览器进程。默认是true
.timeout
<number>
等待浏览器实例启动的最长时间(以毫秒为单位)。默认是30000 (30 秒)
. 通过 0 来禁用超时。dumpio
<boolean>
是否将浏览器进程标准输出和标准错误输入到process.stdout 和 process.stderr
中。默认是false。
userDataDir
<string>
用户数据目录 路径。env
<Object>
指定浏览器可见的环境变量。默认是process.env
。devtools
<boolean>
是否为每个选项卡自动打开 DevTools 面板。如果这个选项是true,headless
选项将会设置成false
。pipe
<boolean>
通过管道而不是 WebSocket 连接到浏览器。默认是false
。returns
:<Promise<Browser>>
浏览器实例支持Promise
。 这个方法结合了下面 3 个步骤:
使用 puppeteer.defaultArgs() 作为一组默认值来启动 Chromium。
启动浏览器并根据 executablePath
,handleSIGINT
,dumpio
和其他选项开始管理它的进程。
创建一个 Browser
类的实例,并根据 defaultViewport
,slowMo
和 ignoreHTTPSErrors
初始化它。
ignoreDefaultArgs
选项可用于自定义(1)步骤的行为。 例如,要从参数中过滤掉 --mute-audio
:
const browser = await puppeteer.launch({
ignoreDefaultArgs: ['--mute-audio'],
})
Puppeteer 浏览器
- extends:
EventEmitter
当Puppeteer
连接到一个Chromium
实例的时候会通过puppeteer.launch
或puppeteer.connect
创建一个Browser
对象。 下面是使用Browser
创建Page
的例子
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser = >{
const page = await browser.newPage();
await page.goto('https://example.com');
await browser.close();
});
- 一个断开连接和重连到 Browser 的例子:
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser = >{ // 存储节点以便能重新连接到 Chromium
const browserWSEndpoint = browser.wsEndpoint(); // 从 Chromium 断开和 puppeteer 的连接 browser.disconnect(); // 使用节点来重新建立连接
const browser2 = await puppeteer.connect({browserWSEndpoint}); // 关闭 Chromium
await browser2.close();});
Puppeteer 页面
Page
提供操作一个tab
页或者extension
background
page
的方法。一个Browser
实例可以有多个Page
实例。 下面的例子创建一个Page
实例,导航到一个url
,然后保存截图:
const puppeteer = require('puppeteer');
puppeteer.launch().then(async browser = >{
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({
path: 'screenshot.png'
});
await browser.close();
});
- Page 会触发多种事件(下面描述的),可以用 node 原生的方法 来捕获处理,比如
on
,once
或者removeListener。
下面的例子捕获了一个page
实例的load
事件,打印了一句话:
page.once('load', () => console.log('Page loaded!'))
- 可以用
removeListener
取消对事件的监听:
function logRequest(interceptedRequest) {
console.log('A request was made:', interceptedRequest.url())
}
page.on('request', logRequest) // 一段时间后...page.removeListener('request', logRequest);
Methods
page.$(selector)v0.9.0
selector
<string>
选择器 返回:<Promise<?ElementHandle>>
此方法在页面内执行
document.querySelector
。如果没有元素匹配指定选择器,返回值是 null。page.mainFrame().$(selector) 的简写。
page.$$(selector)v0.9.0
selector
<string>
选择器 返回:<Promise<Array<ElementHandle>>>
此方法在页面内执行
document.querySelector
。如果没有元素匹配指定选择器,返回值是 null。 page.mainFrame().$(selector) 的简写。
page.$$(selector)v0.9.0
selector
<string>
选择器返回:
<Promise<Array<ElementHandle>>>
此方法在页面内执行
document.querySelectorAll
。如果没有元素匹配指定选择器,返回值是 []。page.mainFrame().$$(selector)
的简写。
page.waitForSelector(".index-content .click-zoom a")
- 等待页面元素加载完成
await page.waitForSelector('.index-content .click-zoom a')
page.waitFor(1000)
- 等待 1000 毫秒执行
await page.waitFor(1000)
page.click('.btn')
await page.click('.btn')
- 点击 class 为 btn 元素
page.evaluate
- 执行原生 js
await page.evaluate(() => {
document.getElementById('HD_CheckIn').value = '2020-07-11'
})
示例
/**
* 爬取游戏站数据 https://game.pipajam.com/
*/
const fs = require('fs')
const puppeteer = require('puppeteer')
;(async () => {
const browser = await puppeteer.launch({
headless: false,
args: ['--start-maximized', '--disable-infobars'],
width: 1600,
height: 900,
})
// 1、 创建两个页面
const page = await browser.newPage()
const page2 = await browser.newPage()
// 2、进入首页
await page.goto('https://game.pipajam.com/')
// 3、等待页面游戏数据元素加载完成
await page.waitForSelector('.index-content .click-zoom a')
// 4、获取所以游戏链接 游戏名称
const list = await page.$$eval('.index-content .click-zoom a', (el) =>
el.map((el) => [el.href, el.getElementsByTagName('h6')[0].innerText])
)
console.log(list)
let len = list.length,
index = 0,
data = []
// 5、调用函数 组装数据
mainFun()
async function mainFun() {
try {
// 6、开启page2 页面 获取游戏详情数据
await page2.goto(list[index][0], {
timeout: 0,
})
// 7、等待详情卡片加载完成
await page2.waitForSelector('.play-game-card .play-game-card-center')
// 8、获取游戏链接地址 图片地址
const item = await page2.$eval(
'.play-game-card .play-game-card-center',
(el) => [
el.getElementsByTagName('p')[0].innerHTML,
el.getElementsByTagName('img')[0].src,
]
)
await page2.click('.play-game-card .play-game-card-center a')
const url = await page2.evaluate((param) => window.location.href, '参数')
// 9、组装数据
data.push({
description: item[0],
img: item[1],
instructton: '',
keyfeatures: '',
name: list[index][1],
published: 'Mon Oct 08 2018',
tags: [],
url,
type: "Children's intelligence",
})
console.log('以写入第' + (index + 1) + '数据。')
// 10、获取所以游戏数据 并写入指定json 文件
++index < len
? mainFun()
: fs.writeFile(
'../../json/Game/pipajam.json',
JSON.stringify(data),
() => {}
)
} catch (error) {
console.log(error, 'err')
}
}
})()