16 Stimmen

Wie man auf ein click() Ereignis in PhantomJS wartet, bevor man fortfährt?

Phantomjs hat diese beiden wirklich praktischen Callbacks onLoadStarted und onLoadFinished, die es Ihnen ermöglichen, die Ausführung im Wesentlichen anzuhalten, während die Seite geladen wird. Aber ich habe gesucht und finde keine Entsprechung, wenn Sie auf eine Schaltfläche oder einen Hyperlink klicken(). Es passiert ein ähnliches Laden der Seite, aber onLoadStarted wird für dieses Ereignis nicht aufgerufen, vermutlich weil kein explizites page.open() erfolgt. Ich versuche eine saubere Möglichkeit zu finden, um die Ausführung während dieses Ladevorgangs auszusetzen.

One-Lösung besteht offensichtlich in verschachtelten setTimeout's, aber ich möchte diesen Ansatz vermeiden, weil er hacky ist und auf Versuch und Irrtum beruht, anstatt auf etwas Zuverlässigem und Robusterem wie dem Testen gegen etwas oder dem Warten auf ein Ereignis.

Gibt es einen spezifischen Callback für diese Art des Seitenaufbaus, den ich übersehen habe? Oder gibt es vielleicht eine Art generisches Code-Muster, das mit dieser Art von Dingen umgehen kann?

EDIT:

Ich habe immer noch nicht herausgefunden, wie ich es zum Anhalten bringen kann. Hier ist der Code, der die Funktion onLoadStarted() nicht aufruft, wenn ich den Befehl klicken() aufrufe:

var loadInProgress = false;

page.onLoadStarted = function() {
  loadInProgress = true;
  console.log("Laden gestartet");
};

page.onLoadFinished = function() {
  loadInProgress = false;
  console.log("Laden beendet");
};

page.open(loginPage.url, function (status) {
    if (status !== 'success') {
        console.log('Netzwerkzugriff nicht möglich');
        fs.write(filePath + errorState, 1, 'w');
        phantom.exit();
    } else {
        page.evaluate(function (loginPage, credentials) {
            console.log('innerhalb der loginPage evaluate Funktion...\n')
            document.querySelector('input[id=' + loginPage.userId + ']').value = credentials.username;
            document.querySelector('input[id=' + loginPage.passId + ']').value = credentials.password;      
            document.querySelector('input[id=' + loginPage.submitId + ']').click();
            //var aTags = document.getElementsByTagName('a')
            //aTags[1].click();
        }, loginPage, credentials);

        page.render(renderPath + 'postLogin.png');
        console.log('gerendert nach dem Login');

I habe überprüft, dass die ID korrekt ist. Das page.render() zeigt, dass die Informationen übermittelt werden, aber nur, wenn ich es in ein setTimeout() setze, sonst rendert es sofort und ich sehe nur die eingegebenen Anmeldeinformationen, bevor die Seite umgeleitet wird. Vielleicht fehlt mir noch etwas anderes?

13voto

Artjom B. Punkte 59691

Ich denke, die Funktionen onLoadStarted und onLoadFinished sind alles, was Sie brauchen. Nehmen Sie zum Beispiel das folgende Skript:

var page = require('webpage').create();

page.onResourceReceived = function(response) {
    if (response.stage !== "end") return;
    console.log('Antwort (#' + response.id + ', stage "' + response.stage + '"): ' + response.url);
};
page.onResourceRequested = function(requestData, networkRequest) {
    console.log('Anfrage (#' + requestData.id + '): ' + requestData.url);
};
page.onUrlChanged = function(targetUrl) {
    console.log('Neue URL: ' + targetUrl);
};
page.onLoadFinished = function(status) {
    console.log('Laden abgeschlossen: ' + status);
};
page.onLoadStarted = function() {
    console.log('Laden gestartet');
};
page.onNavigationRequested = function(url, type, willNavigate, main) {
    console.log('Versuche zu navigieren zu: ' + url);
};

page.open("http://example.com", function(status){
    page.evaluate(function(){
        // Klick
        var e = document.createEvent('MouseEvents');
        e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
        document.querySelector("a").dispatchEvent(e);
    });
    setTimeout(function(){
        phantom.exit();
    }, 10000);
});

Es zeigt

Versuche zu navigieren zu: http://example.com/
Anfrage (#1): http://example.com/
Laden gestartet
Neue URL: http://example.com/
Antwort (#1, stage "end"): http://example.com/
Laden abgeschlossen: Erfolg
Versuche zu navigieren zu: http://www.iana.org/domains/example
Anfrage (#2): http://www.iana.org/domains/example
Laden gestartet
Versuche zu navigieren zu: http://www.iana.org/domains/reserved
Anfrage (#3): http://www.iana.org/domains/reserved
Antwort (#2, stage "end"): http://www.iana.org/domains/example
Neue URL: http://www.iana.org/domains/reserved
Anfrage (#4): http://www.iana.org/\_css/2013.1/screen.css
Anfrage (#5): http://www.iana.org/\_js/2013.1/jquery.js
Anfrage (#6): http://www.iana.org/\_js/2013.1/iana.js
Antwort (#3, stage "end"): http://www.iana.org/domains/reserved
Antwort (#6, stage "end"): http://www.iana.org/\_js/2013.1/iana.js
Antwort (#4, stage "end"): http://www.iana.org/\_css/2013.1/screen.css
Antwort (#5, stage "end"): http://www.iana.org/\_js/2013.1/jquery.js
Anfrage (#7): http://www.iana.org/\_img/2013.1/iana-logo-header.svg
Anfrage (#8): http://www.iana.org/\_img/2013.1/icann-logo.svg
Antwort (#8, stage "end"): http://www.iana.org/\_img/2013.1/icann-logo.svg
Antwort (#7, stage "end"): http://www.iana.org/\_img/2013.1/iana-logo-header.svg
Anfrage (#9): http://www.iana.org/\_css/2013.1/print.css
Antwort (#9, stage "end"): http://www.iana.org/\_css/2013.1/print.css
Laden abgeschlossen: Erfolg

Es zeigt, dass beim Klicken auf einen Link das LoadStarted-Ereignis einmal und das NavigationRequested-Ereignis zweimal auftritt, weil es eine Weiterleitung gibt. Der Trick besteht darin, die Event-Handler hinzuzufügen, bevor die Aktion ausgeführt wird:

var page = require('webpage').create();

page.open("http://example.com", function(status){
    page.onLoadFinished = function(status) {
        console.log('Laden abgeschlossen: ' + status);
        page.render("test37_next_page.png");
        phantom.exit();
    };
    page.onLoadStarted = function() {
        console.log('Laden gestartet');
    };

    page.evaluate(function(){
        var e = document.createEvent('MouseEvents');
        e.initMouseEvent('click', true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
        document.querySelector("a").dispatchEvent(e);
    });
});

Wenn Sie diese Dinge tun müssen, ist es vielleicht an der Zeit, etwas anderes wie CasperJS auszuprobieren. Es läuft auf PhantomJS auf, hat aber eine viel bessere API zum Navigieren auf Webseiten.

8voto

Grigorii Chudnov Punkte 3028

Verwenden Sie den High-Level-Wrapper, nightmarejs. Sie können dort leicht darauf klicken und danach warten.

Hier ist der Code (Beispiele Abschnitt):

var Nightmare = require('nightmare');
new Nightmare()
  .goto('http://yahoo.com')
    .type('input[title="Search"]', 'github nightmare')
    .click('.searchsubmit')
    .run(function (err, nightmare) {
      if (err) return console.log(err);
      console.log('Fertig!');
    });

Weitere Beispiele und API-Nutzung finden Sie unter github

0voto

http203 Punkte 851

Hier ist mein Code basierend auf einigen anderen Antworten. In meinem Fall musste ich nicht speziell irgendweltes JavaScript auswerten. Ich musste nur darauf warten, dass die Seite fertig geladen wird.

var system = require('system');
if (system.args.length === 1) {
    console.log('Versuchen Sie, einige Argumente zu übergeben, wenn Sie dieses Skript aufrufen!');
}
else {
    var page = require('webpage').create();
    var address = system.args[1];

    page.open(address, function(status){
        page.onLoadFinished = function(status) {
            console.log(page.content);
            phantom.exit();
        };    
    });     
}

Speichern Sie das obige in einer Datei namens "scrape.js" und rufen Sie es auf diese Weise auf:

phantomjs --ssl-protocol=any --ignore-ssl-errors=true scrape.js https://www.example.com

Die SSL-bezogenen Parameter werden hinzugefügt, um andere Probleme zu vermeiden, die ich mit bestimmten HTTPS-Seiten hatte (bezogen auf Zertifikat-Lade-Probleme).

Hoffentlich hilft dies jemandem!

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X