Skip to content

Instantly share code, notes, and snippets.

@loretoparisi
Created April 4, 2016 20:12
Show Gist options
  • Select an option

  • Save loretoparisi/e14908c5bbfd42d0a2f616c191797add to your computer and use it in GitHub Desktop.

Select an option

Save loretoparisi/e14908c5bbfd42d0a2f616c191797add to your computer and use it in GitHub Desktop.
Simple Crawler with Node Phantom
/**
* 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