So klicken Sie in Puppeteer auf ein Element mit Text

88

Gibt es eine Methode (in der API nicht gefunden) oder eine Lösung, um auf ein Element mit Text zu klicken?

Zum Beispiel habe ich HTML:

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

Und ich möchte auf ein Element klicken, in das Text eingeschlossen ist (klicken Sie auf die Schaltfläche in .elements), wie:

Page.click('Button text', '.elements')
Aleksandr Golubovskij
quelle
2
Teilen Sie
Md. Abu Taher
Hast du eine Antwort gefunden?
Shubham Batra
Wenn es hilft, wurde auf der Seite, auf die ich klicken wollte, jQuery geladen, sodass ich die Auswertungsmethode zum Ausführen des jQuery-Codes verwenden konnte.
Brandito

Antworten:

82

Die aktuelle Top-Antwort von tokland funktioniert nur auf Textknoten und nicht auf Knoten mit anderen Elementen.

Kurze Antwort

Dieser XPath-Ausdruck fragt eine Schaltfläche ab, die den Text "Schaltflächentext" enthält:

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

<div class="elements">Verwenden Sie den folgenden Code, um auch die Umgebung der Schaltflächen zu berücksichtigen:

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

Erläuterung

text()Schauen wir uns ein Beispiel an, um zu erklären, warum die Verwendung des Textknotens ( ) in einigen Fällen falsch ist:

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

Lassen Sie uns zunächst die Ergebnisse überprüfen, wenn Sie Folgendes verwenden contains(text(), 'Text'):

  • //button[contains(text(), 'Start')]gibt beide zwei Knoten zurück (wie erwartet)
  • //button[contains(text(), 'End')]gibt nur einen Knoten (den ersten) zurück, da text()eine Liste mit zwei Texten ( Startund End) zurückgegeben containswird, überprüft aber nur den ersten
  • //button[contains(text(), 'Middle')] gibt keine Ergebnisse zurück, da text()der Text der untergeordneten Knoten nicht enthalten ist

Hier sind die XPath-Ausdrücke für contains(., 'Text'), die für das Element selbst einschließlich seiner untergeordneten Knoten funktionieren:

  • //button[contains(., 'Start')]kehren beide zwei Tasten
  • //button[contains(., 'End')]wird wieder die beiden Rück zwei Tasten
  • //button[contains(., 'Middle')] gibt eins zurück (die letzte Taste)

In den meisten Fällen ist es daher sinnvoller, den Ausdruck .anstelle von text()in einem XPath-Ausdruck zu verwenden.

Thomas Dondorf
quelle
Warum ist das nicht die beste Antwort? nette Erklärung vielen Dank!
Gianlucca
1
etwas, das mit jeder Art von Element funktioniert? Ich kann nicht wissen, ob sich der Text in einer Schaltfläche, einem
AP
6
@AndreaBisello Sie können //*[...]stattdessen verwenden.
Thomas Dondorf
91

Sie können einen XPath-Selektor mit Seite verwenden. $ X (Ausdruck) :

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");
}

Ein vollständiges Beispiel finden Sie clickByTextin dieser Übersicht . Es kümmert sich um das Entkommen von Anführungszeichen, was bei XPath-Ausdrücken etwas schwierig ist.

tokland
quelle
Genial - Ich habe versucht, es für andere Tags zu tun, kann es aber nicht zum Laufen bringen. (li, h1, ...) Wie würdest du das machen?
Rune Jeppesen
3
@RuneJeppesen Ersetzen //a[containsdurch //*[contains, um ein beliebiges Element auszuwählen, nicht nur ein anchor ( a) -Element.
Unixmonkey
16

Sie können auch page.evaluate()auf Elemente klicken document.querySelectorAll(), die nach Textinhalten gefiltert wurden:

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

Alternativ können page.evaluate()Sie ein Element basierend auf seinem Textinhalt mit document.evaluate()und einem entsprechenden XPath-Ausdruck anklicken :

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();
});
Grant Miller
quelle
14

schnelle Lösung gemacht, um erweiterte CSS-Selektoren wie ": enthält (Text)" verwenden zu können

Mit dieser Bibliothek können Sie also einfach

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

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

Hier ist meine Lösung:

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

Die Lösung ist

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

Es gibt keine unterstützte CSS-Auswahlsyntax für die Textauswahl oder eine Kombinatoroption. Meine Lösung hierfür wäre:

await page.$$eval('selector', selectorMatched => {
    for(i in selectorMatched)
      if(selectorMatched[i].textContent === 'text string'){
          selectorMatched[i].click();
          break;//Remove this line (break statement) if you want to click on all matched elements otherwise the first element only is clicked  
        }
    });
Ekeuwei
quelle
1

Es gibt viele Antworten, die darauf hindeuten, containsaber ich sehe keine Motivation für diese Ungenauigkeit angesichts des Anwendungsfalls von OP, der anscheinend genau mit einer Zielzeichenfolge von "Button text"und einem Element übereinstimmt <button>Button text</button>.

Ich würde es vorziehen, die präzisere zu verwenden [text()="Button text"], die ein falsches Positiv vermeidet, wenn die Schaltfläche beispielsweise lautet <button>Button text and more stuff</button>:

const [el] = await page.$x('//*[@class="elements"]//a[text()="Button text"]');
el && (await el.click());

Dies nimmt das entgegengesetzte Szenario dieser Antwort vorweg , das versucht, so freizügig wie möglich zu sein.

Viele Antworten haben auch die .elementsAnforderungen der Elternklasse verfehlt .

ggorlen
quelle
-1

Ich musste:

await this.page.$eval(this.menuSelector, elem => elem.click());

Alferd Nobel
quelle