Heim >Web-Frontend >js-Tutorial >Webkriechen mit Knoten, Phantomjs und Reiter
Es ist im Verlauf eines Projekts weit verbreitet, dass Sie benutzerdefinierte Skripte schreiben müssen, um eine Vielzahl von Aktionen auszuführen. Solche einmaligen Skripte, die normalerweise über die Befehlszeile (CLI) ausgeführt werden, können für praktisch jede Art von Aufgabe verwendet werden. Nachdem ich im Laufe der Jahre viele solcher Skripte geschrieben habe, habe ich den Wert der Voraussetzung für die Einführung eines benutzerdefinierten CLI -Mikroframeworks gewachsen, um diesen Prozess zu erleichtern. Glücklicherweise machen Node.js und sein umfangreiches Paket -Ökosystem NPM es einfach, genau das zu tun. Unabhängig davon
Obwohl das Web-Crawling nicht unbedingt mit der Befehlszeile verbunden ist, wird sie häufig in bestimmten Problemdomänen wie automatisierten Funktionstests und Entdeckungserkennung verwendet. Dieses Tutorial zeigt, wie ein leichtes CLI -Framework implementiert wird, dessen unterstützte Aktionen sich um das Web -Crawling drehen. Hoffentlich bringt dies Ihre kreativen Säfte zum Fließen, unabhängig davon, ob Ihr Interesse spezifisch für das Kriechen oder für die Befehlszeile ist. Zu den abgedeckten Technologien gehören Node.js, Phantomjs und eine Auswahl von NPM -Paketen, die sowohl auf das Kriechen als auch auf die CLI im Zusammenhang mitbezogen werden.
Der Quellcode für dieses Tutorial finden Sie in GitHub. Um die Beispiele auszuführen, müssen Sie sowohl Node.js als auch Phantomjs installieren lassen. Anweisungen zum Herunterladen und Installieren finden Sie hier: Node.js, Phantomjs.
im Mittelpunkt eines CLI -Frameworks steht das Konzept der Umwandlung eines Befehls, der normalerweise eine oder mehrere optionale oder erforderliche Argumente in eine konkrete Aktion enthält. Zwei NPM -Pakete, die in dieser Hinsicht sehr hilfreich sind, sind Kommandant und prompt.
MitCommander können Sie definieren, welche Argumente unterstützt werden, während Sie die Eingabeaufforderung (angemessen genug) den Benutzer zur Eingabe zur Laufzeit auffordern können. Das Endergebnis ist eine syntaktisch süße Schnittstelle zur Ausführung einer Vielzahl von Aktionen mit dynamischen Verhaltensweisen basierend auf einigen von Benutzer gelieferten Daten.
Sagen Sie zum Beispiel, wir möchten, dass unser Befehl so aussieht:
$ <span>node run.js -x hello_world </span>
Unser Einstiegspunkt (run.js) definiert die möglichen Argumente wie folgt:
program <span>.version('1.0.0') </span> <span>.option('-x --action-to-perform [string]', 'The type of action to perform.') </span> <span>.option('-u --url [string]', 'Optional URL used by certain actions') </span> <span>.parse(process.argv); </span>
und definiert die verschiedenen Benutzereingangsfälle wie folgt:
<span>var performAction = require('./actions/' + program.actionToPerform) </span> <span>switch (program.actionToPerform) { </span> <span>case 'hello_world': </span> prompt<span>.get([{ </span> <span>// What the property name should be in the result object </span> <span>name: 'url', </span> <span>// The prompt message shown to the user </span> <span>description: 'Enter a URL', </span> <span>// Whether or not the user is required to enter a value </span> <span>required: true, </span> <span>// Validates the user input </span> <span>conform: function (value) { </span> <span>// In this case, the user must enter a valid URL </span> <span>return validUrl.isWebUri(value); </span> <span>} </span> <span>}], function (err<span>, result</span>) { </span> <span>// Perform some action following successful input </span> <span>performAction(phantomInstance, result.url); </span> <span>}); </span> <span>break; </span><span>} </span>
Zu diesem Zeitpunkt haben wir einen grundlegenden Pfad definiert, durch den wir eine Aktion für die Ausführung angeben und eine Eingabeaufforderung hinzugefügt haben, eine URL zu akzeptieren. Wir müssen nur ein Modul hinzufügen, um die für diese Aktion spezifische Logik zu verarbeiten. Wir können dies tun, indem wir dem Aktionsverzeichnis eine Datei namens Hello_world.js hinzufügen:
<span>'use strict'; </span> <span>/** </span><span> * <span>@param Horseman phantomInstance </span></span><span> * <span>@param string url </span></span><span> */ </span>module<span>.exports = function (phantomInstance<span>, url</span>) { </span> <span>if (!url || typeof url !== 'string') { </span> <span>throw 'You must specify a url to ping'; </span> <span>} else { </span> <span>console.log('Pinging url: ', url); </span> <span>} </span> phantomInstance <span>.open(url) </span> <span>.status() </span> <span>.then(function (statusCode) { </span> <span>if (Number(statusCode) >= 400) { </span> <span>throw 'Page failed with status: ' + statusCode; </span> <span>} else { </span> <span>console.log('Hello world. Status code returned: ', statusCode); </span> <span>} </span> <span>}) </span> <span>.catch(function (err) { </span> <span>console.log('Error: ', err); </span> <span>}) </span> <span>// Always close the Horseman instance </span> <span>// Otherwise you might end up with orphaned phantom processes </span> <span>.close(); </span><span>}; </span>
Wie Sie sehen, erwartet das Modul, mit einer Instanz eines Phantomjs -Objekts (Phantominstance) und einer URL (URL) versorgt zu werden. Wir werden uns mit den Einzelheiten der Definition einer Phantomjs -Instanz für einen Moment einlassen, aber im Moment reicht es aus, dass wir den Grundstein für die Auslösen einer bestimmten Aktion gelegt haben. Nachdem wir eine Konvention eingesetzt haben, können wir leicht neue Aktionen auf definierte und vernünftige Weise hinzufügen.
Horseman ist ein Node.js -Paket, das eine leistungsstarke Schnittstelle zum Erstellen und Interagieren mit Phantomjs -Prozessen bietet. Eine umfassende Erklärung von Horseman und seinen Merkmalen würde seinen eigenen Artikel rechtfertigen, aber es reicht aus, um zu sagen, dass Sie einfach nur jedes Verhalten simulieren können, das ein menschlicher Benutzer in seinem Browser aufweisen könnte. Horseman bietet eine breite Palette von Konfigurationsoptionen, einschließlich der automatischen Injektion von JQuery und dem Ignorieren von SSL -Zertifikat -Warnungen. Es bietet auch Funktionen für das Umgang mit dem Cookie und das Aufnehmen von Screenshots.
Jedes Mal, wenn wir eine Aktion über unser CLI -Framework auslösen, instanziiert unser Eintragskript (run.js) eine Instanz von Reiter und gibt es an das angegebene Aktionsmodul weiter. In Pseudo-Code sieht es so aus:
<span>var phantomInstance = new Horseman({ </span> <span>phantomPath: '/usr/local/bin/phantomjs', </span> <span>loadImages: true, </span> <span>injectJquery: true, </span> <span>webSecurity: true, </span> <span>ignoreSSLErrors: true </span><span>}); </span> <span>performAction(phantomInstance, ...); </span>
Wenn wir jetzt unseren Befehl ausführen, werden die Horseman -Instanz und die Eingabe -URL an das Modul Hello_world übergeben, wodurch Phantomjs die URL anfordert, seinen Statuscode erfasst und den Status in die Konsole druckt. Wir haben gerade unseren ersten bona Fide -Crawl mit Horseman geführt. Giddyup!
Bisher haben wir uns eine sehr einfache Verwendung von Horseman angesehen, aber das Paket kann viel mehr tun, wenn wir seine Methoden anketten, um eine Folge von Aktionen im Browser auszuführen. Um einige dieser Funktionen zu demonstrieren, definieren wir eine Aktion, die einen Benutzer simuliert, der über GitHub navigiert, um ein neues Repository zu erstellen.
Bitte beachten Sie: Dieses Beispiel dient nur zu Demonstrationszwecken und sollte nicht als praktikable Methode zum Erstellen von Github -Repositorys angesehen werden. Es ist lediglich ein Beispiel dafür, wie man Horseman verwenden könnte, um mit einer Webanwendung zu interagieren. Sie sollten die offizielle Github -API verwenden, wenn Sie daran interessiert sind, Repositorys automatisch zu erstellen.
Nehmen wir an, das neue Crawl wird wie SO ausgelöst:
$ <span>node run.js -x hello_world </span>
Nach der Konvention des CLI -Frameworks, das wir bereits eingerichtet haben, müssen wir dem Aktionsverzeichnis namens create_repo.js ein neues Modul hinzufügen. Wie bei unserem vorherigen Beispiel „Hello World“ exportiert das Modul create_repo eine einzelne Funktion, die die gesamte Logik für diese Aktion enthält.
program <span>.version('1.0.0') </span> <span>.option('-x --action-to-perform [string]', 'The type of action to perform.') </span> <span>.option('-u --url [string]', 'Optional URL used by certain actions') </span> <span>.parse(process.argv); </span>
Beachten Sie, dass wir mit dieser Aktion mehr Parameter an die exportierte Funktion übergeben als zuvor. Die Parameter umfassen Benutzername, Passwort und Repository. Wir werden diese Werte von run.js übergeben, sobald der Benutzer die schnelle Herausforderung erfolgreich abgeschlossen hat.
Bevor dies jedoch passieren kann, müssen wir Logik zum Ausführen hinzufügen. Js, um die Eingabeaufforderung auszulösen und die Daten zu erfassen. Wir tun dies, indem wir unserer Hauptschalteranweisung einen Fall hinzufügen:
<span>var performAction = require('./actions/' + program.actionToPerform) </span> <span>switch (program.actionToPerform) { </span> <span>case 'hello_world': </span> prompt<span>.get([{ </span> <span>// What the property name should be in the result object </span> <span>name: 'url', </span> <span>// The prompt message shown to the user </span> <span>description: 'Enter a URL', </span> <span>// Whether or not the user is required to enter a value </span> <span>required: true, </span> <span>// Validates the user input </span> <span>conform: function (value) { </span> <span>// In this case, the user must enter a valid URL </span> <span>return validUrl.isWebUri(value); </span> <span>} </span> <span>}], function (err<span>, result</span>) { </span> <span>// Perform some action following successful input </span> <span>performAction(phantomInstance, result.url); </span> <span>}); </span> <span>break; </span><span>} </span>
Jetzt, da wir diesen Haken zum Ausführen von.js hinzugefügt haben, wird der Benutzer die relevanten Daten an die Aktion übergeben, sodass wir mit dem Crawl fortfahren können.
Wie bei der Create_Repo -Crawl -Logik selbst verwenden wir Horsemans Array von Methoden, um zur Github -Anmeldeseite zu navigieren, den angegebenen Benutzernamen und den angegebenen Kennwort einzugeben und das Formular einzureichen:
<span>'use strict'; </span> <span>/** </span><span> * <span>@param Horseman phantomInstance </span></span><span> * <span>@param string url </span></span><span> */ </span>module<span>.exports = function (phantomInstance<span>, url</span>) { </span> <span>if (!url || typeof url !== 'string') { </span> <span>throw 'You must specify a url to ping'; </span> <span>} else { </span> <span>console.log('Pinging url: ', url); </span> <span>} </span> phantomInstance <span>.open(url) </span> <span>.status() </span> <span>.then(function (statusCode) { </span> <span>if (Number(statusCode) >= 400) { </span> <span>throw 'Page failed with status: ' + statusCode; </span> <span>} else { </span> <span>console.log('Hello world. Status code returned: ', statusCode); </span> <span>} </span> <span>}) </span> <span>.catch(function (err) { </span> <span>console.log('Error: ', err); </span> <span>}) </span> <span>// Always close the Horseman instance </span> <span>// Otherwise you might end up with orphaned phantom processes </span> <span>.close(); </span><span>}; </span>
Wir setzen die Kette fort, indem wir auf das Laden der Formulareinreichungsseite warten:
<span>var phantomInstance = new Horseman({ </span> <span>phantomPath: '/usr/local/bin/phantomjs', </span> <span>loadImages: true, </span> <span>injectJquery: true, </span> <span>webSecurity: true, </span> <span>ignoreSSLErrors: true </span><span>}); </span> <span>performAction(phantomInstance, ...); </span>
Danach verwenden wir JQuery, um festzustellen, ob das Login erfolgreich war:
$ <span>node run.js -x create_repo </span>
Ein Fehler wird geworfen, wenn der Anmeldung fehlschlägt. Andernfalls werden wir weiterhin Methoden verkettet, um zu unserer Profilseite zu navigieren:
module<span>.exports = function (phantomInstance<span>, username, password, repository</span>) { </span> <span>if (!username || !password || !repository) { </span> <span>throw 'You must specify login credentials and a repository name'; </span> <span>} </span> <span>... </span><span>} </span>
Sobald wir auf unserer Profilseite sind, navigieren wir zu unserer Registerkarte unserer Repositories:
<span>switch (program.actionToPerform) { </span> <span>case 'create_repo': </span> prompt<span>.get([{ </span> <span>name: 'repository', </span> <span>description: 'Enter repository name', </span> <span>required: true </span> <span>}, { </span> <span>name: 'username', </span> <span>description: 'Enter GitHub username', </span> <span>required: true </span> <span>}, { </span> <span>name: 'password', </span> <span>description: 'Enter GitHub password', </span> <span>hidden: true, </span> <span>required: true </span> <span>}], function (err<span>, result</span>) { </span> <span>performAction( </span> phantomInstance<span>, </span> result<span>.username, </span> result<span>.password, </span> result<span>.repository </span> <span>); </span> <span>}); </span> <span>break; </span> <span>... </span>
Auf der Registerkarte unserer Repositories überprüfen wir, ob ein Repository mit dem angegebenen Namen bereits vorhanden ist. Wenn dies der Fall ist, werfen wir einen Fehler. Wenn nicht, fahren wir mit unserer Sequenz fort:
phantomInstance <span>.open('https://github.com/login') </span> <span>.type('input[name="login"]', username) </span> <span>.type('input[name="password"]', password) </span> <span>.click('input[name="commit"]') </span>
Unter der Annahme, dass keine Fehler geworfen wurden, klicken wir programmgesteuert auf die Schaltfläche „Neues Repository“ und warten auf die nächste Seite:
<span>.waitForNextPage() </span>
Danach geben wir den angegebenen Repository -Namen ein und senden das Formular:
<span>.evaluate(function () { </span> $ <span>= window.$ || window.jQuery; </span> <span>var fullHtml = $('body').html(); </span> <span>return !fullHtml.match(<span>/Incorrect username or password/</span>); </span><span>}) </span><span>.then(function (isLoggedIn) { </span> <span>if (!isLoggedIn) { </span> <span>throw 'Login failed'; </span> <span>} </span><span>}) </span>
Sobald wir die resultierende Seite erreicht haben, wissen wir, dass das Repository erstellt wurde:
.click('a:contains("Your profile")')
<span>.waitForNextPage()
</span>
Wie bei jedem Reiterkriechen ist es entscheidend, dass wir am Ende den Horseman -Instanz schließen:
<span>.click('nav[role="navigation"] a:nth-child(2)') </span><span>.waitForSelector('a.new-repo') </span>
Die nicht schließende Horseman -Instanz kann zu verwaisten Phantom -Prozessen führen, die auf der Maschine bestehen.
Zu diesem Zeitpunkt haben wir eine statische Folge von Aktionen zusammengestellt, um programmatisch ein neues Repository auf GitHub zu erstellen. Um dies zu erreichen, haben wir eine Reihe von Horseman -Methoden verkettet.
Dieser Ansatz kann für spezifische strukturelle und verhaltensbezogene Muster nützlich sein, die zuvor bekannt sind. Möglicherweise müssen Sie jedoch feststellen, dass Sie irgendwann flexiblere Skripten implementieren müssen. Dies kann der Fall sein, wenn Ihre Aktionssequenz das Potenzial hat, sich stark auf dem Kontext zu unterscheiden oder mehrere verschiedene Ergebnisse zu erzielen. Es wäre auch der Fall, wenn Sie Daten aus dem DOM extrahieren müssen.
In solchen Fällen können Sie die Methode von Horsemans evaluate () verwenden, mit der Sie freie Forminteraktionen im Browser ausführen können, indem Sie entweder Inline- oder externe JavaScript injizieren.
Dieser Abschnitt zeigt ein Beispiel für das Extrahieren von grundlegenden Daten aus einer Seite (in diesem Fall Ankerlinks). Ein Szenario, in dem dies notwendig sein könnte, wäre, einen Crawler für die Entdeckungserkennung aufzubauen, um jede URL auf einer Domäne zu treffen.
Wie bei unserem letzten Beispiel müssen wir dem Aktionsverzeichnis zuerst ein neues Modul hinzufügen:
$ <span>node run.js -x hello_world </span>
und fügen Sie dann einen Haken für die neue Aktion in run.js hinzu:
program <span>.version('1.0.0') </span> <span>.option('-x --action-to-perform [string]', 'The type of action to perform.') </span> <span>.option('-u --url [string]', 'Optional URL used by certain actions') </span> <span>.parse(process.argv); </span>
Jetzt, da dieser Code vorhanden ist, können wir ein Crawl ausführen, um Links von einer bestimmten Seite zu extrahieren, indem wir den folgenden Befehl ausführen:
<span>var performAction = require('./actions/' + program.actionToPerform) </span> <span>switch (program.actionToPerform) { </span> <span>case 'hello_world': </span> prompt<span>.get([{ </span> <span>// What the property name should be in the result object </span> <span>name: 'url', </span> <span>// The prompt message shown to the user </span> <span>description: 'Enter a URL', </span> <span>// Whether or not the user is required to enter a value </span> <span>required: true, </span> <span>// Validates the user input </span> <span>conform: function (value) { </span> <span>// In this case, the user must enter a valid URL </span> <span>return validUrl.isWebUri(value); </span> <span>} </span> <span>}], function (err<span>, result</span>) { </span> <span>// Perform some action following successful input </span> <span>performAction(phantomInstance, result.url); </span> <span>}); </span> <span>break; </span><span>} </span>
Diese Aktion zeigt, dass Daten aus einer Seite extrahieren und keine Browseraktionen verwendet, die von Reiter eingebaut werden. Es führt direkt aus welchem JavaScript, das Sie in die Methode Evaluate () einfügen, und tut dies so, als ob es in einer Browserumgebung nativ ausgeführt wird.
Eine letzte Sache sollte in diesem Abschnitt festgestellt werden, der auf früher angedeutet wurde Ihre Bewertungslogik. Dies kann wie SO erfolgen:
<span>'use strict'; </span> <span>/** </span><span> * <span>@param Horseman phantomInstance </span></span><span> * <span>@param string url </span></span><span> */ </span>module<span>.exports = function (phantomInstance<span>, url</span>) { </span> <span>if (!url || typeof url !== 'string') { </span> <span>throw 'You must specify a url to ping'; </span> <span>} else { </span> <span>console.log('Pinging url: ', url); </span> <span>} </span> phantomInstance <span>.open(url) </span> <span>.status() </span> <span>.then(function (statusCode) { </span> <span>if (Number(statusCode) >= 400) { </span> <span>throw 'Page failed with status: ' + statusCode; </span> <span>} else { </span> <span>console.log('Hello world. Status code returned: ', statusCode); </span> <span>} </span> <span>}) </span> <span>.catch(function (err) { </span> <span>console.log('Error: ', err); </span> <span>}) </span> <span>// Always close the Horseman instance </span> <span>// Otherwise you might end up with orphaned phantom processes </span> <span>.close(); </span><span>}; </span>
Durch Erweiterung der obigen Logik können Sie praktisch jede Aktion auf jeder Website ausführen.
Der endgültige Anwendungsfall, den ich zeigen möchte, ist, wie Sie Horseman verwenden würden, um Screenshots zu machen. Wir können dies mit Horsemans Screenshotbase64 () -Methode tun, die eine base64 codierte Zeichenfolge zurückgibt, die den Screenshot darstellt.
Wie bei unserem vorherigen Beispiel müssen wir dem Aktionsverzeichnis zuerst ein neues Modul hinzufügen:
<span>var phantomInstance = new Horseman({ </span> <span>phantomPath: '/usr/local/bin/phantomjs', </span> <span>loadImages: true, </span> <span>injectJquery: true, </span> <span>webSecurity: true, </span> <span>ignoreSSLErrors: true </span><span>}); </span> <span>performAction(phantomInstance, ...); </span>
und fügen Sie dann einen Haken für die neue Aktion in run.js hinzu:
$ <span>node run.js -x create_repo </span>
Jetzt können Sie Screenshots mit dem folgenden Befehl machen:
module<span>.exports = function (phantomInstance<span>, username, password, repository</span>) { </span> <span>if (!username || !password || !repository) { </span> <span>throw 'You must specify login credentials and a repository name'; </span> <span>} </span> <span>... </span><span>} </span>
Der Grund für die Verwendung von Base64 -codierten Zeichenfolgen (und nicht zum Beispiel des Speicherns der tatsächlichen Bilder) ist, dass sie eine bequeme Möglichkeit sind, Rohbilddaten darzustellen. Diese Stackoverflow -Antwort geht detaillierter aus.
Wenn Sie tatsächliche Bilder speichern möchten, verwenden Sie die Screenshot () -Methode.
Dieses Tutorial hat versucht, sowohl eine benutzerdefinierte CLI -Mikroframework als auch eine grundlegende Logik für das Kriechen in Node.js zu demonstrieren, wobei das Horseman -Paket verwendet wird, um Phantomjs zu nutzen. Während die Verwendung eines CLI -Frameworks wahrscheinlich vielen Projekten zugute kommt, ist die Verwendung von Krabbeln in der Regel auf sehr spezifische Problemdomänen beschränkt. Ein Gemeinschaftsbereich befindet sich in der Qualitätssicherung (QA), in der das Krabbeln für funktionale und Benutzeroberflächen -Tests verwendet werden kann. Ein weiterer Bereich ist Sicherheit, in dem Sie beispielsweise Ihre Website regelmäßig kriechen möchten, um festzustellen, ob sie unkompliziert oder auf andere Weise beeinträchtigt wurde.
Wie auch immer der Fall für Ihr Projekt sein mag, stellen Sie sicher, dass Sie Ihre Ziele klar definieren und so unauffällig wie möglich sein. Erhalten Sie die Erlaubnis, wenn Sie können, seien Sie in maximalem Maße höflich, und achten Sie darauf, dass Sie niemals eine Site ddoS ddosen machen. Wenn Sie den Verdacht haben, dass Sie viel automatisierten Verkehr generieren, sind Sie wahrscheinlich Ihre Ziele, Ihre Implementierung oder Ihre Genehmigung neu bewerten.
Kann ich Web -Crawling verwenden, um Änderungen auf einer Website zu überwachen? Das Web -Crawling kann verwendet werden, um Änderungen auf einer Website zu überwachen. Wenn Sie regelmäßig eine Website kriechen und den aktuellen Zustand mit einem früheren Zustand vergleichen, können Sie Änderungen erkennen. Dies kann für eine Vielzahl von Zwecken hilfreich sein, z. B. die Verfolgung von Preisänderungen auf E-Commerce-Websites oder Überwachung von Updates auf Nachrichten-Websites. Zu mehreren Faktoren, einschließlich der Gerichtsbarkeit, in der Sie sich befinden, und der spezifischen Website, die Sie kriechen. Auf einigen Websites ermöglichen das Web -Crawling in der Datei robots.txt ausdrücklich, während andere sie verbieten. Es ist wichtig, die Robots.txt -Datei der Website zu respektieren und den Server der Website nicht mit zu vielen Anfragen in kurzer Zeit zu überlasten.
Das obige ist der detaillierte Inhalt vonWebkriechen mit Knoten, Phantomjs und Reiter. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!