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')


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.


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.


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

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()

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

5

Die Lösung ist

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

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

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 .


-1

Ich musste:

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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.