通过Puppeteer Api来控制Chrome进行数据抓取或自动化测试通常模拟鼠标或键盘的操作。
接下来通过一些实例来介绍这些基本操作。后文的代码演示环境如下:
1. headless均设置为false即有界面状态下测试
2. puppeteer版本0.1.7
3. chrome版本5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3514.2 Safari/537.36
## 鼠标移动
进入baidu首页,模拟鼠标移动到更多产品按钮下。
主要流程:
1. 获取更多产品按钮对象,page.$(“#u1 > a.bri”)
2. 通过element.boundingBox()拿到坐标参数
3. 移动鼠标page.mouse.move(x,y)
```
(async () => {
let browser = await puppeteer.launch({headless: false});
let page = await browser.newPage();
let response = await page.goto("https://www.baidu.com/");
await page.waitFor(1000);
let element = await page.$("#u1 > a.bri");
let box = await element.boundingBox();
const x = box.x + (box.width/2);
const y = box.y + (box.height/2);
await page.mouse.move(x,y);
await page.waitFor(10000);
await page.close();
await browser.close();
}
)();
```
## 鼠标拖拽
进入baidu地图首页进行拖拽模拟。由于puppeteer并未直接提供拖动api,因此拖动通过按下鼠标、移动鼠标、松开鼠标这三个操作来模拟。运行如下的拖拽操作会看到地图发生拖动。
```
(async () => {
let browser = await puppeteer.launch({headless: false});
let page = await browser.newPage();
let response = await page.goto("https://map.baidu.com/");
await page.waitFor(1000);
await page.mouse.move(500,400);
await page.mouse.down();
await page.mouse.move(100,200,{steps:1000});
await page.mouse.up();
await page.waitFor(10000);
await page.close();
await browser.close();
}
)();
```
## 鼠标单击
这次我们进入百度首页,单机新闻连接,并获取跳转后页面的html代码。示例代码如下。
```
(async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
const response = await page.goto("https://www.baidu.com/");
const searchButton = await page.$("#u1 > a:nth-child(1)");
const box = await searchButton.boundingBox();
const x = box.x + (box.width/2);
const y = box.y + (box.height/2);
const r = await Promise.all([
page.mouse.click(x,y),
page.waitForNavigation()
]);
const content = await page.content();
console.log(content);
await page.waitFor(20000);
}
)();
```
单击链接会触发页面跳转,当单机后直接尝试通过page.content()获取页面内容会出现异常(观察该效果可将例子中page.waitForNavigation()注释)
> UnhandledPromiseRejectionWarning: Error: Execution context was destroyed, most likely because of a navigation.
因此需要调用page.waitForNavigation()等待页面跳转完毕,然后在调用page.content()才能正常获取页面内容。
## 键盘按键
这个例子中,首先进入我的博客首页https://blog.csdn.net/Revivedsun,然后在输入框中输入搜索文字,随后按下回车。按下回车后会接着打开一个新的标签页,我们将获取这个标签页的html文本。首先完整例如下。
```
function getNewPageWhenLoaded(browser) {
return new Promise((x) => browser.once('targetcreated', async (target) => {
const newPage = await target.page();
const newPagePromise = new Promise(() => newPage.once('domcontentloaded', () => x(newPage)));
const isPageLoaded = await newPage.evaluate(() => document.readyState);
return isPageLoaded.match('complete|interactive') ? x(newPage) : newPagePromise;
}));
}
(async () => {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
const response = await page.goto("https://blog.csdn.net/Revivedsun");
const elementHandler = await page.$("#csdn-toolbar > div > div > ul > li:nth-child(2) > div > a");
await page.focus("input#toolber-keyword");
await page.keyboard.type("Puppeteer",{delay:100});
const newPagePromise = getNewPageWhenLoaded(browser);
await page.keyboard.press("Enter",{delay:50});
const newPage = await newPagePromise;
const content = await newPage.content();
console.log(content);
}
)();
```
打开新标签页后需要获取新标签页的对象,这时通过监听事件targetcreated来实现。当事件targetcreated发生后,在回调中获取page页标签对象,随后监听domcontentloaded事件,等待页面加载完毕,一旦加载完毕,调用Promise构造函数中的resolve方法,将页标签对象传递给调用方。若未加载完毕,则将返回Promise对象。状态检测通过document.readyState来获取。
```
function getNewPageWhenLoaded(browser) {
return new Promise((x) => browser.once('targetcreated', async (target) => {
const newPage = await target.page();
const newPagePromise = new Promise(() => newPage.once('domcontentloaded', () => x(newPage)));
const isPageLoaded = await newPage.evaluate(() => document.readyState);
return isPageLoaded.match('complete|interactive') ? x(newPage) : newPagePromise;
}));
}
```
这种方法可以获得新打开标签页对象。此外页可以尝试修改target属性为_selft,在当前页打开新页面。这是使用“键盘按键“小节中介绍的方法获取新跳转页面html内容即可。 属性修改示例如下。
```
const elementHandler = await page.$(cssSelector);
await page.evaluateHandle((e) => {
e.target = '_self'
} ,elementHandler);
```
# 参考
1. target属性修改,
https://github.com/GoogleChrome/puppeteer/issues/386#issuecomment-343059315
2. 键盘按键表
https://github.com/GoogleChrome/puppeteer/blob/v1.7.0/lib/USKeyboardLayout.js
3. 事件监听获取新打开标签对象,
https://github.com/GoogleChrome/puppeteer/issues/386
---------------------
作者:FserSuN
原文:https://blog.csdn.net/revivedsun/article/details/82121123