Il est assez courant au cours d'un projet pour vous retrouver à rédiger des scripts personnalisés pour effectuer une variété d'actions. Ces scripts uniques, qui sont généralement exécutés via la ligne de commande (CLI), peuvent être utilisés pour pratiquement n'importe quel type de tâche. Après avoir écrit de nombreux scripts de ce type au fil des ans, j'ai grandi pour apprécier la valeur de prendre un peu de temps à l'avance pour mettre en place un microframe CLI personnalisé pour faciliter ce processus. Heureusement, Node.js et son vaste écosystème de package, NPM, facilitent le fait de faire exactement cela. Que ce soit l'analyse d'un fichier texte ou l'exécution d'un ETL, avoir une convention en place facilite l'ajout de nouvelles fonctionnalités de manière efficace et structurée.
Bien que cela ne soit pas nécessairement associé à la ligne de commande, la rampe Web est souvent utilisée dans certains domaines de problème tels que les tests fonctionnels automatisés et la détection de dégradation. Ce didacticiel montre comment implémenter un cadre CLI léger dont les actions prises en charge tournent autour de la réparation du Web. Espérons que cela fera couler votre créativité, que votre intérêt soit spécifique à la rampe ou à la ligne de commande. Les technologies couvertes incluent Node.js, Phantomjs et un assortiment de packages NPM liés à la fois à la rampe et au CLI.
Le code source de ce tutoriel peut être trouvé sur GitHub. Pour exécuter les exemples, vous devrez installer à la fois Node.js et Phantomjs. Les instructions pour les télécharger et les installer peuvent être trouvées ici: Node.js, Phantomjs.
Au cœur de tout cadre CLI se trouve le concept de convertissement d'une commande, qui comprend généralement un ou plusieurs arguments facultatifs ou requis, en une action concrète. Deux packages NPM qui sont très utiles à cet égard sont le commandant et l'invite.
Le commandant vous permet de définir les arguments pris en charge, tandis que l'invite vous permet de (de manière appropriée) l'invite de l'intervalle pour les entrées au moment de l'exécution. Le résultat final est une interface syntaxiquement douce pour effectuer une variété d'actions avec des comportements dynamiques basés sur certaines données fournies par l'utilisateur.
Dites, par exemple, nous voulons que notre commande ressemble à ceci:
$ <span>node run.js -x hello_world </span>
Notre point d'entrée (run.js) définit les arguments possibles comme celui-ci:
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>
et définit les différents cas d'entrée utilisateur comme celui-ci:
<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>
À ce stade, nous avons défini un chemin de base à travers lequel nous pouvons spécifier une action à effectuer, et avons ajouté une invite pour accepter une URL. Nous avons juste besoin d'ajouter un module pour gérer la logique spécifique à cette action. Nous pouvons le faire en ajoutant un fichier nommé hello_world.js au répertoire des actions:
<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>
Comme vous pouvez le voir, le module prévoit d'être fourni avec une instance d'un objet Phantomjs (Phantominstance) et d'une URL (URL). Nous allons entrer dans les détails de la définition momentanément d'une instance Phantomjs, mais pour l'instant, il suffit de voir que nous avons jeté les bases de la déclenchement d'une action particulière. Maintenant que nous avons mis en place une convention, nous pouvons facilement ajouter de nouvelles actions de manière définie et saine.
Horseman est un package Node.js qui fournit une interface puissante pour créer et interagir avec les processus Phantomjs. Une explication complète de Horseman et de ses caractéristiques justifierait son propre article, mais il suffit de dire qu'il vous permet de simuler facilement à peu près tout comportement qu'un utilisateur humain pourrait montrer dans son navigateur. Horseman offre une large gamme d'options de configuration, y compris des choses comme l'injection automatique de JQuery et l'ignorer les avertissements de certificat SSL. Il fournit également des fonctionnalités pour la manipulation des cookies et la prise de captures d'écran.
Chaque fois que nous déclenchez une action via notre cadre CLI, notre script d'entrée (run.js) instancie une instance de cavalier et le transmet au module d'action spécifié. En pseudo-code, il ressemble à ceci:
<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>
Maintenant, lorsque nous exécutons notre commande, l'instance de cavalier et l'URL d'entrée sont transmises au module hello_world, provoquant des phantomjs à demander l'URL, à capturer son code d'état et à imprimer l'état à la console. Nous venons de diriger notre première manche de bonne foi en utilisant Horseman. Giddyup!
Jusqu'à présent, nous avons examiné une utilisation très simple de Horseman, mais le package peut faire beaucoup plus lorsque nous enchaînons ses méthodes ensemble pour effectuer une séquence d'actions dans le navigateur. Afin de démontrer quelques-unes de ces fonctionnalités, définissons une action qui simule un utilisateur naviguant dans GitHub pour créer un nouveau référentiel.
Veuillez noter: Cet exemple est uniquement à des fins de démonstration et ne doit pas être considéré comme une méthode viable pour créer des référentiels GitHub. Il s'agit simplement d'un exemple de la façon dont on pourrait utiliser Horseman pour interagir avec une application Web. Vous devez utiliser l'API GitHub officielle si vous souhaitez créer des référentiels de manière automatisée.
Supposons que le nouveau manche sera déclenché comme ça:
$ <span>node run.js -x hello_world </span>
Après la convention du cadre CLI que nous avons déjà mis en place, nous devons ajouter un nouveau module au répertoire des actions nommé Create_repo.js. Comme pour notre exemple précédent «Hello World», le module create_repo exporte une seule fonction contenant toute la logique de cette action.
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>
Notez qu'avec cette action, nous passons plus de paramètres à la fonction exportée que nous l'avons fait précédemment. Les paramètres incluent le nom d'utilisateur, le mot de passe et le référentiel. Nous passerons ces valeurs de run.js une fois que l'utilisateur aura terminé avec succès le défi rapide.
Avant que tout cela ne puisse se produire, nous devons ajouter de la logique pour exécuter.js pour déclencher l'invite et capturer les données. Nous le faisons en ajoutant un cas à notre déclaration de commutation principale:
<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>
Maintenant que nous avons ajouté ce crochet à Run.js, lorsque l'utilisateur entre les données pertinentes, elle sera transmise à l'action, ce qui nous permet de procéder à la rampe.
Quant à la logique de crawl create_repo elle-même, nous utilisons la gamme de méthodes de Horseman pour accéder à la page de connexion GitHub, entrez le nom d'utilisateur et le mot de passe fourni et soumettez le formulaire:
<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>
Nous continuons la chaîne en attendant que la page de soumission de formulaire se charge:
<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>
après quoi nous utilisons jQuery pour déterminer si la connexion a réussi:
$ <span>node run.js -x create_repo </span>
Une erreur est lancée si la connexion échoue. Sinon, nous continuons des méthodes de chaîne pour naviguer vers notre page de profil:
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>
Une fois que nous sommes sur notre page de profil, nous naviguons vers notre onglet Repositaires:
<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>
Dans l'onglet de nos référentiels, nous vérifions si un référentiel avec le nom spécifié existe déjà. Si c'est le cas, nous lançons une erreur. Sinon, nous continuons avec notre séquence:
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>
En supposant qu'aucune erreur n'a été lancée, nous procédons en cliquant par programme sur le bouton "Nouveau référentiel" et en attendant la page suivante:
<span>.waitForNextPage() </span>
après quoi nous entrons le nom du référentiel fourni et soumettons le formulaire:
<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>
Une fois que nous atteignons la page résultante, nous savons que le référentiel a été créé:
.click('a:contains("Your profile")')
<span>.waitForNextPage()
</span>
Comme pour tout crawl de cavalier, il est crucial que nous fermons l'instance de cavalier à la fin:
<span>.click('nav[role="navigation"] a:nth-child(2)') </span><span>.waitForSelector('a.new-repo') </span>
Ne pas fermer l'instance de cavalier peut entraîner des processus de fantômes orphelins qui persistent sur la machine.
À ce stade, nous avons assemblé une séquence statique d'actions pour créer par programme un nouveau référentiel sur GitHub. Pour ce faire, nous avons enchaîné une série de méthodes de cavalier.
Cette approche peut être utile pour des modèles structurels et comportementaux spécifiques qui sont connus au préalable, cependant, vous pouvez constater que vous devez implémenter des scripts plus flexibles à un moment donné. Cela pourrait être le cas si votre séquence d'action a le potentiel de varier considérablement en fonction du contexte ou de produire plusieurs résultats différents. Ce serait également le cas si vous avez besoin d'extraire des données du dom.
Dans de tels cas, vous pouvez utiliser la méthode Evaluate () de Horseman, qui vous permet d'exécuter des interactions de forme libre dans le navigateur en injectant JavaScript en ligne ou liée à l'extérieur.
Cette section montre un exemple d'extraction des données de base d'une page (liens d'ancrage, dans ce cas). Un scénario où cela pourrait être nécessaire serait de construire un robot de détection de déménagement pour frapper chaque URL sur un domaine.
Comme avec notre dernier exemple, nous devons d'abord ajouter un nouveau module au répertoire des actions:
$ <span>node run.js -x hello_world </span>
puis ajoutez un crochet pour la nouvelle action dans run.js:
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>
Maintenant que ce code est en place, nous pouvons exécuter une analyse pour extraire des liens de n'importe quelle page donnée en exécutant la commande suivante:
<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>
Cette action démontre l'extraction de données d'une page et n'utilise aucune action de navigateur intégrée par Horseman. Il exécute directement tout le JavaScript que vous mettez dans la méthode Evaluat (), et le fait comme s'il s'exécutait nativement dans un environnement de navigateur.
Une dernière chose doit être notée dans cette section, à qui a été mentionnée plus tôt: non seulement vous pouvez exécuter JavaScript personnalisé dans le navigateur à l'aide de la méthode Evaluate (), mais vous pouvez également injecter des scripts externes dans l'environnement d'exécution avant d'exécuter votre logique d'évaluation. Cela peut être fait comme ça:
<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>
En étendant la logique ci-dessus, vous pouvez effectuer pratiquement n'importe quelle action sur n'importe quel site Web.
Le cas d'utilisation final que je veux démontrer est comment vous utiliseriez Horseman pour prendre des captures d'écran. Nous pouvons le faire avec la méthode ScreenshotBase64 () de Horseman, qui renvoie une chaîne codée Base64 représentant la capture d'écran.
Comme avec notre exemple précédent, nous devons d'abord ajouter un nouveau module au répertoire des actions:
<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>
puis ajoutez un crochet pour la nouvelle action dans run.js:
$ <span>node run.js -x create_repo </span>
Maintenant, vous pouvez prendre des captures d'écran avec la commande suivante:
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>
La raison de l'utilisation de chaînes codées Base64 (et non, par exemple, de l'enregistrement des images réelles) est qu'ils sont un moyen pratique de représenter les données d'image brutes. Cette réponse StackOverflow va plus en détail.
Si vous vouliez enregistrer des images réelles, vous utiliseriez la méthode de capture d'écran ().
Ce tutoriel a tenté de démontrer à la fois un microframe CLI personnalisé et une logique de base pour ramper dans Node.js, en utilisant le package de cavalier pour tirer parti des Phantomjs. Bien que l'utilisation d'un cadre CLI profiterait probablement à de nombreux projets, l'utilisation de rampage est généralement limitée aux domaines de problème très spécifiques. Une zone commune est dans l'assurance qualité (QA), où la rampe peut être utilisée pour les tests fonctionnels et d'interface utilisateur. Un autre domaine est la sécurité où, par exemple, vous voudrez peut-être ramper périodiquement votre site Web pour détecter si elle a été dégradée ou autrement compromise.
quel que soit le cas pour votre projet, assurez-vous de définir clairement vos objectifs et d'être aussi discrets que possible. Obtenez la permission lorsque vous le pouvez, soyez poli dans la mesure maximale que vous pouvez et prenez soin de ne jamais DDOS un site. Si vous soupçonnez que vous générez beaucoup de trafic automatisé, vous le êtes probablement, et devriez probablement réévaluer vos objectifs, votre implémentation ou votre niveau d'autorisation.
Le rampage du Web et le grattage Web sont deux processus distincts, bien qu'ils soient souvent utilisés de manière interchangeable. Le compromis Web est le processus de navigation systématique sur le Web, généralement réalisé par des bots ou des araignées. Cela implique d'indexer le contenu d'un site Web et les liens suivants vers d'autres pages Web. D'un autre côté, le grattage Web est le processus d'extraction de données spécifiques d'un site Web. Cela implique l'analyse du HTML d'une page Web pour retirer les données dont vous avez besoin. Alors que le compromis Web consiste à naviguer et à indexer, le grattage Web concerne l'extraction des données.
Node.js est un choix populaire pour le compromis Web en raison de son nature asynchrone. Il permet un traitement simultané, ce qui signifie que vous pouvez ramper plusieurs pages en même temps. Cela le rend considérablement plus rapide que les autres langues qui s'exécutent de manière synchrone. De plus, Node.js a un écosystème riche avec de nombreuses bibliothèques et outils qui peuvent aider à ramper le Web, comme Phantomjs Horseman.
Le rendu JavaScript peut affecter considérablement la rampe du Web. Les robots Web traditionnels analysent uniquement le HTML d'une page Web, ignorant tout contenu généré par JavaScript. Cependant, les moteurs de recherche modernes sont capables de rendre JavaScript, ce qui leur permet d'indexer du contenu généré par JavaScript. Cela signifie que si votre site Web s'appuie fortement sur JavaScript pour la génération de contenu, il peut ne pas être entièrement indexé par les robots Web traditionnels.
Oui , le compromis Web peut être utilisé pour surveiller les modifications sur un site Web. En rampant régulièrement un site Web et en comparant l'état actuel avec un état précédent, vous pouvez détecter toute modification. Cela peut être utile à diverses fins, tels que le suivi des modifications des prix sur les sites Web de commerce électronique ou la surveillance des mises à jour sur les sites Web d'information.
Comment puis-je optimiser mon processus de rampe Web?
Puis-je faire des sites Web qui nécessitent une connexion avec Node.js et Phantomjs Horseman?
Comment puis-je gérer le contenu dynamique dans le flux Web?
Il existe plusieurs stratégies pour éviter que votre robot Web soit bloqué. Une façon consiste à respecter le fichier robots.txt du site Web, qui fournit des directives sur les parties du site Web que vous êtes autorisé à ramper. Une autre façon consiste à limiter le taux auquel vous envoyez des demandes au site Web, pour éviter de surcharger le serveur. Vous pouvez également faire pivoter votre adresse IP et votre agent utilisateur pour éviter d'être détecté comme un bot.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!