Created
April 4, 2016 20:12
-
-
Save loretoparisi/e14908c5bbfd42d0a2f616c191797add to your computer and use it in GitHub Desktop.
Simple Crawler with Node Phantom
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * Simple Crawler with Node Phantom | |
| * @author: loretoparisi at gmail dot com | |
| */ | |
| (function() { | |
| var Crawler,phantom,cheerio; | |
| cheerio = require("cheerio"), | |
| phantom = require('phantom'); | |
| Crawler = (function() { | |
| /** | |
| * Wait for event condition to verify | |
| */ | |
| function waitFor(testFx, onReady, timeOutMillis) { | |
| var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 5000, //< Default Max Timout is 5s | |
| start = new Date().getTime(), | |
| condition = false, | |
| interval = setInterval(function () { | |
| if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) { | |
| // If not time-out yet and condition not yet fulfilled | |
| condition = (typeof (testFx) === "string" ? eval(testFx) : testFx()); //< defensive code | |
| } else { | |
| if (!condition) { | |
| // If condition still not fulfilled (timeout but condition is 'false') | |
| //console.log("'waitFor()' timeout"); | |
| typeof (onReady) === "string" ? eval(onReady) : onReady(); | |
| clearInterval(interval); | |
| //phantom.exit(1); | |
| } else { | |
| // Condition fulfilled (timeout and/or condition is 'true') | |
| console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); | |
| typeof (onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled | |
| clearInterval(interval); //< Stop this interval | |
| } | |
| } | |
| }, 500); //< repeat check every 500ms | |
| }; | |
| /** | |
| * Simple Crawler | |
| * @uses phantom module | |
| * @author Loreto Parisi (loretoparisi at musixmatch dot com) | |
| */ | |
| function Crawler(options,logger) { | |
| this.logger=logger; | |
| /** Command line phantom options */ | |
| this.options = { | |
| debug : false, // true to debug page events | |
| error : true, // true to log page errors | |
| info : true, // true to log page info | |
| commandline : ['--ignore-ssl-errors=yes', '--load-images=no', '--ssl-protocol=tlsv1'] | |
| } | |
| for (var attrname in options) { this.options[attrname] = options[attrname]; } | |
| } | |
| /** | |
| * Fetch url | |
| * @param done Block success Block | |
| * @param fail Block error Block | |
| */ | |
| Crawler.prototype.fetchUrl = function(url, done, fail) { | |
| var self=this; | |
| var sitepage | |
| ,pageContent | |
| ,pageStatus | |
| ,pageResponse | |
| ,phInstance | |
| ,pageContext; | |
| sitepage = phInstance = null; | |
| // context for the page | |
| pageContext = { | |
| options : self.options | |
| } | |
| phantom.create(self.options.commandline) | |
| .then((instance) => { | |
| phInstance = instance; | |
| return instance.createPage(); | |
| }) | |
| .then((page) => { | |
| pageResponse = phInstance.createOutObject(); | |
| sitepage = page; | |
| // attach events: resources | |
| page.property('onResourceRequested', function(requestData, networkRequest,pageContext) { | |
| if(pageContext.options.debug) console.log(requestData.url); | |
| },pageContext); | |
| page.property('onResourceReceived', function(response, networkRequest,pageContext) { | |
| var msg = ' id: ' + response.id + ', stage: "' + response.stage + '", response: ' + JSON.stringify(response); | |
| if(pageContext.options.debug) console.log(msg); | |
| },pageContext); | |
| page.property('onResourceError', function(resourceError, networkRequest,pageContext) { | |
| var errorString = resourceError.errorString; | |
| var errorPageUrl = resourceError.url; | |
| if(pageContext.options.error) console.log(resourceError,errorString,errorPageUrl); | |
| },pageContext); | |
| // attach events: load | |
| page.property('onLoadStarted', function(pageContext) { | |
| if(pageContext.options.info) console.log('onLoadStarted'); | |
| },pageContext); | |
| page.property('onLoadFinished', function(response,pageContext,pageResponse) { | |
| if(pageContext.options.info) console.log('onLoadFinished'); | |
| },pageContext,pageResponse); | |
| // events: navigation | |
| page.property('onNavigationRequested', function(url, type, willNavigate, main,pageContext) { | |
| var msg = ' destination_url: ' + url; | |
| msg += ' type (cause): ' + type; | |
| msg += ' will navigate: ' + willNavigate; | |
| msg += ' from page\'s main frame: ' + main; | |
| if(pageContext.options.debug) console.log(msg); | |
| },pageContext); | |
| // error | |
| page.property('onError', function(requestData, networkRequest,pageContext) { | |
| if(pageContext.options.error) console.log(requestData.url); | |
| },pageContext); | |
| return page.open(url); | |
| }) | |
| .then((status) => { | |
| console.log("Page status [%s]",status); | |
| pageStatus=status; | |
| return sitepage.property('content'); | |
| }) | |
| .then((content) => { | |
| pageContent = content; | |
| console.log("Content ready Page Status [%s]",pageStatus); | |
| //var $ = cheerio.load( pageContent ); | |
| if (pageStatus !== 'success') { | |
| console.log("Page status error %s", pageStatus); | |
| if(fail) fail(error); | |
| sitepage.close(); | |
| phInstance.exit(); | |
| } else { | |
| sitepage.evaluate(function() { | |
| return document.body; | |
| //return document.querySelectorAll('.content > ul > li'); | |
| }).then(function(html) { | |
| pageContent = sitepage.content; | |
| if(done) done( pageContent, html ); | |
| sitepage.close(); | |
| phInstance.exit(); | |
| }); | |
| } | |
| }) | |
| .catch((error) => { | |
| self.logger.error(error); | |
| if(fail) fail(error); | |
| if(sitepage) sitepage.close(); | |
| phInstance.exit(); | |
| }); | |
| } //fetchUrl | |
| return Crawler; | |
| })(); | |
| module.exports = Crawler; | |
| }).call(this); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment