内容简介:从CDP与源码的角度简单分析下pptr中的常用API开放了用程序控制页面行为的接口。允许工具在chromium、chrome和其他基于Blink的浏览器上插桩、监测、调试。其中插桩(instrument)操作根据特点被分成了多种域(DOM, 调试器,网络等等)。每个域中都定义了它所支持的命令及生成的事件,命令与事件都被序列化成了固定结构的JSON对象。
从CDP与源码的角度简单分析下pptr中的常用API
Should know
puppeteer
- 单词含义:puppeteer[ˌpʌpɪˈtɪr],操纵木偶的人。
- 本质:实现了遵循 CDP(Chrome DevTools protocol) 的Node顶层API。
Chrome DevTools Protocol
开放了用程序控制页面行为的接口。
允许 工具 在chromium、chrome和其他基于Blink的浏览器上插桩、监测、调试。其中插桩(instrument)操作根据特点被分成了多种域(DOM, 调试器,网络等等)。每个域中都定义了它所支持的命令及生成的事件,命令与事件都被序列化成了固定结构的JSON对象。
ws远程连接步骤
-
使用
--remote-debugging-port=0命令启动chrome.exe -
请求
/json/version获取数据,得到ws连接地址:webSocketDebuggerUrl - 连接ws,就可以访问到浏览器实例
Example
const puppeteer = require('puppeteer');
(async() => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('https://developers.google.com/web/');
// Type into search box.
await page.type('#searchbox input', 'Headless Chrome');
// Wait for suggest overlay to appear and click "show all results".
const allResultsSelector = '.devsite-suggest-all-results';
await page.waitForSelector(allResultsSelector);
await page.click(allResultsSelector);
// Wait for the results page to load and display the results.
const resultsSelector = '.gsc-results .gsc-thumbnail-inside a.gs-title';
await page.waitForSelector(resultsSelector);
// Extract the results from the page.
const links = await page.evaluate(resultsSelector => {
const anchors = Array.from(document.querySelectorAll(resultsSelector));
return anchors.map(anchor => {
const title = anchor.textContent.split('|')[0].trim();
return `${title} - ${anchor.href}`;
});
}, resultsSelector);
console.log(links.join('\n'));
await browser.close();
})();
Most used
puppteer.launch()
发生了什么:
-
根据参数拼接命令
包含三种类型设置:
LaunchOptions,ChromeArgOptions,BrowserOptions。 根据配置选项决定chrome执行命令,通过可执行路径寻找chrome应用 - 启动子进程,绑定流 使用node的childProcess.spawn()启动一个子进程,并绑定默认IO流: stdin, stdout, stderr
-
获取地址,建立连接
/^DevTools listening on (ws:\/\/.*)$/
- 监听链路上的消息与事件 使用由EventEmitter扩展的Connection类扩展连接的通信链路,在外层进一步封装链路中的消息格式等。返回Connection和CDPSession对象(作为client收发遵循CDP的消息)。
-
创建Browser实例,并新建初始空白页
ensureInitialPage(),最终返回Promise<Browser>
browser.newPage()
newPage为browser中浏览器上下文的方法,在进行页面的操作时需要传递浏览器contextId
使用CDP中Target域的 Target.createTarget
创建页面:
async _createPageInContext(contextId) {
const {targetId} = await this._connection.send('Target.createTarget', {url: 'about:blank', browserContextId: contextId || undefined});
const target = await this._targets.get(targetId);
assert(await target._initializedPromise, 'Failed to create target for page');
const page = await target.page();
return page;
}
page.goto()
实际上是执行FrameManager对象的navigate()方法:
// Page -> FrameManager(_frameManager)
async function navigate(client, url, referrer, frameId) {
try {
const response = await client.send('Page.navigate', {url, referrer, frameId});
ensureNewDocumentNavigation = !!response.loaderId;
return response.errorText ? new Error(`${response.errorText} at ${url}`) : null;
} catch (error) {
return error;
}
}
Page对象中的manager
page对象主要使用三种manager来管理常见操作:
- _frameManager - 管理页面相关行为。页面跳转(goto),等待加载(waitFor), 元素选择与处理(evaluate)…
- _networkManager - 管理网络相关行为。请求拦截(setRequestInterception),离线模式(setOfflineMode)…
- _emulationManager - 管理模拟行为。设置移动设备与视口尺寸(setViewport)
page.click()
同样属于_frameManager控制的操作行为,并且是属于在domWorld中操作的行为。
FrameManager中的DOMWorld实例
FrameManager中使用两个DomWorld对象实例管理对于元素的不同操作:
- _mainWorld - 负责使用选择器的元素选择与注入函数等操作
- _secondaryWorld - 负责操作行为,
// Page -> FrameManager(_frameManager) -> DOMWorld(_secondaryWorld)
async click(selector, options) {
const handle = await this.$(selector);
assert(handle, 'No node found for selector: ' + selector);
await handle.click(options);
await handle.dispose();
}
page.\$(), page.evaluate(), page.\$eval()
属于 _frameManager
中 _mainWorld
的操作,executionContext中执行元素选择与 evaluate
等操作。
// Page -> FrameManager(_frameManager) -> DOMWorld(_mainWorld)
async $(selector) {
const document = await this._document();
const value = await document.$(selector);
return value;
}
...
async evaluate(pageFunction, ...args) {
const context = await this.executionContext();
return context.evaluate(pageFunction, ...args);
}
...
async $eval(selector, pageFunction, ...args) {
const document = await this._document();
return document.$eval(selector, pageFunction, ...args);
}
...
除此之外,还有用来选择多个元素和Handle的 page.$$()
, page.$$eval()
方法。
Page.waitForSelector()
// Page -> FrameManager(_frameManager)
async waitForSelector(selector, options) {
const handle = await this._secondaryWorld.waitForSelector(selector, options);
if (!handle)
return null;
// executionContext -> [CDP]Runtime Domain -> ExecutionContextDescription
const mainExecutionContext = await this._mainWorld.executionContext();
// _adoptElementHandle -> [CDP]DOM Domain -> describeNode + resolveNode
const result = await mainExecutionContext._adoptElementHandle(handle);
await handle.dispose();
return result;
}
// Page -> FrameManager(_frameManager) -> DOMWorld(_secondaryWorld)
async _waitForSelectorOrXPath(selectorOrXPath, isXPath, options = {}) {
...
const polling = waitForVisible || waitForHidden ? 'raf' : 'mutation';
const title = `${isXPath ? 'XPath' : 'selector'} "${selectorOrXPath}"${waitForHidden ? ' to be hidden' : ''}`;
const waitTask = new WaitTask(this, predicate, title, polling, timeout, selectorOrXPath, isXPath, waitForVisible, waitForHidden);
const handle = await waitTask.promise;
if (!handle.asElement()) {
await handle.dispose();
return null;
}
return handle.asElement();
...
}
Page.screenshot() & Page.pdf()
- 处理传入配置:格式,路径,质量,剪裁等参数
- 加入到截图任务队列中,screenshotTaskQueue
Other
puppeteer的其他相关项目
juggler
- 实现了符合puppeteer API规范的FireFox自动化协议。
- 该项目在 gecko 的基础上,将 juggler远程调试协议 作为测试工具添加了进来。
- 测试时需要在本地手动构建添加了juggler的firefox。
puppeteer-firefox
- 实现了使用puppeteer的语法来操纵firefox
- 使用时需要有绑定了juggler的firefox
- 开发者目前跑测试中
例子:
const pptrFirefox = require('puppeteer-firefox');
(async () => {
const browser = await pptrFirefox.launch();
const page = await browser.newPage();
await page.goto('https://example.com');
await page.screenshot({path: 'example.png'});
await browser.close();
})();
语法与puppeteer保持一致。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
有限与无限的游戏
[美]詹姆斯·卡斯 / 马小悟、余倩 / 电子工业出版社 / 2013-10 / 35.00元
在这本书中,詹姆斯·卡斯向我们展示了世界上两种类型的「游戏」:「有限的游戏」和「无限的游戏」。 有限的游戏,其目的在于赢得胜利;无限的游戏,却旨在让游戏永远进行下去。有限的游戏在边界内玩,无限的游戏玩的就是边界。有限的游戏具有一个确定的开始和结束,拥有特定的赢家,规则的存在就是为了保证游戏会结束。无限的游戏既没有确定的开始和结束,也没有赢家,它的目的在于将更多的人带入到游戏本身中来,从而延续......一起来看看 《有限与无限的游戏》 这本书的介绍吧!