頁面互動
Puppeteer 允許透過滑鼠、觸控事件和鍵盤輸入與頁面上的元素互動。通常,您首先使用 CSS 選擇器 查詢 DOM 元素,然後對選取的元素調用動作。所有接受選擇器的 Puppeteer API 預設都接受 CSS 選擇器。此外,Puppeteer 還提供 自訂選擇器語法,允許使用 XPath、文字、輔助功能屬性尋找元素,並存取 Shadow DOM,而無需執行 JavaScript。
如果您想在不先選取元素的情況下發出滑鼠或鍵盤事件,請使用 page.mouse
、page.keyboard
和 page.touchscreen
API。本指南的其餘部分概述如何選取 DOM 元素並對其調用動作。
定位器
定位器是選取元素並與之互動的建議方式。定位器封裝了如何選取元素的資訊,並允許 Puppeteer 自動等待元素出現在 DOM 中,並處於適合執行動作的正確狀態。您始終可以使用 page.locator()
或 frame.locator()
函式來實例化定位器。如果定位器 API 沒有提供您需要的功能,您仍然可以使用較低階的 API,例如 page.waitForSelector()
或 ElementHandle
。
使用定位器點擊元素
// 'button' is a CSS selector.
await page.locator('button').click();
定位器會在點擊之前自動檢查以下事項
- 確保元素位於可視區域中。
- 等待元素變成 可見 或隱藏。
- 等待元素變成啟用狀態。
- 等待元素在兩個連續的動畫影格中具有穩定的邊界框。
填寫輸入框
// 'input' is a CSS selector.
await page.locator('input').fill('value');
自動偵測輸入類型,並選擇適當的方式使用提供的值填寫。例如,它將填寫 <select>
元素以及 <input>
元素。
定位器會在輸入之前自動檢查以下事項
- 確保元素位於可視區域中。
- 等待元素變成 可見 或隱藏。
- 等待元素變成啟用狀態。
- 等待元素在兩個連續的動畫影格中具有穩定的邊界框。
將滑鼠懸停在元素上方
await page.locator('div').hover();
定位器會在懸停之前自動檢查以下事項
- 確保元素位於可視區域中。
- 等待元素變成 可見 或隱藏。
- 等待元素在兩個連續的動畫影格中具有穩定的邊界框。
滾動元素
[.scroll()
] 函式使用滑鼠滾輪事件來滾動元素。
// Scroll the div element by 10px horizontally
// and by 20 px vertically.
await page.locator('div').scroll({
scrollLeft: 10,
scrollTop: 20,
});
定位器會在懸停之前自動檢查以下事項
- 確保元素位於可視區域中。
- 等待元素變成 可見 或隱藏。
- 等待元素在兩個連續的動畫影格中具有穩定的邊界框。
等待元素可見
有時您只需要等待元素可見。
// '.loading' is a CSS selector.
await page.locator('.loading').wait();
定位器會在傳回之前自動檢查以下事項
- 等待元素變成 可見 或隱藏。
等待函式
有時等待以 JavaScript 函式表示的任意條件會很有用。在這種情況下,可以使用函式而不是選擇器來定義定位器。以下範例會等待 MutationObserver 偵測到頁面上出現 HTMLCanvasElement
元素。您也可以在函式定位器上呼叫其他定位器函式,例如 .click()
或 .fill()
。
await page
.locator(() => {
let resolve!: (node: HTMLCanvasElement) => void;
const promise = new Promise(res => {
return (resolve = res);
});
const observer = new MutationObserver(records => {
for (const record of records) {
if (record.target instanceof HTMLCanvasElement) {
resolve(record.target);
}
}
});
observer.observe(document);
return promise;
})
.wait();
在定位器上套用篩選器
以下範例說明如何將以 JavaScript 函式表示的額外條件新增至定位器。只有當按鈕元素的 innerText
為「My button」時,才會點擊該元素。
await page
.locator('button')
.filter(button => button.innerText === 'My button')
.click();
從定位器傳回值
map
函式允許將元素對應至 JavaScript 值。在這種情況下,呼叫 wait()
將傳回還原序列化的 JavaScript 值。
const enabled = await page
.locator('button')
.map(button => !button.disabled)
.wait();
從定位器傳回 ElementHandle
waitHandle
函式允許傳回 ElementHandle。如果沒有您需要的動作的對應定位器 API,這可能會很有用。
const buttonHandle = await page.locator('button').waitHandle();
await buttonHandle.click();
設定定位器
可以設定定位器來調整預先條件和其他選項
// Clicks on a button without waiting for any preconditions.
await page
.locator('button')
.setEnsureElementIsInTheViewport(false)
.setVisibility(null)
.setWaitForEnabled(false)
.setWaitForStableBoundingBox(false)
.click();
定位器逾時
依預設,定位器會繼承頁面的逾時設定。但是,可以針對每個定位器設定逾時。如果在指定的時間段內找不到元素或未滿足預先條件,將會擲回 TimeoutError。
// Time out after 3 sec.
await page.locator('button').setTimeout(3000).click();
取得定位器事件
目前,定位器支援 單一事件,該事件會在定位器即將執行動作時通知您,表示已滿足預先條件
let willClick = false;
await page
.locator('button')
.on(LocatorEvent.Action, () => {
willClick = true;
})
.click();
此事件可用於記錄/偵錯或其他用途。如果定位器重試動作,則事件可能會觸發多次。
waitForSelector
waitForSelector
是相較於定位器較低階的 API,允許等待元素在 DOM 中可用。如果動作失敗,它不會自動重試,並且需要手動處置產生的 ElementHandle 以防止記憶體洩漏。該方法存在於 Page、Frame 和 ElementHandle 執行個體上。
// Import puppeteer
import puppeteer from 'puppeteer';
// Launch the browser.
const browser = await puppeteer.launch();
// Create a page.
const page = await browser.newPage();
// Go to your site.
await page.goto('YOUR_SITE');
// Query for an element handle.
const element = await page.waitForSelector('div > .class-name');
// Do something with element...
await element.click(); // Just an example.
// Dispose of handle.
await element.dispose();
// Close browser.
await browser.close();
某些頁面層級 API(例如 page.click(selector)
、page.type(selector)
、page.hover(selector)
)是使用 waitForSelector
為了向後相容性而實作的。
不等待的查詢
有時您知道元素已在頁面上。在這種情況下,Puppeteer 提供多種方式來尋找符合選擇器的單一元素或多個元素。這些方法存在於 Page、Frame 和 ElementHandle 執行個體上。
page.$()
傳回符合選擇器的單一元素。page.$$()
傳回所有符合選擇器的元素。page.$eval()
傳回在符合選擇器的第一個元素上執行 JavaScript 函式的結果。page.$$eval()
傳回在每個符合選擇器的元素上執行 JavaScript 函式的結果。
選擇器
Puppeteer 在每個接受選擇器的 API 中都接受 CSS 選擇器。此外,您可以選擇使用其他選擇器語法來執行 CSS 選擇器所能提供的更多功能。
非 CSS 選擇器
Puppeteer 使用自訂 虛擬元素 擴充了 CSS 語法,這些虛擬元素定義如何使用非 CSS 選擇器選取元素。Puppeteer 支援的虛擬元素以 -p
供應商前置詞作為開頭。
XPath 選擇器 (-p-xpath
)
XPath 選擇器將使用瀏覽器的原生 Document.evaluate
來查詢元素。
// Runs the `//h2` as the XPath expression.
const element = await page.waitForSelector('::-p-xpath(//h2)');
文字選擇器 (-p-text
)
文字選擇器會選取包含給定文字的「最小」元素,即使在 (開啟) shadow root 內也是如此。在這裡,「最小」表示包含給定文字的最深層元素,但不包含其父元素 (在技術上也會包含給定文字)。
// Click a button inside a div element that has Checkout as the inner text.
await page.locator('div ::-p-text(Checkout)').click();
// You need to escape CSS selector syntax such '(', ')' if it is part of the your search text ('Checkout (2 items)').
await page.locator(':scope >>> ::-p-text(Checkout \\(2 items\\))').click();
// or use quotes escaping any quotes that are part of the search text ('He said: "Hello"').
await page.locator(':scope >>> ::-p-text("He said: \\"Hello\\"")').click();
ARIA 選擇器 (-p-aria
)
ARIA 選擇器可用於透過計算出的可存取名稱和角色來尋找元素。這些標籤是使用瀏覽器內部對可存取性樹狀結構的表示法計算得出的。這意味著在執行查詢之前,會先解析諸如 labeledby 之類的 ARIA 關係。如果您不想依賴任何特定的 DOM 結構或 DOM 屬性,ARIA 選擇器會很有用。
await page.locator('::-p-aria(Submit)').click();
await page.locator('::-p-aria([name="Click me"][role="button"])').click();
穿透選擇器 (pierce/
)
穿透選擇器是一種選擇器,它會傳回文件中所有陰影根 (shadow roots) 中符合所提供 CSS 選擇器的所有元素。我們建議改用深層組合器,因為它們在組合不同選擇器時提供了更大的彈性。pierce/
僅在帶前綴的表示法中可用。
await page.locator('pierce/div').click();
// Same query as the pierce/ one using deep combinators.
await page.locator('& >>> div').click();
在 Shadow DOM 中查詢元素
CSS 選擇器不允許進入 Shadow DOM,因此,Puppeteer 將兩個組合器新增至 CSS 選擇器語法,以便在 shadow DOM 內部進行搜尋。
>>>
組合器
>>>
被稱為「深層後代」組合器。它類似於 CSS 的後代組合器(以單個空格字元
表示,例如 div button
),它會選擇父元素下任何深度的符合元素。例如,my-custom-element >>> button
會選擇 my-custom-element
(陰影主機)的 shadow DOM 中可用的所有按鈕元素。
深層組合器僅適用於 CSS 選擇器的第一「層」和打開的陰影根;例如,:is(div > > a)
將不起作用。
>>>>
組合器
>>>>
被稱為「深層子代」組合器。它類似於 CSS 的子代組合器(以 >
表示,例如 div > button
),它會選擇父元素直接的陰影根下的符合元素(如果元素有的話)。例如,my-custom-element >>>> button
會選擇 my-custom-element
(陰影主機)直接的陰影根中可用的所有按鈕元素。
自訂選擇器
您也可以使用 Puppeteer.registerCustomQueryHandler 來新增自己的虛擬元素。這對於基於框架物件或您的應用程式建立自訂選擇器很有用。
例如,您可以使用 react-component
虛擬元素撰寫所有選擇器,並實作自訂邏輯來解析提供的 ID。
Puppeteer.registerCustomQueryHandler('react-component', {
queryOne: (elementOrDocument, selector) => {
// Dummy example just delegates to querySelector but you can find your
// React component because this callback runs in the page context.
return elementOrDocument.querySelector(`[id="${CSS.escape(selector)}"]`);
},
queryAll: (elementOrDocument, selector) => {
// Dummy example just delegates to querySelector but you can find your
// React component because this callback runs in the page context.
return elementOrDocument.querySelectorAll(`[id="${CSS.escape(selector)}"]`);
},
});
在您的應用程式中,您現在可以如下撰寫選擇器。
await page.locator('::-p-react-component(MyComponent)').click();
// OR used in conjunction with other selectors.
await page.locator('.side-bar ::-p-react-component(MyComponent)').click();
另一個範例示範如何定義自訂查詢處理常式以定位 vue 元件
請小心依賴程式庫或框架的內部 API。它們隨時可能會變更。
Puppeteer.registerCustomQueryHandler('vue', {
queryOne: (element, name) => {
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
do {
const currentNode = walker.currentNode;
if (
currentNode.__vnode?.ctx?.type?.name.toLowerCase() ===
name.toLocaleLowerCase()
) {
return currentNode;
}
} while (walker.nextNode());
return null;
},
});
搜尋指定的檢視元件如下
const element = await page.$('::-p-vue(MyComponent)');
帶前綴的選擇器語法
雖然我們維護帶前綴的選擇器,但建議的方式是使用上面所述的選擇器語法。
也支援以下舊版語法 (${nonCssSelectorName}/${nonCssSelector}
),它允許一次執行單個非 CSS 選擇器。請注意,此語法不允許組合多個選擇器。
// Same as ::-p-text("My text").
await page.locator('text/My text').click();
// Same as ::-p-xpath(//h2).
await page.locator('xpath///h2').click();
// Same as ::-p-aria(My label).
await page.locator('aria/My label').click();
await page.locator('pierce/div').click();