├── .gitignore ├── .travis.yml ├── LICENCE ├── README.md ├── extend.js ├── index.js ├── package.json └── test ├── index.js └── number-#3.js /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules/ 3 | 4 | *.log 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | - "0.11" 5 | - "0.12" 6 | - "4" 7 | - "node" 8 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Jason Morgan, http://www.jasonmorgandevelopment.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | alchemy-api - An Alchemy API library for Node.js 2 | ==================== 3 | [![Build Status](https://secure.travis-ci.org/framingeinstein/node-alchemy.png)](http://travis-ci.org/framingeinstein/node-alchemy) 4 | 5 | This module provides calls to the [AlchemyAPI](http://www.alchemyapi.com/) for [Nodejs](http://nodejs.org). 6 | For more information on the API request and responses visit the [AlchemyAPI docs](http://www.alchemyapi.com/api/). To use the module you will need to obtain an api key from [Alchemy](http://www.alchemyapi.com/api/register.html). 7 | 8 | Installation 9 | ------------ 10 | You can install this through npm: npm install alchemy-api 11 | 12 | You can also install via git by cloning: `git clone https://github.com/framingeinstein/node-alchemy.git /path/to/alchemy-api` 13 | 14 | Usage 15 | ----- 16 | var AlchemyAPI = require('alchemy-api'); 17 | var alchemy = new AlchemyAPI(''); 18 | alchemy.sentiment('', {}, function(err, response) { 19 | if (err) throw err; 20 | 21 | // See http://www.alchemyapi.com/api/ for format of returned object 22 | var sentiment = response.docSentiment; 23 | 24 | // Do something with data 25 | }); 26 | 27 | For api methods that allow for sentiment analysis such as Entity Extraction, Relations and Keywords pass: 28 | {"sentiment":1} as the second parameter. 29 | 30 | Tests 31 | ----- 32 | To run tests type `npm test` 33 | 34 | AlchemyAPI Features 35 | --------------- 36 | 37 | Named Entity Extraction 38 | ----------------------- 39 | var AlchemyAPI = require('alchemy-api'); 40 | var alchemy = new AlchemyAPI(''); 41 | alchemy.entities('', {}, function(err, response) { 42 | if (err) throw err; 43 | 44 | // See http://www.alchemyapi.com/api/entity/htmlc.html for format of returned object 45 | var entities = response.entities; 46 | 47 | // Do something with data 48 | }); 49 | 50 | Sentiment Analysis 51 | ------------------ 52 | var AlchemyAPI = require('alchemy-api'); 53 | var alchemy = new AlchemyAPI(''); 54 | alchemy.sentiment('', {}, function(err, response) { 55 | if (err) throw err; 56 | 57 | // See http://www.alchemyapi.com/api/sentiment/htmlc.html for format of returned object 58 | var sentiment = response.docSentiment; 59 | 60 | // Do something with data 61 | }); 62 | 63 | Emotions 64 | ------------------ 65 | var AlchemyAPI = require('alchemy-api'); 66 | var alchemy = new AlchemyAPI(''); 67 | alchemy.emotions('', {}, function(err, response) { 68 | if (err) throw err; 69 | 70 | // See http://www.alchemyapi.com/api/html-api-1 for format of returned object 71 | var emotions = response.docEmotions; 72 | 73 | // Do something with data 74 | }); 75 | 76 | 77 | Targeted Sentiment Analysis 78 | ------------------ 79 | var AlchemyAPI = require('alchemy-api'); 80 | var alchemy = new AlchemyAPI(''); 81 | alchemy.sentiment_targeted('', '', {}, function(err, response) { 82 | if (err) throw err; 83 | 84 | // See http://www.alchemyapi.com/api/sentiment/htmlc.html for format of returned object 85 | var sentiment = response.docSentiment; 86 | 87 | // Do something with data 88 | }); 89 | 90 | Relation Extraction 91 | ------------------- 92 | var AlchemyAPI = require('alchemy-api'); 93 | var alchemy = new AlchemyAPI(''); 94 | alchemy.relations('', {}, function(err, response) { 95 | if (err) throw err; 96 | 97 | // See http://www.alchemyapi.com/api/relation/htmlc.html for format of returned object 98 | var relations = response.relations; 99 | 100 | // Do something with data 101 | }); 102 | 103 | Concept Tagging 104 | --------------- 105 | var AlchemyAPI = require('alchemy-api'); 106 | var alchemy = new AlchemyAPI(''); 107 | alchemy.concepts('', {}, function(err, response) { 108 | if (err) throw err; 109 | 110 | // See http://www.alchemyapi.com/api/concept/htmlc.html for format of returned object 111 | var concepts = response.concepts; 112 | 113 | // Do something with data 114 | }); 115 | 116 | Keyword / Terminology Extraction 117 | --------------- 118 | var AlchemyAPI = require('alchemy-api'); 119 | var alchemy = new AlchemyAPI(''); 120 | alchemy.keywords('', {}, function(err, response) { 121 | if (err) throw err; 122 | 123 | // See http://www.alchemyapi.com/api/keyword/htmlc.html for format of returned object 124 | var keywords = response.keywords; 125 | 126 | // Do something with data 127 | }); 128 | 129 | Taxonomy 130 | --------------- 131 | var AlchemyAPI = require('alchemy-api'); 132 | var alchemy = new AlchemyAPI(''); 133 | alchemy.taxonomies('', {}, function(err, response) { 134 | if (err) throw err; 135 | 136 | // See http://www.alchemyapi.com/api/taxonomy_calls/html.html for format of returned object 137 | var taxonomies = response.taxonomies; 138 | 139 | // Do something with data 140 | }); 141 | 142 | Topic Categorization 143 | --------------- 144 | var AlchemyAPI = require('alchemy-api'); 145 | var alchemy = new AlchemyAPI(''); 146 | alchemy.category('', {}, function(err, response) { 147 | if (err) throw err; 148 | 149 | // See http://www.alchemyapi.com/api/categ/htmlc.html for format of returned object 150 | var category = response.category; 151 | 152 | // Do something with data 153 | }); 154 | 155 | Image Link Extraction (Main Image) 156 | --------------- 157 | var AlchemyAPI = require('alchemy-api'); 158 | var alchemy = new AlchemyAPI(''); 159 | alchemy.imageLink('', {}, function(err, response) { 160 | if (err) throw err; 161 | 162 | // See http://www.alchemyapi.com/api/image-link-extraction/htmlc.html for format of returned object 163 | var image = response.image; 164 | 165 | // Do something with data 166 | }); 167 | 168 | Image Tags/Keyword Extraction 169 | --------------- 170 | var AlchemyAPI = require('alchemy-api'); 171 | var alchemy = new AlchemyAPI(''); 172 | alchemy.imageKeywords('', {}, function(err, response) { 173 | if (err) throw err; 174 | 175 | // See http://www.alchemyapi.com/api/image-tagging/urls.html for format of returned object 176 | var imageKeywords = response.imageKeywords; 177 | 178 | // Do something with data 179 | }); 180 | 181 | Image Faces Detection 182 | --------------- 183 | var AlchemyAPI = require('alchemy-api'); 184 | var alchemy = new AlchemyAPI(''); 185 | alchemy.imageFaces('', {}, function(err, response) { 186 | if (err) throw err; 187 | 188 | // See http://www.alchemyapi.com/api/face-detection/urls.html for format of returned object 189 | var imageFaces = response.imageFaces; 190 | 191 | // Do something with data 192 | }); 193 | 194 | Language Detection 195 | --------------- 196 | var AlchemyAPI = require('alchemy-api'); 197 | var alchemy = new AlchemyAPI(''); 198 | alchemy.language('', {}, function(err, response) { 199 | if (err) throw err; 200 | 201 | // See http://www.alchemyapi.com/api/lang/htmlc.html for format of returned object 202 | var language = response.language; 203 | 204 | // Do something with data 205 | }); 206 | 207 | Author Extraction 208 | --------------- 209 | var AlchemyAPI = require('alchemy-api'); 210 | var alchemy = new AlchemyAPI(''); 211 | alchemy.author('', {}, function(err, response) { 212 | if (err) throw err; 213 | 214 | // See http://www.alchemyapi.com/api/author/htmlc.html for format of returned object 215 | var author = response.author; 216 | 217 | // Do something with data 218 | }); 219 | 220 | Text Extraction / Web Page Cleaning 221 | --------------- 222 | var AlchemyAPI = require('alchemy-api'); 223 | var alchemy = new AlchemyAPI(''); 224 | alchemy.text('', {}, function(err, response) { 225 | if (err) throw err; 226 | 227 | // See http://www.alchemyapi.com/api/text/htmlc.html for format of returned object 228 | var text = response.text; 229 | 230 | // Do something with data 231 | }); 232 | 233 | Structured Content Scraping 234 | --------------- 235 | var AlchemyAPI = require('alchemy-api'); 236 | var alchemy = new AlchemyAPI(''); 237 | alchemy.scrape('', {}, function(err, response) { 238 | if (err) throw err; 239 | 240 | // See http://www.alchemyapi.com/api/scrape/htmlc.html for format of returned object 241 | var results = response.queryResults; 242 | 243 | // Do something with data 244 | }); 245 | 246 | Microformats 247 | ------------ 248 | var AlchemyAPI = require('alchemy-api'); 249 | var alchemy = new AlchemyAPI(''); 250 | alchemy.microformats('', {}, function(err, response) { 251 | if (err) throw err; 252 | 253 | // See http://www.alchemyapi.com/api/mformat/htmlc.html for format of returned object 254 | var microformats = response.microformats; 255 | 256 | // Do something with data 257 | }); 258 | 259 | RSS / ATOM Feed Discovery 260 | ---------- 261 | var AlchemyAPI = require('alchemy-api'); 262 | var alchemy = new AlchemyAPI(''); 263 | alchemy.feeds('', {}, function(err, response) { 264 | if (err) throw err; 265 | 266 | // See http://www.alchemyapi.com/api/feed/htmlc.html for format of returned object 267 | var feeds = response.feeds; 268 | 269 | // Do something with data 270 | }); 271 | 272 | Publication Date 273 | ---------- 274 | var AlchemyAPI = require('alchemy-api'); 275 | var alchemy = new AlchemyAPI(''); 276 | alchemy.publicationDate('', {}, function(err, response) { 277 | if (err) throw err; 278 | 279 | // See http://www.alchemyapi.com/api/publication-date/htmlc.html for format of returned object 280 | var publicationDate = response.publicationDate; //YYYYMMDDTHHMMSS string 281 | 282 | // Do something with data 283 | }); 284 | 285 | Combined Feature Extraction Call 286 | ---------- 287 | var AlchemyAPI = require('alchemy-api'); 288 | var alchemy = new AlchemyAPI(''); 289 | alchemy.combined('', ["FEATURE_NAME",...], {}, function(err, response) { 290 | if (err) throw err; 291 | 292 | // See http://www.alchemyapi.com/api/combined-call/ for format of returned object. 293 | // Each feature response will be available as a separate property. 294 | var feature_response = response.FEATURE_NAME; 295 | 296 | // Do something with data 297 | }); 298 | 299 | API Key Information 300 | ---------- 301 | var AlchemyAPI = require('alchemy-api'); 302 | var alchemy = new AlchemyAPI(''); 303 | alchemy.apiKeyInfo({}, function(err, response) { 304 | if (err) throw err; 305 | 306 | // Do something with data 307 | console.log('Status:', response.status, 'Consumed:', response.consumedDailyTransactions, 'Limit:', response.dailyTransactionLimit); 308 | 309 | }); 310 | -------------------------------------------------------------------------------- /extend.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery.extend extracted from the jQuery source & optimised for NodeJS 3 | Twitter: @FGRibreau / fgribreau.com 4 | 5 | Usage: 6 | var Extend = require('./Extend'); 7 | 8 | 9 | // Extend 10 | var obj = Extend({opt1:true, opt2:true}, {opt1:false}); 11 | 12 | // Deep Copy 13 | var clonedObject = Extend(true, {}, myObject); 14 | var clonedArray = Extend(true, [], ['a',['b','c',['d']]]); 15 | */ 16 | 17 | 18 | var toString = Object.prototype.toString, 19 | hasOwn = Object.prototype.hasOwnProperty, 20 | push = Array.prototype.push, 21 | slice = Array.prototype.slice, 22 | trim = String.prototype.trim, 23 | indexOf = Array.prototype.indexOf, 24 | 25 | // [[Class]] -> type pairs 26 | class2type = {}; 27 | 28 | // Populate the class2type map 29 | "Boolean Number String Function Array Date RegExp Object".split(" ").forEach(function(name) { 30 | class2type[ "[object " + name + "]" ] = name.toLowerCase(); 31 | }); 32 | 33 | function type(obj){ 34 | return obj == null ? 35 | String( obj ) : 36 | class2type[ toString.call(obj) ] || "object"; 37 | } 38 | 39 | function isPlainObject( obj ) { 40 | if ( !obj || type(obj) !== "object") { 41 | return false; 42 | } 43 | 44 | // Not own constructor property must be Object 45 | if ( obj.constructor && 46 | !hasOwn.call(obj, "constructor") && 47 | !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { 48 | return false; 49 | } 50 | 51 | // Own properties are enumerated firstly, so to speed up, 52 | // if last one is own, then all properties are own. 53 | 54 | var key; 55 | for ( key in obj ) {} 56 | 57 | return key === undefined || hasOwn.call( obj, key ); 58 | } 59 | 60 | module.exports = function extend(){ 61 | var options, name, src, copy, copyIsArray, clone, 62 | target = arguments[0] || {}, 63 | i = 1, 64 | length = arguments.length, 65 | deep = false; 66 | 67 | // Handle a deep copy situation 68 | if ( typeof target === "boolean" ) { 69 | deep = target; 70 | target = arguments[1] || {}; 71 | // skip the boolean and the target 72 | i = 2; 73 | } 74 | 75 | // Handle case when target is a string or something (possible in deep copy) 76 | if ( typeof target !== "object" && type(target) !== "function") { 77 | target = {}; 78 | } 79 | 80 | // extend jQuery itself if only one argument is passed 81 | if ( length === i ) { 82 | target = this; 83 | --i; 84 | } 85 | 86 | for ( ; i < length; i++ ) { 87 | // Only deal with non-null/undefined values 88 | if ( (options = arguments[ i ]) != null ) { 89 | // Extend the base object 90 | for ( name in options ) { 91 | src = target[ name ]; 92 | copy = options[ name ]; 93 | 94 | // Prevent never-ending loop 95 | if ( target === copy ) { 96 | continue; 97 | } 98 | 99 | // Recurse if we're merging plain objects or arrays 100 | if ( deep && copy && ( isPlainObject(copy) || (copyIsArray = type(copy) === "array") ) ) { 101 | if ( copyIsArray ) { 102 | copyIsArray = false; 103 | clone = src && type(src) === "array" ? src : []; 104 | 105 | } else { 106 | clone = src && isPlainObject(src) ? src : {}; 107 | } 108 | 109 | // Never move original objects, clone them 110 | target[ name ] = extend( deep, clone, copy ); 111 | 112 | // Don't bring in undefined values 113 | } else if ( copy !== undefined ) { 114 | target[ name ] = copy; 115 | } 116 | } 117 | } 118 | } 119 | 120 | // Return the modified object 121 | return target; 122 | }; 123 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * alchemy-api - A node module for calling the Alchemy API 3 | * See http://www.alchemyapi.com/api/ for details about the API requests and responses 4 | * Copyright (c) 2012 Jason Morgan 5 | * MIT Licence 6 | */ 7 | 8 | var url = require('url'); 9 | var http = require('http'); 10 | var https = require('https'); 11 | var querystring = require('querystring'); 12 | var extend = require('./extend'); 13 | var imageType = require('image-type'); 14 | 15 | //To install via NPM type the following: `npm install alchemy-api` 16 | var AlchemyAPI = function(api_key, opts) { 17 | var settings = { 18 | format: "json" 19 | ,api_url: "gateway-a.watsonplatform.net" 20 | ,protocol: "https" 21 | }; 22 | 23 | settings = extend(settings, opts); 24 | 25 | this.config = { 26 | api_url: settings.api_url 27 | ,protocol: settings.protocol 28 | }; 29 | 30 | this.options = { 31 | apikey: api_key 32 | ,outputMode: settings.format 33 | }; 34 | 35 | return this; 36 | }; 37 | 38 | /** 39 | * Returns API path depending on method being called 40 | * @param {String} method The Alchemy API method to call with the request 41 | * @return {String} 42 | */ 43 | AlchemyAPI.prototype._getMethodType = function(method){ 44 | var regex = new RegExp("^(text|html|url)", "i"); 45 | var results = regex.exec(method); 46 | if(results != null){ 47 | return results[0].toLowerCase(); 48 | } 49 | 50 | return ""; 51 | }; 52 | 53 | AlchemyAPI.prototype._getPathFromMethod = function(method){ 54 | var results = this._getMethodType(method); 55 | if(results != ""){ 56 | return "/" + results; 57 | } 58 | return ""; 59 | }; 60 | 61 | 62 | /** 63 | * Generates the URL object to be passed to the HTTP request for a specific 64 | * API method call 65 | * @param {Object} query The query object 66 | * @param {String} method The Alchemy API method to call with the request 67 | * @return {Object} The URL object for this request 68 | */ 69 | AlchemyAPI.prototype._generateNiceUrl = function(query, options, method) { 70 | var result = url.parse(url.format({ 71 | protocol: this.config.protocol, 72 | hostname: this.config.api_url, 73 | pathname: '/calls' + this._getPathFromMethod(method) + '/' + method, 74 | method: "POST", 75 | query: options 76 | })); 77 | // HACK: Fixes the redirection issue in node 0.4.x 78 | if (!result.path) { result.path = result.pathname + result.search; } 79 | //console.log(result); 80 | //if (this._urlCheck()) 81 | return result; 82 | }; 83 | 84 | 85 | /** 86 | * Function to do a HTTP request with the current query 87 | * @param {Object} request_query The current query object 88 | * @param {Function} cb The callback function for the returned data 89 | * @return {void} 90 | */ 91 | AlchemyAPI.prototype._doRequest = function(request_query, cb) { 92 | // Pass the requested URL as an object to the get request 93 | //console.log(request_query.nice); 94 | var http_protocol = (request_query.nice.protocol === 'https:') ? https : http; 95 | 96 | //var server = http.createClient(80, this.config.api_url); 97 | //console.log(request_query.nice.path); 98 | var req = http_protocol.request(request_query.nice, function(res) { 99 | var data = []; 100 | res 101 | .on('data', function(chunk) { data.push(chunk); }) 102 | .on('end', function() { 103 | var urldata = data.join('').trim(); 104 | //console.log(urldata); 105 | var result; 106 | try { 107 | result = JSON.parse(urldata); 108 | } catch (exp) { 109 | //console.log(request_query.nice.href); 110 | //console.log(querystring.stringify(request_query.post)); 111 | //console.log(urldata); 112 | //console.log(urldata); 113 | result = {'status_code': 500, 'status_text': 'JSON Parse Failed'}; 114 | } 115 | //console.log(result); 116 | cb(null, result); 117 | }) 118 | .on("error", function (err) { 119 | //console.log('response error : ' + err); 120 | cb(new Error("response.error: " + err), null); 121 | }); 122 | 123 | }); 124 | 125 | req.on("error", function (err) { 126 | cb(new Error("request.error: " + err), null); 127 | }); 128 | 129 | if(req.method == "POST") { 130 | req.end(request_query.post.image ? request_query.post.image : querystring.stringify(request_query.post)); 131 | } else { 132 | req.end(); 133 | } 134 | 135 | 136 | 137 | }; 138 | 139 | 140 | /** 141 | * Function to check if a passed string is a valid URL 142 | * @param {String} str The URL string to be checked 143 | * @return {Boolean} 144 | */ 145 | AlchemyAPI.prototype._urlCheck = function(str) { 146 | var parsed = url.parse(str) 147 | return (!!parsed.hostname && !!parsed.protocol && str.indexOf(' ') < 0); 148 | }; 149 | 150 | /** 151 | * Function to check if a passed string contains html (really just checking for tags <...>) 152 | * @param {String} str The text string to be checked 153 | * @return {Boolean} 154 | */ 155 | AlchemyAPI.prototype._htmlCheck = function(str) { 156 | var v = new RegExp(); 157 | v.compile("<[A-Za-z][A-Za-z0-9][^>]*>"); 158 | if (!v.test(str)) return false; 159 | return true; 160 | }; 161 | 162 | /** 163 | * Function to check if a passed value is a valid byte stream 164 | * @param {Byte} data Image byte stream 165 | * @return {Boolean} 166 | */ 167 | AlchemyAPI.prototype._imageCheck = function(data) { 168 | return data instanceof Buffer && imageType(data) !== null; 169 | }; 170 | 171 | /** 172 | * Function to return request parameters based in the AlchemyAPI rest interface 173 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 174 | * @param {String} method The Alchemy rest service method to call 175 | * @return {Object} 176 | */ 177 | AlchemyAPI.prototype._getQuery = function(data, opts, method) { 178 | var query = {}; 179 | //console.log(this.options); 180 | var options = extend(this.options, opts); 181 | query.data = data; 182 | query.post = {}; 183 | query.apimethod = "HTML" + method; 184 | 185 | var httpMethod = "POST"; 186 | if(this._imageCheck(data)) { 187 | query.apimethod = "image/Image" + method; 188 | query.post = {image: data}; 189 | query.headers = { 190 | 'content-length': data.length 191 | ,'content-type': imageType(data).mime 192 | }; 193 | } 194 | else if(this._urlCheck(data)){ 195 | query.apimethod = "URL" + method; 196 | httpMethod = "GET"; 197 | options.url = data; 198 | query.headers = { 199 | 'content-length': '0' 200 | } 201 | //console.log("======================1=================="); 202 | } 203 | else if(!this._htmlCheck(data)){ 204 | query.apimethod = "Text" + method; 205 | query.post = {text: data}; 206 | query.headers = { 207 | 'content-length': '' + querystring.stringify(query.post).length + '' 208 | ,'content-type': 'application/x-www-form-urlencoded' 209 | }; 210 | //console.log("======================2=================="); 211 | } 212 | else { 213 | query.post = {html: data}; 214 | query.headers = { 215 | 'content-length': '' + querystring.stringify(query.post).length + '' 216 | ,'content-type': 'application/x-www-form-urlencoded' 217 | }; 218 | //console.log("======================3=================="); 219 | } 220 | 221 | query.nice = this._generateNiceUrl(query.url, options, query.apimethod); 222 | query.nice.method = httpMethod; 223 | query.nice.headers = query.headers; 224 | 225 | return query; 226 | 227 | }; 228 | 229 | /** 230 | * Function to return the API key usage information 231 | * @param {Object} options Options to be passed to the AlchemyAPI (no options are currently supported) 232 | * @param cb 233 | */ 234 | AlchemyAPI.prototype.apiKeyInfo = function(options, cb) { 235 | // Since this request is nothing like the others, build it manually 236 | var opts = extend(this.options, opts), 237 | query = { 238 | data: "", 239 | post: {}, 240 | apimethod: "info/GetAPIKeyInfo", 241 | headers: { 242 | "content-length": 0 243 | } 244 | }; 245 | query.nice = this._generateNiceUrl(null, opts, query.apimethod) 246 | query.nice.method = "GET"; 247 | query.nice.headers = query.headers; 248 | this._doRequest(query, cb) 249 | }; 250 | 251 | /** 252 | * Function to return sentiment of the data passed in 253 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 254 | * @param {Object} options Options to be passed to the AlchemyAPI (no options are currently supported) 255 | * @return {Object} 256 | */ 257 | AlchemyAPI.prototype.sentiment = function(data, options, cb) { 258 | this._doRequest(this._getQuery(data, options, "GetTextSentiment"), cb); 259 | }; 260 | 261 | AlchemyAPI.prototype.sentiment_targeted = function(data, target, options, cb) { 262 | if(typeof target !== 'Object'){ 263 | options.target = target; 264 | } 265 | this._doRequest(this._getQuery(data, options, "GetTargetedSentiment"), cb); 266 | }; 267 | /** 268 | * Function to return relations in the data passed in 269 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 270 | * @return {Object} 271 | */ 272 | AlchemyAPI.prototype.relations = function(data, options, cb) { 273 | this._doRequest(this._getQuery(data, options, "GetRelations"), cb); 274 | }; 275 | 276 | /** 277 | * Function to return concepts in the data passed in 278 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 279 | * @return {Object} 280 | */ 281 | AlchemyAPI.prototype.concepts = function(data, options, cb) { 282 | this._doRequest(this._getQuery(data, options, "GetRankedConcepts"), cb); 283 | }; 284 | 285 | /** 286 | * Function to return entities in the data passed in 287 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 288 | * @return {Object} 289 | */ 290 | AlchemyAPI.prototype.entities = function(data, options, cb) { 291 | this._doRequest(this._getQuery(data, options, "GetRankedNamedEntities"), cb); 292 | }; 293 | 294 | /** 295 | * Function to return keywords in the data passed in 296 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 297 | * @return {Object} 298 | */ 299 | AlchemyAPI.prototype.keywords = function(data, options, cb) { 300 | this._doRequest(this._getQuery(data, options, "GetRankedKeywords"), cb); 301 | }; 302 | 303 | /** 304 | * Function to return taxonomies in the data passed in 305 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 306 | * @return {Object} 307 | */ 308 | AlchemyAPI.prototype.taxonomies = function(data, options, cb) { 309 | this._doRequest(this._getQuery(data, options, "GetRankedTaxonomy"), cb); 310 | }; 311 | 312 | /** 313 | * Function to return emotions of the data passed in 314 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 315 | * @return {Object} 316 | */ 317 | AlchemyAPI.prototype.emotions = function(data, options, cb) { 318 | this._doRequest(this._getQuery(data, options, "GetEmotion"), cb); 319 | }; 320 | 321 | /** 322 | * Function to return category of the data passed in 323 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 324 | * @return {Object} 325 | */ 326 | AlchemyAPI.prototype.category = function(data, options, cb) { 327 | this._doRequest(this._getQuery(data, options, "GetCategory"), cb); 328 | }; 329 | 330 | /** 331 | * Function to return image links of the data passed in 332 | * @param {String} data The text to be passed to Alchemy can either a url, html text 333 | * @return {Object} 334 | */ 335 | AlchemyAPI.prototype.imageLink = function(data, options, cb) { 336 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 337 | cb(new Error('The imageLinks method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 338 | return; 339 | } 340 | this._doRequest(this._getQuery(data, options, "GetImage"), cb); 341 | }; 342 | 343 | /** 344 | * Function to return image keywords of the data passed in 345 | * @param {String} data The text to be passed to Alchemy should be a url of a image 346 | * @return {Object} 347 | */ 348 | AlchemyAPI.prototype.imageKeywords = function(data, options, cb) { 349 | if (!this._imageCheck(data) && !this._urlCheck(data)) { 350 | cb(new Error('The imageKeywords method can only be used with a URL or a raw byte stream. HTML encoded text and plain text is not supported.'), null); 351 | return; 352 | } 353 | 354 | options.imagePostMode = 'raw'; 355 | this._doRequest(this._getQuery(data, options, "GetRankedImageKeywords"), cb); 356 | }; 357 | 358 | /** 359 | * Function to detect faces in an image from the data passed in 360 | * @param {String} data URL pointing to an image or the raw images bytes 361 | * @return {Object} 362 | */ 363 | AlchemyAPI.prototype.imageFaces = function(data, options, cb) { 364 | if (!this._imageCheck(data) && !this._urlCheck(data)) { 365 | cb(new Error('The imageFaces method can only be used with a URL or a raw byte stream. HTML encoded text and plain text is not supported.'), null); 366 | return; 367 | } 368 | 369 | options.imagePostMode = 'raw'; 370 | this._doRequest(this._getQuery(data, options, "GetRankedImageFaceTags"), cb); 371 | }; 372 | 373 | /** 374 | * Function to return language of the data passed in 375 | * @param {String} data The text to be passed to Alchemy can either a url, html text or plain text 376 | * @return {Object} 377 | */ 378 | AlchemyAPI.prototype.language = function(data, options, cb) { 379 | this._doRequest(this._getQuery(data, options, "GetLanguage"), cb); 380 | }; 381 | 382 | /** 383 | * Function to return author of the data passed in 384 | * @param {String} data The text to be passed to Alchemy can either a url or html text 385 | * @return {Object} 386 | */ 387 | AlchemyAPI.prototype.author = function(data, options, cb) { 388 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 389 | cb(new Error('The author method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 390 | return; 391 | } 392 | this._doRequest(this._getQuery(data, options, "GetAuthor"), cb); 393 | }; 394 | 395 | /** 396 | * Function to return publication date of the data passed in 397 | * @param {String} data The text to be passed to Alchemy can either a url or html text 398 | * @return {Object} 399 | */ 400 | AlchemyAPI.prototype.publicationDate = function(data, options, cb) { 401 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 402 | cb(new Error('The publicationDate method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 403 | return; 404 | } 405 | this._doRequest(this._getQuery(data, options, "GetPubDate"), cb); 406 | }; 407 | 408 | /** 409 | * Function to return plain text of the data passed in 410 | * @param {String} data The text to be passed to Alchemy can either a url or html text 411 | * @return {Object} 412 | */ 413 | AlchemyAPI.prototype.text = function(data, options, cb) { 414 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 415 | cb(new Error('The text method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 416 | return; 417 | } 418 | this._doRequest(this._getQuery(data, options, "GetText"), cb); 419 | }; 420 | 421 | /** 422 | * Function to return structured plain text of the data passed in retaining semantic meanings 423 | * @param {String} data The text to be passed to Alchemy can either a url or html text 424 | * @return {Object} 425 | */ 426 | AlchemyAPI.prototype.scrape = function(data, options, cb) { 427 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 428 | cb(new Error('The scrape method can only use a URL or HTML encoded text. Plain text is not supported.'), null); 429 | return; 430 | } 431 | this._doRequest(this._getQuery(data, options, "GetConstraintQuery"), cb); 432 | }; 433 | 434 | /** 435 | * Function to return the microformats used in a URL or html text passed in 436 | * @param {String} data The text to be passed to Alchemy can either a url or html text 437 | * @return {Object} 438 | */ 439 | AlchemyAPI.prototype.microformats = function(data, options, cb) { 440 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 441 | cb(new Error('The microformats method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 442 | return; 443 | } 444 | this._doRequest(this._getQuery(data, options, "GetMicroformatData"), cb); 445 | }; 446 | 447 | /** 448 | * Function to return the RSS/ATOM feeds found in a URL or html text passed in 449 | * @param {String} data The text to be passed to Alchemy can either a url or html text 450 | * @return {Object} 451 | */ 452 | AlchemyAPI.prototype.feeds = function(data, options, cb) { 453 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 454 | cb(new Error('The feeds method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 455 | return; 456 | } 457 | this._doRequest(this._getQuery(data, options, "GetFeedLinks"), cb); 458 | }; 459 | 460 | /** 461 | * Function to return the title found in a URL or html text passed in 462 | * @param {String} data The text to be passed to Alchemy can either a url or html text 463 | * @return {Object} 464 | */ 465 | AlchemyAPI.prototype.title = function(data, options, cb) { 466 | if (!this._urlCheck(data) && !this._htmlCheck(data)) { 467 | cb(new Error('The text method can only be used a URL or HTML encoded text. Plain text is not supported.'), null); 468 | return; 469 | } 470 | this._doRequest(this._getQuery(data, options, "GetTitle"), cb); 471 | }; 472 | 473 | /** 474 | * Function to run combined feature extraction from the URL, HTML or Text that is passed in. 475 | * @param {String} data The text to be passed to Alchemy can either a url, html or raw text 476 | * @return {Array} extract List of API features to be analysed on the input data. For valid feature names, see: http://www.alchemyapi.com/api/combined/urls.html 477 | * @return {Object} options Custom request parameters that are passed to the Alchemy API 478 | */ 479 | AlchemyAPI.prototype.combined = function(data, extract, options, cb) { 480 | if (!extract.length) { 481 | cb(new Error('The extract parameter must contain at least ONE feature.'), null); 482 | return; 483 | } 484 | options.extract = extract.join(","); 485 | this._doRequest(this._getQuery(data, options, "GetCombinedData"), cb); 486 | }; 487 | 488 | // Export as main entry point in this module 489 | module.exports = AlchemyAPI; 490 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "alchemy-api", 3 | "description": "An Alchemy API library for Node.js", 4 | "tags": [ 5 | "Alchemy", 6 | "Natural Language Processing", 7 | "util" 8 | ], 9 | "version": "1.3.3", 10 | "author": "Jason Morgan ", 11 | "contributors": [], 12 | "repository": { 13 | "type": "git", 14 | "url": "http://github.com/framingeinstein/node-alchemy.git" 15 | }, 16 | "license": "MIT", 17 | "engines": { 18 | "node": ">= 0.4.0" 19 | }, 20 | "scripts": { 21 | "test": "node_modules/.bin/nodeunit test" 22 | }, 23 | "devDependencies": { 24 | "nodeunit": ">=0.9.1" 25 | }, 26 | "dependencies": { 27 | "image-type": "^2.0.2" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | var Alchemy = require('../'); 2 | 3 | var testURL = "https://github.com/framingeinstein/node-alchemy"; 4 | var testRSSURL = "http://www.cnn.com/services/rss/"; 5 | var testAuthorURL = "http://www.cnn.com/2014/08/09/world/meast/iraq-crisis/index.html?hpt=hp_t1s"; 6 | var testHTML = 'Alchemy Test HTML

Alchemy Test HTML

This is something I am writing about. I have to write this as I do not feel like getting it from the web. So here it is. A bunch of text to test the API with

GEO: 37.386013, -122.082932
'; 7 | var testImageURL = "https://www.google.co.za/images/srpr/logo11w.png"; 8 | 9 | var apikey = "fcb11f5cebca4850ae9771ed0678ae4222d5733e"; 10 | 11 | module.exports = { 12 | 'check html match': function(test) { 13 | var alchemy = new Alchemy(apikey); 14 | var result = alchemy._htmlCheck(testHTML); 15 | test.deepEqual(result, true); 16 | test.done(); 17 | }, 18 | 'check url match': function(test){ 19 | var alchemy = new Alchemy(apikey); 20 | test.equal(alchemy._urlCheck(testURL), true); 21 | test.equal(alchemy._urlCheck(testHTML), false); 22 | test.equal(alchemy._urlCheck("http://feedproxy.google.com/~r/nmecom/rss/newsxml/~3/oAtTtYbCpl0/story01.htm"), true); 23 | test.equal(alchemy._urlCheck('http://google.com is my favorite site ever'), false); 24 | test.done(); 25 | }, 26 | 'get api key info': function(test) { 27 | var alchemy = new Alchemy(apikey); 28 | alchemy.apiKeyInfo({}, function(error, result) { 29 | test.ifError(error); 30 | test.ok(result); 31 | test.ok(result.hasOwnProperty('status')); 32 | test.ok(result.hasOwnProperty('consumedDailyTransactions')); 33 | test.ok(result.hasOwnProperty('dailyTransactionLimit')); 34 | //console.log(result.docSentiment); 35 | //test.deepEqual(result.status, "OK"); 36 | test.done(); 37 | }); 38 | }, 39 | 'get sentiment': function(test) { 40 | var alchemy = new Alchemy(apikey); 41 | alchemy.sentiment(testURL, {}, function(error, result) { 42 | test.ifError(error); 43 | //console.log(result.docSentiment); 44 | //test.deepEqual(result.status, "OK"); 45 | test.done(); 46 | }); 47 | }, 48 | 'get sentiment_targeted': function(test) { 49 | var alchemy = new Alchemy(apikey); 50 | alchemy.sentiment_targeted("Guy Somethington is an candidate but Hillary is not", "Guy Somethington", {}, function(error, result) { 51 | test.ifError(error); 52 | 53 | //console.log(result); 54 | test.deepEqual(result.docSentiment.type, "positive"); 55 | test.done(); 56 | }); 57 | }, 58 | 'get sentiment_targeted 2': function(test) { 59 | var alchemy = new Alchemy(apikey); 60 | alchemy.sentiment_targeted("Guy Somethington is an awesome candidate but Billary is not", "Billary", {}, function(error, result) { 61 | test.ifError(error); 62 | test.deepEqual(result.docSentiment.type, "negative"); 63 | //console.log(result); 64 | //test.deepEqual(result.status, "OK"); 65 | test.done(); 66 | }); 67 | }, 68 | 'get relations': function(test) { 69 | var alchemy = new Alchemy(apikey); 70 | alchemy.relations(testURL, {sentiment: 1}, function(error, result) { 71 | //console.log(result); 72 | test.ifError(error); 73 | //test.deepEqual(result.status, "OK"); 74 | test.done(); 75 | }); 76 | }, 77 | 'get concepts': function(test) { 78 | var alchemy = new Alchemy(apikey); 79 | alchemy.concepts(testURL, {}, function(error, result) { 80 | //console.log(result); 81 | test.ifError(error); 82 | //test.deepEqual(result.status, "OK"); 83 | test.done(); 84 | }); 85 | }, 86 | 'get emotions': function(test) { 87 | var alchemy = new Alchemy(apikey); 88 | alchemy.emotions(testURL, {}, function(error, result) { 89 | //console.log(result); 90 | test.ifError(error); 91 | //test.deepEqual(result.status, "OK"); 92 | test.done(); 93 | }); 94 | }, 95 | 'get entities': function(test) { 96 | var alchemy = new Alchemy(apikey); 97 | alchemy.entities(testURL, {}, function(error, result) { 98 | //console.log(result); 99 | test.ifError(error); 100 | //test.deepEqual(result.status, "OK"); 101 | test.done(); 102 | }); 103 | }, 104 | 'get keywords': function(test) { 105 | var alchemy = new Alchemy(apikey); 106 | alchemy.keywords(testURL, {}, function(error, result) { 107 | //console.log(result); 108 | test.ifError(error); 109 | //test.deepEqual(result.status, "OK"); 110 | test.done(); 111 | }); 112 | }, 113 | 'get russian keywords': function(test) { 114 | var alchemy = new Alchemy(apikey); 115 | alchemy.keywords("http://www.framingeinstein.com/russian.html", {}, function(error, result) { 116 | //console.log(result); 117 | test.ifError(error); 118 | //test.deepEqual(result.status, "OK"); 119 | test.done(); 120 | }); 121 | }, 122 | 'get russian keywords from text': function(test) { 123 | var alchemy = new Alchemy(apikey); 124 | var text = "сервис, который поможет все успеть и ничего не пропустить Создание событий добавьте в календарь напоминание о фильме или концерте или создавайте напоминания о своих делах Оповещение настройте оповещение и календарь предупредит вас Используйте другие службы Яндекса добавляйте события из Телепрограммы или Афиши"; 125 | alchemy.keywords(text, {}, function(error, result) { 126 | //console.log(result); 127 | test.ifError(error); 128 | //test.deepEqual(result.status, "OK"); 129 | test.done(); 130 | }); 131 | }, 132 | 'get taxonomies': function(test) { 133 | var alchemy = new Alchemy(apikey); 134 | alchemy.taxonomies(testURL, {}, function(error, result) { 135 | //console.log(result); 136 | test.ifError(error); 137 | //test.deepEqual(result.status, "OK"); 138 | test.done(); 139 | }); 140 | }, 141 | 'get category': function(test) { 142 | var alchemy = new Alchemy(apikey); 143 | alchemy.category(testURL, {}, function(error, result) { 144 | //console.log(result); 145 | test.ifError(error); 146 | //test.deepEqual(result.status, "OK"); 147 | test.done(); 148 | }); 149 | }, 150 | 'get image link': function(test) { 151 | var alchemy = new Alchemy(apikey); 152 | alchemy.imageLink(testURL, {}, function(error, result) { 153 | //console.log(result); 154 | test.ifError(error); 155 | //test.deepEqual(result.status, "OK"); 156 | test.done(); 157 | }); 158 | }, 159 | 'get image keywords': function(test) { 160 | var alchemy = new Alchemy(apikey); 161 | alchemy.imageKeywords(testImageURL, {}, function(error, result) { 162 | //console.log(result); 163 | test.ifError(error); 164 | //test.deepEqual(result.status, "OK"); 165 | test.done(); 166 | }); 167 | }, 168 | 'get language': function(test) { 169 | var alchemy = new Alchemy(apikey); 170 | alchemy.language(testURL, {}, function(error, result) { 171 | //console.log(result); 172 | test.ifError(error); 173 | //test.deepEqual(result.status, "OK"); 174 | test.done(); 175 | }); 176 | }, 177 | 'get language from html': function(test) { 178 | var alchemy = new Alchemy(apikey); 179 | alchemy.language(testHTML, {}, function(error, result) { 180 | //console.log(result); 181 | test.ifError(error); 182 | //test.deepEqual(result.status, "OK"); 183 | test.done(); 184 | }); 185 | }, 186 | 'get author': function(test) { 187 | var alchemy = new Alchemy(apikey); 188 | alchemy.author(testAuthorURL, {}, function(error, result) { 189 | //console.log(result); 190 | test.ifError(error); 191 | test.deepEqual(result.status, "OK"); 192 | //test.deepEqual(result.status, "OK"); 193 | test.done(); 194 | }); 195 | }, 196 | 'get publication date': function(test) { 197 | var alchemy = new Alchemy(apikey); 198 | alchemy.publicationDate(testURL, {}, function(error, result) { 199 | //console.log(result); 200 | test.ifError(error); 201 | test.deepEqual(result.status, "OK"); 202 | //test.deepEqual(result.status, "OK"); 203 | test.done(); 204 | }); 205 | }, 206 | 'scrape text': function(test) { 207 | var alchemy = new Alchemy(apikey); 208 | alchemy.scrape(testURL, {cquery:"all readme"}, function(error, result) { 209 | //console.log(result); 210 | test.ifError(error); 211 | test.deepEqual(result.status, "OK"); 212 | test.done(); 213 | }); 214 | }, 215 | 'get microformats': function(test) { 216 | var alchemy = new Alchemy(apikey); 217 | alchemy.microformats(testURL, {}, function(error, result) { 218 | //console.log(result); 219 | test.ifError(error); 220 | test.deepEqual(result.status, "OK"); 221 | //test.deepEqual(result.status, "OK"); 222 | test.done(); 223 | }); 224 | }, 225 | 'get feeds': function(test) { 226 | var alchemy = new Alchemy(apikey); 227 | alchemy.feeds(testRSSURL, {}, function(error, result) { 228 | //console.log(result); 229 | test.ifError(error); 230 | test.deepEqual(result.status, "OK"); 231 | //test.deepEqual(result.status, "OK"); 232 | test.done(); 233 | }); 234 | }, 235 | 'small positive text': function(test){ 236 | var alchemy = new Alchemy(apikey); 237 | alchemy.sentiment("This is awesome", {}, function(error, result) { 238 | test.ifError(error); 239 | //console.log(result.docSentiment); 240 | test.deepEqual(result.docSentiment.type, "positive"); 241 | test.done(); 242 | }); 243 | } 244 | }; 245 | -------------------------------------------------------------------------------- /test/number-#3.js: -------------------------------------------------------------------------------- 1 | var AlchemyAPI = require('../'); 2 | var alchemy = new AlchemyAPI('fcb11f5cebca4850ae9771ed0678ae4222d5733e'); 3 | alchemy.sentiment('This is awesome', {}, function(err, response) { 4 | if (err) throw err; 5 | 6 | var sentiment = response.docSentiment; 7 | console.log(sentiment); 8 | 9 | }); --------------------------------------------------------------------------------