Puppeteer: How to evaluate an XPath with a context node?

Viewed 192

From the doc: enter image description here

So I tried this code:

const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();
    await page.goto('http://personalitycore.com/a.html');
    let p = (await page.$x('/html/body/p'))[0]
    console.log("Var[p] Class: " + p.constructor.name)
    console.log("Var[p] Tag: " + await p.evaluate(e => e.tagName, p))
    let spans = await p.$x('/*')
    for (var i = 0; i < spans.length; i++) {
        console.log("Var[spans] Tag: " + await spans[i].evaluate(e => e.tagName, spans[i]))
        console.log("Var[spans] Text: " + await spans[i].evaluate(e => e.textContent, spans[i]))
    }
    await browser.close();
})();

And the HTML of http://personalitycore.com/a.html is:

<head>
</head>
<body>
<p>
text_node1
<span>span_node1</span>
text_node2
<span>span_node2</span>
</p>
</body>

The results:

/usr/local/bin/node example.js
Var[p] Class: ElementHandle
Var[p] Tag: P
Var[spans] Tag: HTML
Var[spans] Text: 

text_node1
span_node1
text_node2
span_node2

I got confused. According to the document, p is an ElementHandle and evaluate xpath /* should get [TextNode, Span, TextNode, Span].

But it returned the whole page, with the tag HTML!

So, my question:

  1. Is there any mistake in my code so I don't get the expected result?
  2. How to evaluate an XPath with a context node? In my example, I want to evaluate /* on the tag p.
1 Answers

You just need to add the context node symbol (a dot) to the XPath: './*'. Without it, the '/*' means 'all the children of the document', i.e. the html element.

import puppeteer from 'puppeteer';

const browser = await puppeteer.launch();

const html = `
  <!doctype html>
  <html>
    <head>
    </head>
    <body>
      <p>
        text_node1
        <span>span_node1</span>
        text_node2
        <span>span_node2</span>
      </p>
    </body>
  </html>`;

try {
  const page = await browser.newPage();
  await page.goto('http://personalitycore.com/a.html');

  const [p] = await page.$x('/html/body/p');
  console.log("Var[p] Class: " + p.constructor.name);
  console.log("Var[p] Tag: " + await p.evaluate(e => e.tagName, p));

  const spans = await p.$x('./*');
  for (let i = 0; i < spans.length; i++) {
      console.log("Var[spans] Tag: " + await spans[i].evaluate(e => e.tagName, spans[i]));
      console.log("Var[spans] Text: " + await spans[i].evaluate(e => e.textContent, spans[i]));
  }
} catch(err) { console.error(err); } finally { await browser.close(); }

Output:

Var[p] Class: ElementHandle
Var[p] Tag: P
Var[spans] Tag: SPAN
Var[spans] Text: span_node1
Var[spans] Tag: SPAN
Var[spans] Text: span_node2
Related