web-dev-qa-db-ja.com

操り人形師:テキスト付きの要素をクリック

テキスト付きの要素をクリックする方法(APIで見つからなかった)または解決策はありますか?

たとえば、私はhtmlを持っています:

<div class="elements">
    <button>Button text</button>
    <a href=#>Href text</a>
    <div>Div text</div>
</div>

そして、次のように、テキストがラップされている要素をクリックします(.elements内のボタンをクリック)。

Page.click('Button text', '.elements')

解決策はありますか?

50

page。$ x(expression) でXPathセレクターを使用できます。

const linkHandlers = await page.$x("//a[contains(text(), 'Some text')]");

if (linkHandlers.length > 0) {
  await linkHandlers[0].click();
} else {
  throw new Error("Link not found");
}

完全な例については、この中のclickByTextをご覧ください Gist 引用符のエスケープを処理しますが、XPath式では少し注意が必要です。

62
tokland

Toklandによる current top answer は、テキストノードでのみ機能し、内部に他の要素があるノードでは機能しません。

簡潔な答え

このXPath式は、テキスト「ボタンテキスト」を含むボタンを照会します。

const [button] = await page.$x("//button[contains(., 'Button text')]");
if (button) {
    await button.click();
}

ボタンを囲む<div class="elements">も尊重するには、次のコードを使用します。

const [button] = await page.$x("//div[@class='elements']/button[contains(., 'Button text')]");

説明

テキストノード(text())の使用がいくつかのケースで間違っている理由を説明するために、例を見てみましょう。

<div>
    <button>Start End</button>
    <button>Start <em>Middle</em> End</button>
</div>

まず、contains(text(), 'Text')を使用するときの結果を確認しましょう。

  • //button[contains(text(), 'Start')]は両方のtwoノードを返します(予想どおり)
  • //button[contains(text(), 'End')]は2つのテキスト(StartEnd)を含むリストを返すため、text()は最初のノードのみを返しますoneのみを返しますが、containsは最初のチェックのみ
  • //button[contains(text(), 'Middle')]には子ノードのテキストが含まれないため、text()noの結果を返します

以下に、contains(., 'Text')のXPath式を示します。これは、子ノードを含む要素自体に対して機能します。

  • //button[contains(., 'Start')]は両方のtwoボタンを返します
  • //button[contains(., 'End')]は再び両方のtwoボタンを返します
  • //button[contains(., 'Middle')]one(最後のボタン)を返します

したがって、ほとんどの場合、XPath式でtext()の代わりに.を使用する方が理にかなっています。

13
Thomas Dondorf

":contains(text)"のような高度なCSSセレクターを使用できるようにするための迅速なソリューションを作成しました

したがって、これを使用して library

const select = require ('puppeteer-select');

const element = await select(page).getElement('button:contains(Button text)');
await element.click()
6
user3493381

また、 page.evaluate() を使用して、 document.querySelectorAll() から取得したテキストコンテンツでフィルタリングされた要素をクリックすることもできます。

await page.evaluate(() => {
  [...document.querySelectorAll('.elements button')].find(element => element.textContent === 'Button text').click();
});

または、 page.evaluate() を使用して、 document.evaluate() と対応するXPath式を使用して、テキストコンテンツに基づいて要素をクリックすることもできます。

await page.evaluate(() => {
  const xpath = '//*[@class="elements"]//button[contains(text(), "Button text")]';
  const result = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null);

  result.iterateNext().click();
});
5
Grant Miller

解決策は

(await page.$$eval(selector, a => a
            .filter(a => a.textContent === 'target text')
))[0].click()
3
Alex A

私の解決策は次のとおりです。

let selector = 'a';
    await page.$$eval(selector, anchors => {
        anchors.map(anchor => {
            if(anchor.textContent == 'target text') {
                anchor.click();
                return
            }
        })
    });
3
Kamen