webentwicklung-frage-antwort-db.com.de

DOM-Knotentext mit Puppeteer und headless Chrome erhalten

Ich versuche, headless Chrome und Puppeteer für die Ausführung unserer Javascript-Tests zu verwenden, aber ich kann die Ergebnisse nicht aus der Seite extrahieren. Basierend auf dieser Antwort sieht es so aus, als sollte ich page.evaluate() verwenden. Dieser Abschnitt hat sogar ein Beispiel, das aussieht, was ich brauche.

const bodyHandle = await page.$('body');
const html = await page.evaluate(body => body.innerHTML, bodyHandle);
await bodyHandle.dispose();

Als vollständiges Beispiel habe ich versucht, das in ein Skript zu konvertieren, das meinen Namen aus meinem Benutzerprofil in Stack Overflow extrahiert. Unser Projekt verwendet Knoten 6, also habe ich die await-Ausdrücke in .then() konvertiert.

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.$('h2.user-card-name').then(function(heading_handle) {
                page.evaluate(function(heading) {
                    return heading.innerText;
                }, heading_handle).then(function(result) {
                    console.info(result);
                    browser.close();
                }, function(error) {
                    console.error(error);
                    browser.close();
                });
            });
        });
    });
});

Wenn ich das ausführen, erhalte ich diesen Fehler:

$ node get_user.js 
TypeError: Converting circular structure to JSON
    at Object.stringify (native)
    at args.map.x (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/helper.js:30:43)
    at Array.map (native)
    at Function.evaluationString (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/helper.js:30:29)
    at Frame.<anonymous> (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:376:31)
    at next (native)
    at step (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:355:24)
    at Promise (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:373:12)
    at fn (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:351:10)
    at Frame._rawEvaluate (/mnt/data/don/git/Kive/node_modules/puppeteer/node6/FrameManager.js:375:3)

Das Problem scheint bei der Serialisierung des Eingabeparameters in page.evaluate() zu liegen. Ich kann Strings und Zahlen übergeben, aber keine Element-Handles. Ist das Beispiel falsch oder liegt ein Problem mit Knoten 6 vor? Wie kann ich den Text eines DOM-Knotens extrahieren?

8
Don Kirkby

Ich habe drei Lösungen für dieses Problem gefunden, je nachdem, wie kompliziert Ihre Extraktion ist. Die einfachste Option ist eine verwandte Funktion, die mir nicht aufgefallen ist: page.$eval() . Im Grunde macht es das, was ich versucht habe: kombiniert page.$() und page.evaluate(). Hier ist ein Beispiel, das funktioniert:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.$eval('h2.user-card-name', function(heading) {
                return heading.innerText;
            }).then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});

Das gibt mir das erwartete Ergebnis:

$ node get_user.js 
Don Kirkby top 2% overall

Ich wollte etwas komplizierter extrahieren, aber schließlich wurde mir klar, dass die Bewertungsfunktion im Kontext der Seite ausgeführt wird. Das bedeutet, dass Sie beliebige Werkzeuge verwenden können, die in die Seite geladen werden, und dann einfach Zeichenfolgen und Zahlen hin und her senden. In diesem Beispiel verwende ich jQuery in einer Zeichenfolge, um das zu extrahieren, was ich will:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.evaluate("$('h2.user-card-name').text()").then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});

Das gibt mir ein Ergebnis mit dem Whitespace intact:

$ node get_user.js 

                            Don Kirkby

                                top 2% overall

In meinem echten Skript möchte ich den Text mehrerer Knoten extrahieren, daher brauche ich eine Funktion anstelle einer einfachen Zeichenfolge:

const puppeteer = require('puppeteer');

puppeteer.launch().then(function(browser) {
    browser.newPage().then(function(page) {
        page.goto('https://stackoverflow.com/users/4794').then(function() {
            page.evaluate(function() {
                return $('h2.user-card-name').text();
            }).then(function(result) {
                console.info(result);
                browser.close();
            });
        });
    });
});

Das ergibt genau das gleiche Ergebnis. Jetzt muss ich die Fehlerbehandlung hinzufügen und möglicherweise die Einrückungsstufen reduzieren.

9
Don Kirkby

Mit await/async und $eval sieht die Syntax folgendermaßen aus:

await page.goto('https://stackoverflow.com/users/4794')
const nameElement = await context.page.$eval('h2.user-card-name', el => el.text())
console.log(nameElement)
3
RobLoach

Ich benutze Seite. $ Eval

const text = await page.$eval('h2.user-card-name', el => el.innerText );
console.log(text);

1
vnguyen

Ich hatte Erfolg mit folgendem:

const browser = await puppeteer.launch();
try {
  const page = await browser.newPage();
  await page.goto(url);
  await page.waitFor(2000);
  let html_content = await page.evaluate(el => el.innerHTML, await page.$('.element-class-name'));
  console.log(html_content);
} catch (err) {
  console.log(err);
}

Ich hoffe es hilft.

0
Darren Hall