Как нажать на элемент с текстом в Puppeteer


86

Есть ли какой-либо метод (не нашел в 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')

2
Поделился ответом здесь: stackoverflow.com/a/47829000/6161265
Md. Abu Taher

Вы нашли ответ?
Шубхам Батра

Если это помогает, на странице, на которую я хотел выполнить щелчок, загружен jQuery, поэтому я смог и использовал метод оценки для выполнения кода jQuery.
Brandito

Ответы:


78

Топовой ответ на tokland работает только на текстовых узлах , а не на узлах с другими элементами внутри.

Короткий ответ

Это выражение 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')]вернет оба двух узла (как и ожидалось)
  • //button[contains(text(), 'End')]вернет только один узел (первый), так как text()возвращает список с двумя текстами ( Startи End), но containsпроверит только первый
  • //button[contains(text(), 'Middle')] не будет возвращать не результаты, text()не включает в себя текст дочерних узлов

Вот выражения XPath для contains(., 'Text'), которые работают с самим элементом, включая его дочерние узлы:

  • //button[contains(., 'Start')]вернет обе две кнопки
  • //button[contains(., 'End')]снова вернет обе две кнопки
  • //button[contains(., 'Middle')] вернет один (последняя кнопка)

Поэтому в большинстве случаев имеет смысл использовать .вместо text()выражения XPath.


почему это не главный ответ? хорошее объяснение, большое спасибо!
Gianlucca

1
что-то, что работает с каждым типом элемента? я не могу знать, находится ли текст внутри кнопки, ap, div, span и т. д.
Андреа Бизелло

5
@AndreaBisello Вместо этого вы можете использовать //*[...].
Томас

90

Вы можете использовать селектор XPath со страницей. $ X (выражение) :

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этой сутью для полного примера. Он заботится об экранировании кавычек, что немного сложно с выражениями XPath.


Замечательно - я пытался сделать это для других тегов, но не могу заставить его работать. (li, h1, ...) Как бы вы это сделали?
Rune Jeppesen

3
@RuneJeppesen Замените //a[containsна, //*[containsчтобы выбрать любой элемент, а не только aэлемент anchor ( ).
Unixmonkey,

14

Вы также можете использовать 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();
});

13

сделали быстрое решение, чтобы иметь возможность использовать расширенные селекторы CSS, такие как ": contains (text)"

поэтому с помощью этой библиотеки вы можете просто

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

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

4

Вот мое решение:

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


0

Нет поддерживаемого синтаксиса селектора css для текстового селектора или опции комбинатора, моя работа для этого будет:

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

0

Есть много предложений, containsно я не вижу мотивации для этой неточности с учетом варианта использования OP, который, судя по всему, является точным совпадением с целевой строкой "Button text"и элементом <button>Button text</button>.

Я бы предпочел использовать более точный [text()="Button text"], чтобы избежать ложного срабатывания, когда кнопка, скажем <button>Button text and more stuff</button>,:

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

Это предполагает противоположный сценарий этого ответа, который пытается быть как можно более снисходительным.

Во многих ответах также пропущено требование .elementsродительского класса.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.