Home > OS >  How to get text inside frame with puppeteer
How to get text inside frame with puppeteer

Time:10-02

I want to extract the text result with the "input#Readonly1" selector. But unfortunately I get an empty text. (value contain an empty text)

const puppeteer = require('puppeteer');
var sleep = require('system-sleep');

(async () => {
    const browser = await puppeteer.launch({ headless: false });
    const page = await browser.newPage();
    await page.goto('https://www.mobilit.fgov.be/WebdivPub_FR/wmvpstv1_fr?SUBSESSIONID=16382865', { timeout: 8000 });

    const elementHandle = await page.$(
    'frame[name="AppWindow"]',);
  const frame = await elementHandle.contentFrame();
  await frame.type("input[name='Writable3']", 'VF33C9HYB84113557', { delay: 5 });
  await frame.$eval("button[name='btnOphalen']", el => el.click());
  const element = await frame.waitForSelector("input#Readonly1", {visible: true}); // select the element
  const value = await element.evaluate(el => el.textContent);
  console.log(value);
  
  await browser.close();
})(); 

I think this line of code is incorrect:

const element = await frame.waitForSelector("input#Readonly1", {visible: true});

CodePudding user response:

There seem to be two errors:

  1. const element = await frame.waitForSelector("input#Readonly1", {visible: true});

Checking the page you're using, it seems that input#Readonly1 is always visible, it only changes the value when you perform the query.

In the waitForTimeout documentation:

Warning: This API is now obsolete.
Use new Promise(r => setTimeout(r, milliseconds));
Causes your script to wait for the given number of milliseconds.

You can declare a function to make the puppeteer wait x time:

function delay(time) {
     return new Promise(function(resolve) {
         setTimeout(resolve, time)
     });
}

And then:

await frame.$eval("button[name='btnOphalen']", el => el.click());
await delay(5000);
const element = await frame.$("input#Readonly1");
  1. According to mdn: Node.textContent

For other node types, textContent returns the concatenation of the textContent of every child node, excluding comments and processing instructions. (This is an empty string if the node has no children.)

Since you are retrieving the text inside an input, use value instead of textContent

const value = await element.evaluate(el => el.value);

CodePudding user response:

The existing answer is correct that you would use .value rather than .textContent for getting the value from an input field. However, it also suggests a sleep/delay workaround in place of waiting for a proper predicate. Sleeping is inaccurate, resulting in either wasted time or false positives.

One better approach is using waitForFunction to compare the input's value before the click against its current value in a tight requestAnimationFrame loop. At the precise render that the value changes, the waitForFunction predicate will resolve and your code will continue on to the next line, taking no more or less time than it has to.

const puppeteer = require("puppeteer"); // ^18.0.4

let browser;
(async () => {
  browser = await puppeteer.launch({headless: true});
  const [page] = await browser.pages();
  const url = "https://www.mobilit.fgov.be/WebdivPub_FR/wmvpstv1_fr?SUBSESSIONID=16382865";
  await page.goto(url, {waitUntil: "load"});
  const el = await page.$('frame[name="AppWindow"]');
  const frame = await el.contentFrame();
  await frame.type("#Writable3", "VF33C9HYB84113557");
  const output = await frame.$("#Readonly1");
  const preValue = await output.evaluate(el => el.value);
  await frame.click("#btnOphalen");
  await frame.waitForFunction(preValue =>
    document.querySelector("#Readonly1").value !== preValue,
    {}, preValue
  );
  const value = await output.evaluate(el => el.value);
  console.log(value); // => Marque et type : PEUGEOT 307
})()
  .catch(err => console.error(err))
  .finally(() => browser?.close())
;

To get all fields, try:

await frame.$$eval('[id^="Readonly"]', els => els.map(el => el.value));
  • Related