【问题标题】:Puppeteer - how to select an element based on its inner text?Puppeteer - 如何根据内部文本选择元素?
【发布时间】:2021-01-10 21:10:02
【问题描述】:

我正在使用 Puppeteer 抓取一堆页面。内容不区分类/ID/等。并且在页面之间以不同的顺序呈现。因此,我需要根据元素的内部文本来选择元素。我在下面包含了一个简化的示例 html:

<table>
<tr>
    <th>Product name</th>
    <td>Shakeweight</td>
</tr>
<tr>
    <th>Product category</th>
    <td>Exercise equipment</td>
</tr>
<tr>
    <th>Manufacturer name</th>
    <td>The Shakeweight Company</td>
</tr>
<tr>
    <th>Manufacturer address</th>
    <td>
        <table>
            <tr><td>123 Fake Street</td></tr>
            <tr><td>Springfield, MO</td></tr>
        </table>
    </td>
</tr>

在本例中,我需要获取制造商名称和制造商地址。所以我想我需要根据嵌套 th 的内部文本选择适当的 tr 并在同一个 tr 中刮取关联的 td。请注意,此表的行顺序并不总是相同,并且该表包含的行比这个简化示例多得多,所以我不能只选择第 3 和第 4 个 td。

我尝试使用如下 XPATH 选择基于内部文本的元素,但它似乎不起作用:

var manufacturerName = document.evaluate("//th[text()='Manufacturer name']", document, null, XPathResult.ANY_TYPE, null)

这甚至不是我需要的数据(它将是与此相关联的 td),但我认为这至少是第 1 步。如果有人可以提供有关通过内部文本选择或选择与此相关联的 td 的策略的输入,我将不胜感激。

【问题讨论】:

    标签: javascript node.js puppeteer innertext


    【解决方案1】:

    你可以这样做来获取数据:

    await page.goto(url, { waitUntil: 'networkidle2' }); // Go to webpage url
    
    await page.waitFor('table'); //waitFor an element that contains the text
    
    const textDataArr = await page.evaluate(() => {
        const element = document.querySelector('table tbody tr:nth-child(3) td'); // select thrid row td element like so
        return element && element.innerText; // will return text and undefined if the element is not found
    });
    console.log(textDataArr);
    

    【讨论】:

    • 感谢您的回复-不幸的是,此表的行顺序并不总是相同,所以我不能只选择第 3 和第 4 td。也没有 id 或类 - 我需要根据同一 tr 的 th 的内部文本选择 td,即“制造商名称”或“制造商地址”
    • 我为您在此处阐明的用例发布了一个新答案,请尝试适用于您的逻辑。
    【解决方案2】:

    根据您在上述答案中的用例解释,这是用例的逻辑:

    await page.goto(url, { waitUntil: 'networkidle2' }); // Go to webpage url
    
    await page.waitFor('table'); //waitFor an element that contains the text
    
    const textDataArr = await page.evaluate(() => {
        const trArr = Array.from(document.querySelectorAll('table tbody tr'));
    
        //Find an index of a tr row where th innerText equals 'Manufacturer name'
        let fetchValueRowIndex = trArr.findIndex((v, i) => {
            const element = document.querySelector('table tbody tr:nth-child(i+1) th');
            return element.innerText === 'Manufacturer name';
        });
    
        //If the findex is found return the innerText of td of the same row else returns undefined
        return (fetchValueRowIndex > -1) ? document.querySelector(`table tbody tr:nth-child(${fetchValueRowIndex}+1) td`).innerText : undefined;
    });
    console.log(textDataArr);
    

    【讨论】:

      【解决方案3】:

      这确实是一个 xpath 问题,并不特定于 puppeteer,所以这个问题也可能会有所帮助,因为您需要找到 &lt;td&gt;,它位于您找到的 &lt;th&gt; 之后:@ 987654321@

      但是您的 xpath 确实 对我有用。在您问题中包含 HTML 的页面上的 Chrome DevTools 中,运行以下行来查询文档:

      $x('//th[text()="Manufacturer name"]')
      

      注意:$x() 是一个仅在 Chrome DevTools 中有效的辅助函数,尽管 Puppeteer 具有类似的 Page.$x 函数。

      该表达式应返回一个包含一个元素的数组,&lt;th&gt; 与查询中的文本。获取旁边的&lt;td&gt;

      $x('//th[text()="Manufacturer name"]/following-sibling::td')
      

      并获取其内部文本:

      $x('//th[text()="Manufacturer name"]/following-sibling::td')[0].innerText
      

      一旦您能够遵循该模式,您应该能够使用类似的策略在 puppeteer 中获取您想要的数据,类似于:

      const puppeteer = require('puppeteer');
      
      const main = async () => {
        const browser = await puppeteer.launch();
        const page = await browser.newPage();
        await page.goto('http://127.0.0.1:8080/');  // <-- EDIT THIS
      
        const mfg = await page.$x('//th[text()="Manufacturer name"]/following-sibling::td');
        const prop = await mfg[0].getProperty('innerText');
        const text = await prop.jsonValue();
        console.log(text);
      
        await browser.close();
      }
      
      main();
      

      【讨论】:

        【解决方案4】:

        一次获得所有这些的简单方法:

        let data = await page.evaluate(() => {
          return [...document.querySelectorAll('tr')].reduce((acc, tr, i) => {
            let cells = [...tr.querySelectorAll('th,td')].map(el => el.innerText)
            acc[cells[0]] = cells[1]
            return acc
          }, {})
        })
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-01-21
          • 2018-08-13
          • 2011-03-11
          • 2019-11-04
          • 1970-01-01
          • 2023-03-23
          • 2016-08-26
          • 1970-01-01
          相关资源
          最近更新 更多