Search code examples
javascriptpuppeteer

Iterating through child elements in puppeteer returns undefined


I'm a beginner with puppeteer and sorry if my question sounds dumb or I missed something. But when I try iterating through the parents node children elements and trying to get its innerText (that does exist) it returns undefined?

let Atsiskaitymai = await page.$$("#slenkanti_dalis > table > tbody > tr")

for (atsiskaitymas1 of Atsiskaitymai) {
  let s = await atsiskaitymas1.evaluate(x => Object.values(x.children))
  for (let i of s) {
    console.log(i.innerText); 
  }
  //returns undefined

Tried to do:

i.evaluate(x => x.innertext)
i.getAttribute('innerText')

Solution

  • There are multiple issues with the code, but the most fundamental misunderstanding is that it's not possible to return DOM nodes from the browser because they're complex, non-serializable circular structures (i.e. calling JSON.stringify() on them fails).

    Instead, try pulling out the serializable text content inside your evaluate (I'm using $$eval to do it all in one call and avoid element handles which are less reliable and awkward to work with):

    const sel = "#slenkanti_dalis tr";
    await page.waitForSelector(sel);
    const data = await page.$$eval(sel, rows =>
      rows.map(row =>
        [...row.querySelectorAll("td")].map(cell =>
          cell.textContent.trim()
        )
      )
    );
    console.table(data);
    

    It's also good to wait for the selector, since data is often added after the page loads. You can use the new locator API to do this, but since you may be on an older Puppeteer version, I went with the compatible approach.

    If this generic table scrape doesn't work for you, please share the page so I can see what I'm doing.

    As an aside, I like to avoid > in my CSS selectors, which is usually overly-specific and prone to fail. I'd only use > table > tbody > tr if I had to disambiguate a nested table, which I assume doesn't apply here.


    JS safety tip: for (atsiskaitymas1 of Atsiskaitymai) { should be for (const atsiskaitymas1 of Atsiskaitymai) {. Always use const or let to avoid creating a global. Using ESLint will pick this up: 'atsiskaitymas1' is not defined. (no-undef).

    Disclosure: I'm the author of the linked blog post.