Wenn wget versagt: PhantomJS

Aus einem gewissen Grund brauche ich regelmäßig die aktuellen IP-Ranges von Amazon EC2 (ein Produkt für Cloud Computing). Zum Glück gibt es im entsprechenden Amazon-Forum einen Thread, in dem immer die aktuellen IP-Ranges aufgelistet sind. Bis vor einiger Zeit konnte ich diesen Thread problemlos wie folgt herunterladen, um ihn dann mit einem regulären Ausdruck nach IP-Ranges zu durchsuchen:

wget -O amazon_ec2.txt https://forums.aws.amazon.com/ann.jspa?annID=1701

Nun geht das nicht mehr — man erhält nur eine Fehlermeldung, dass der Browser JavaScript unterstützen muss. Das Forum arbeitet jetzt scheinbar Client-seitig mit JavaScript, und wget kann kein JavaScript ausführen.

Doch zum Glück gibt es PhantomJS! Dabei handelt es sich um ein Tool, das sozusagen einen Browser simuliert und mit JavaScript gesteuert werden kann. Mit folgendem Skript kann ich die Seite laden und ihren HTML-Quellcode ausgeben, womit das Problem gelöst ist:

var page = require("webpage").create();
page.open("https://forums.aws.amazon.com/ann.jspa?annID=1701",
 function(status) {
  var f = function() {
   var html = page.evaluate(function() { return document.documentElement.innerHTML; });
   console.log(html);
   phantom.exit();
  };
  setTimeout(f, 10000);
 }
);

Die Verzögerung von 10 Sekunden sorgt dafür, dass alle Umleitungen (mit hoher Wahrscheinlichkeit) abgeschlossen sind, bevor der HTML-Quellcode ausgegeben wird. Es geht wahrscheinlich auch eleganter, aber es erfüllt seinen Zweck.

Bislang habe ich für solche Zwecke übrigens Awesomium benutzt, was auch ganz nett ist, aber hier gibt es schon seit Ewigkeiten keine 64-Bit-Version mehr für Linux (eigentlich ein Unding). Vielleicht werde ich meine schon existierenden Anwendungen, die auf Awesomium basieren, auf PhantomJS portieren.

Nachtrag (1. Oktober 2016): Dieser umständliche Weg ist für den konkreten Anwendungsfall nicht mehr nötig, da Amazon die IP-Ranges nun als JSON-Datei bereitstellt.

11 Gedanken zu „Wenn wget versagt: PhantomJS

  1. Alex

    Muss am Dienstag ein projekt in der Uni abgeben und ich muss den Code erklären können 🙂

    Ich versteh eigenltich alles, außer das mit dem TimeOut, was hat es damit auf sich?

    WEnn ich z.B: diese Zeile mit dem Timeout weglasse funktioniert gar nichts mehr…

    Könntest du mir das vlt ggaaaannz kurz mal erklären?

    Das wäre nice 🙂

    bis denne

    Antworten
      1. Alex

        oh man, ja klar, weil f ist ja unsere Funktion die eigentlicht „alles macht“ 🙂

        Danke schon mal soweit (vor allem f die schnelle Antwort)

        –> Könnte man das vlt auch anders lösen? z.B., dass man immer wartet bis die funktion fertig mit Laden
        ist oder so?

        Danke schon mal

        Greetz

        Antworten
      2. Alex

        ich weiß nicht ob meine letzte Antwort angekommen ist von gestern…

        Ich hatte mich gefragt, ob man das mit dem Timeout noch anders lösen könnte? Weil du ja geschrieben hast, dass kann man bestimmt noch eleganter lösen..:

        Ich will es selbst umsetzen, aber was wäre denn eine elegantere Lösung?

        Ich dachte mir vlt., dass man abwarten könnte bis die Funktion immer mit dem Ergebnis zuück ist und dann erst weiter machen oder so? Lieg ich da schon nahe dran?

        1000 Dank / Gruß

        Antworten
        1. David Scherfgen Beitragsautor

          Das Problem ist, dass man manchmal nicht so einfach feststellen kann, wann eine Seite wirklich vollständig geladen ist. Die Seite könnte ja z. B. über Ajax noch dynamische Inhalte anfordern. Ich glaube, das war bei meinem ursprünglichen Anwendungsfall so. Man könnte die XMLHttpRequest-Klasse verändern und sich in alle Ajax-Requests „einklinken“, um abzuwarten, bis alle fertig sind. Das ist hier beschrieben.

          Antworten
  2. Alex

    wie ich das jetzt genau implementieren soll weiß ich nicht, könntest du mir vlt n kleinen Ansatz geben?

    Hab nen Crawler gebaut (der nur HTML interpr. kann), dann hab ich dazwischen einfach Phantom JS gehängt, damit mir dieser dann JS Inhalten auswerten kann und rufe dann immer mit dem Content der Webseiten diese File hier auf:

    page.open(url,
    function (status) {
    var f = function () {

    var html = page.evaluate(function () { return document.documentElement.innerHTML });

    fs.write(file_name, html, ‚w‘);

    phantom.exit();
    };
    setTimeout(f, 10);
    }
    );

    also eigenltich wie Deins 🙂

    Kansnt du mir vlt nen Tip noch abschließend geben, WIE genau ich die STackvoverflow Funktion in meinen bisherigen Code einbauen könnte, um die sich in dei Ajax Requests zu hooken um abzuwarten bis alles wirklich fertig ist?

    Wenn du die Zeit und die Lust nicht dazu hast würde ich das natürlich verstehen 🙂

    1000 Dank schon mal 🙂

    Antworten
    1. David Scherfgen Beitragsautor

      Willst du wirklich nur 10 Millisekunden warten? Den Code, der die Klasse kapselt, müsste ausgeführt werden, bevor die Seite geladen wird. Er muss natürlich noch angepasst werden, schließlich willst du darüber Buch führen, wie viele Requests noch offen sind und erst dann weiter machen, wenn diese Zahl bei 0 angekommen ist. An deiner Stelle würde ich es aber einfach bei einem festen Timeout belassen.

      Antworten
  3. Alex

    ok gut 🙂 Ich nehme Deinen Ratschlag an, nur ich muss morgen auch erklären können WAS ich da gerade tu UND wie die Alternative aussehe…

    Ich würde es dann vlt so formulieren:

    – Wir öffnen uns eine URL mit page.open(url,
    – f ist unsere Callback Funktion (oder?)
    – welche dann eigenltich in HTML mittels evaluate den HTML Content in die Variabel HTML speichert
    – Consolen Ausagbe is klar!
    – fs.write ist auch klar
    – pahntom.exit(); versteh ich nicht so ganz –> heißt das jetzt wird pahntom js verlassen
    – und meisten fällt es mri schwer, den Timeout richtig zu interpretiren
    —–> heißt das, dass mein Timout 10ms beträgt, aber wenn die Seiten länger als 10ms (weil derzeit benötigen alle Seiten so im Schnitt 5 Sek.) brauchen, wird das interpretieren einfach abgebrochen sowie man sich nen Timout halt vorstellt? oder heißt das etwas anderes?

    Danke nochmal 🙂

    Grüße

    Antworten

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.