【问题标题】:Puppeteer: how to click element that contains certain value in its descendantsPuppeteer:如何单击其后代中包含特定值的元素
【发布时间】:2021-03-02 19:02:17
【问题描述】:

我第一次使用 Puppeteer,我有这个代码可以点击某个元素:

await page.waitForSelector('.item-table > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');
await page.click('.item-table > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');

由于我在页面上有很多.item-table 元素,我想让它点击它的一个后代中有特定的文本(我不知道后代的级别)。 我已经在文档中甚至在 SO 问题中搜索了解决方案,但我找不到任何有用的东西。

我尝试添加> :contains("Foo bar"),但可能是错误的方法。事实上,它不起作用。

await page.waitForSelector('.item-table > :contains("Foo Bar") > .grid-item > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');
await page.click('.item-table > :contains("Foo Bar") > .grid-item-container > .grid-table-container > .grid-option-table:nth-child(1) > .grid-option:nth-child(1) > .grid-option-selectable > div');

那么,Puppeteer 怎么做呢?

编辑: 这是我要抓取的标记:

<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table"></div>
<div class="item-table">
    <div class="grid-item">
        <div class="grid-item-container">
            <div class="grid-table-container>
                <div class="grid-option-header">
                    <div class="grid-option-caption">
                        <div class="grid-option-name">
                            Foo Bar
                            <span>some other text</span>
                        </div>
                    </div>
                </div>
                <div class="grid-option-table">
                    <div class="grid-option">
                        <div class="grid-option-selectable">
                            <div></div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="item-table"></div>
<div class="item-table"></div>

所以,我想点击div,它位于grid-option-selectable div 中,在item-table div 中包含Foo Bar 的后代。

【问题讨论】:

标签: javascript puppeteer selector


【解决方案1】:

我将对您的标记稍作调整,以帮助验证代码是否有效:

<!-- ... same ... -->
<div class="grid-option-selectable">
    <div>You got me!</div>
</div>
<!-- ... same ... -->

添加一些结构相同但文本不匹配的同级也是一个不错的主意,以确保我们不会产生误报。

我们将尝试的策略是this answer 中描述的策略(同一线程中的this answer 提供替代方案,而this 单独线程相关):使用目标父对象上的xpath 查询在目标文本之间相互共享和可点击元素的后代文本,然后查询该父元素的可点击元素。

const puppeteer = require("puppeteer");

(async () => {
  const browser = await puppeteer.launch(/*{dumpio: true}*/);
  const page = await browser.newPage();
  await page.goto("http://localhost:8000");
  const xp = `
    //div[@class="item-table" 
          and descendant::*[contains(text(), "Foo Bar")]]
    //div[@class="grid-option-selectable"]
  `;
  const [el] = await page.$x(xp);
  console.log(await page.evaluate(el => el.innerText, el));
  await el.click();
  await browser.close();
})();

输出:

You got me!

请注意,我使用的是精确的类匹配,但如果您的目标元素上可能有多个类,则需要使用 contains 放宽查询。所以你可以这样写 xpath 表达式:

//div[contains(@class, "item-table")
      and .//*[contains(text(), "Foo Bar")]]
//div[contains(@class, "grid-option-selectable")]

【讨论】:

  • 它不起作用。原因可能是“你抓住了我!” text 有一个span 作为兄弟?
  • 我确实在 forEach 循环中使用 cheerio 获得了按钮。有什么方法可以将所选元素传递给 Puppeteer 以使其点击?
  • 您的实际标记必须与您显示的不同,因为此代码适用于您发布的内容。如果您有一个与 Cheerio 一起使用的选择器,为什么不在 Puppeteer 中使用相同的选择器呢?此外,如果 Cheerio 工作并且标记不是动态创建的,为什么还要使用 Puppeteer?请更新您的帖子以显示您正在抓取的实际标记并解释它是否是动态的。谢谢。
猜你喜欢
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 2017-09-18
  • 1970-01-01
  • 2019-07-03
  • 2013-03-21
  • 2020-06-15
  • 2019-06-20
相关资源
最近更新 更多