前言
很多时候我们需要遍历我们的页面来检查页面是否存在问题,以更好的保证可用性和安全性,所以遍历一个给定页面是比较通用的能力,是很多高级功能的基础。下面就介绍怎么使用puppeteer来实现页面遍历的功能。
基本算法
实现页面的遍历功能,我们需要实现几个基础的方法,如下:
- 怎么获取到登录态
- 页面如何打开
- 获取到页面的可点击节点
- 页面的滑动,点击,截图等基础功能
- 页面请求的拦截,识别
- 页面返回/退出
上述能力实现之后,将他们组合起来,就可以实现一个页面的遍历功能,我们分别讲解每个功能如何实现。
功能实现
登录态
不同的帐号有不同的登录方式,最通用的就是专门写一个脚本来实现登录功能。对于需要扫码登录的页面,其实是从服务端获取登录token,可以直接调用服务端来获取(前提是你自己的业务)。否则就只能老老实实的通过脚本来实现了(在浏览器端实现脚本相对容易)。
页面打开
puppeteer提供了专门的方法来打开页面,我们只要初始化之后,调用这个功能就可以了。代码如下:
async function openUrl( targetUrl ){
//const browser = await puppeteer.connect(config.browserLaunchOptions);
const browser = await puppeteer.launch(config.browserLaunchOptions);
console.log("userAgent:"+JSON.stringify(await browser.userAgent()));
const page = await browser.newPage();
interactor(page)
/**
* 开启监听页面请求数据
*/
await setPageWatcher(page);
await page.emulate(config.pageEmulateOptions);
await page.goto(targetUrl, {timeout:config.timeoutMillSeconds, waitUntil: config.waitUntilStr}).catch(err => {});
await page.waitFor(10000);
await waitForPageComplete(page,2);
await setBrowserWatcher(browser);
loginPageUrl = targetUrl;
return page;
}
获取页面的可点击点
拿到一个页面之后我们需要获取到他可点击的点位信息,以便我们后续遍历的时候操作,所以需要一个获取页面可点击点的方法。实现如下:
async function getClickableElements( targetFrame ){
try{
let sections = await targetFrame.$$('[data-clickable=true],.tr-tabbar-item')
return sections;
} catch (e) {
console.log(e);
return null;
}
}
页面的滑动,点击,截图等基础功能
对于页面的滑动,点击和截图,有专门的方法来实现,代码如下:
function screenshot( page,picName ){
return new Promise((resolve, reject) => {
setTimeout(async () => {
var newPage=null;
try {
var buf=null;
if( newPagePromise ){
console.log('新开了一个页面');
newPage = await newPagePromise;
await newPage.waitFor(6000);
var currentUrl=await newPage.url();
console.log("new-Page-Url:"+currentUrl);
if(currentUrl&¤tUrl.includes('https://login.xxx.com/login.htm?redirectURL=')){
currentUrl = currentUrl.replace('https://login.xxx.com/login.htm?redirectURL=',config.loginUrl);
console.log("newUrl="+currentUrl);
await openTargetUrl(newPage,currentUrl);
}
buf = await newPage.screenshot({
})
await newPage.close();
newPagePromise = null;
await page.bringToFront();
} else {
buf = await page.screenshot({
})
}
if(buf){
console.log('截图完成,开始上传');
var picUrl =await util.uploadImageWithRetry(picName,buf);
console.log("截图成功:"+picUrl);
resolve(picUrl)
}
resolve(null);
} catch (e) {
if (newPage) {
await newPage.close();
}
newPagePromise = null;
await page.bringToFront();
console.log(e);
reject(null)
}
})
})
}
// 元素点击
await element.tap(2000);
页面请求的拦截,识别
在执行过程中页面可能出现跳转,并且我们还需要监听页面的请求来检查是不是正常。实现如下:
function setPageWatcher(page) {
page.on('requestfailed', error => {
if(error.url()&&error.url().includes('https://login.xxx.com')){
console.log("跳转到了登录页面:"+error);
needReLogin = true;
}
})
page.on('error', (error) => {
//console.log(chalk.red('💢 whoops! there was an error'))
//console.log(error)
})
page.on('pageerror', (error) => {
//console.log(error)
})
page.on('response',(response) => {
if( "image"==response.request().resourceType() && response.url() && response.headers()['content-length'] && response.headers()['content-length'] >200 ){
allResource.push({
size: response.headers()['content-length'],
type: response.request().resourceType(),
url: response.url()
});
}
})
page.on('dialog', async dialog => {
console.log("dialog");
await dialog.accept();
console.log(dialog.message());
//await dialog.dismiss();
});
}
页面返回/退出
代码如下:
function pageBack( page, frameInfo,num ){
console.log("开始回到首页");
return new Promise((resolve, reject) => {
setTimeout(async () => {
try {
let backIcons = await frameInfo.backIconFrame.$$('.backIcon');
if(backIcons.length>0&&!needReLogin){
try{
console.log("回到首页");
await backIcons[0].tap(2000);
await page.waitFor(2000);
resolve(frameInfo);
}catch (e) {
resolve(reloadPage(page,num));
}
} else {
resolve(reloadPage(page,num));
}
} catch (e) {
console.log('回到首页出错!',e);
resolve(reloadPage(page,num))
}
})
})
}
以上基本功能就实现了,欢迎交流。