diff --git a/README.md b/README.md index eb4af18..3c93114 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,20 @@ istex-api-harvester --query hypertext --size 100 ``` +Pour moissonner les 50 documents les plus pertinants correspondant à l'ensemble des mots-clés contenus dans un fichier "keywords.txt" tout corpus confondus : + +```bash +istex-api-harvester --file keywords.txt --size 50 +``` + +Les mots-clés contenus dans le fichier doivent être ligne par ligne (**1 mot-clé par ligne**). + +Pour moissonner les 15 documents les plus pertinants correspondant à l'ensemble des mots-clés contenus dans un fichier "keywords.txt" et correspondant également à la requête hypertext tout corpus confondus : + +```bash +istex-api-harvester --file keywords.txt --query hypertext --size 15 +``` + Pour moissonner également les pleins textes : ```bash @@ -61,4 +75,6 @@ * ./springer/8db224e66c7fa77be4210d4d9ddb5dd84666066f.mods.xml * ./springer/8db224e66c7fa77be4210d4d9ddb5dd84666066f.pdf +En rajoutant l'option **--zip 1**, l'ensemble des fichiers téléchargés seront rassemblés dans un unique fichier zip au lieu d'être stockés dans un répertoire. + A noter que la longue chaîne de caractère est l'identifiant unique du document en question. A noter que le temps d'exécution du script dépend fortement de la qualité du réseau et du volume des données téléchargées. diff --git a/nodejs/istex-api-harvester.njs b/nodejs/istex-api-harvester.njs index b761c54..7199e7f 100755 --- a/nodejs/istex-api-harvester.njs +++ b/nodejs/istex-api-harvester.njs @@ -1,166 +1,206 @@ #!/usr/bin/env node -var program = require('commander'); -var request = require('superagent'); -var uuid = require('uuid'); -var fs = require('fs'); -var mkdirp = require('mkdirp'); -var async = require('async'); -var package = require('./package.json'); +var program = require('commander'); +var request = require('superagent'); +var uuid = require('uuid'); +var fs = require('fs'); +var mkdirp = require('mkdirp'); +var async = require('async'); +var package = require('./package.json'); program - .version(package.version) - .option('-q, --query [requete]', "La requete (?q=) ", '*') - .option('-c, --corpus [corpus]', "Le corpus souhaité (ex: springer, ecco, ...)", 'istex') - .option('-s, --size [size]', "Quantité de documents à télécharger", 10) - .option('-ft, --fulltext [0|1]', "Pour retourner ou pas le plein texte", 0) - .option('-v, --verbose', "Affiche plus d'informations", false) - .parse(process.argv); + .version(package.version) + .option('-q, --query [requete]', "La requete (?q=) ", '*') + .option('-c, --corpus [corpus]', "Le corpus souhaité (ex: springer, ecco, ...)", 'istex') + .option('-s, --size [size]', "Quantité de documents à télécharger", 10) + .option('-f, --file [file]', "Fichier contenant tous les mots-clés à rechercher", null) + .option('-ft, --fulltext [0|1]', "Pour retourner ou pas le plein texte", 0) + .option('-zip, --zip [0|1]', "Pour retourner l'ensemble des fichiers trouvés archivés en zip", 0) + .option('-v, --verbose', "Affiche plus d'informations", false) + .parse(process.argv); var dstPath = process.cwd() + '/' + program.corpus; mkdirp.sync(dstPath); var zipName = process.cwd() + '/' + uuid.v1() + '.zip'; +var zip = new require('node-zip')(); +var data; // découpe le téléchargement par pages // pour éviter de faire une énorme requête var nbHitPerPage = 100; -var nbPages = Math.floor(program.size / nbHitPerPage); -var nbLastPage = program.size - (nbPages * nbHitPerPage); -var ranges = []; +var nbPages = Math.floor(program.size / nbHitPerPage); +var nbLastPage = program.size - (nbPages * nbHitPerPage); +var ranges = []; for (var page = 0; page < nbPages; page++) { - ranges.push([ page * nbHitPerPage, nbHitPerPage]); + ranges.push([page * nbHitPerPage, nbHitPerPage]); }; -ranges.push([ nbPages * nbHitPerPage, nbLastPage ]); +ranges.push([nbPages * nbHitPerPage, nbLastPage]); + +// si fichier en paramètre, on récupère les mots-clés +if (program.file) { + try { + var raw = fs.readFileSync(program.file, 'utf-8'); + var keywords = raw.split('\n'); // Un mot-clé par ligne + var queryFile = ''; + for (i = 0; i < keywords.length; i++) { + queryFile += (keywords[i] != '') ? keywords[i] + ' OR ' : ''; // On vérifie qu'il ne s'agit pas d'un mot clé vide + } + program.query = (program.query != '*') ? queryFile + program.query : queryFile.slice(0, -4); // Suppression du dernier OR + } catch (e) { + console.error('Le fichier en paramètre ne peut pas être lu : ' + e); + } +} // lance les recherches et les téléchargements console.log("Téléchargement des " + program.size + - " premiers documents (metadata & fulltext) ici : " + dstPath); + " premiers documents (metadata & fulltext) ici : " + dstPath); // télécharge page par page var firstPage = true; -async.mapLimit(ranges, 1, function (range, cb) { - downloadPage(range, cb, function (body) { - if (firstPage) { - console.log("Nombre de documents dans le corpus " + program.corpus + " : " + body.total); - firstPage = false; +async.mapLimit(ranges, 1, function(range, cb) { + downloadPage(range, cb, function(body) { + if (firstPage) { + console.log("Nombre de documents dans le corpus " + program.corpus + " : " + body.total); + firstPage = false; + } + console.log('Téléchargement de la page ' + + (range[0] / nbHitPerPage + 1) + ' (' + (range[0] + range[1]) + ' documents)'); + }); +}, function(err) { + if (err) return console.log(err); + console.log('Téléchargements terminés'); + if (program.zip) { + var data = zip.generate({ + base64: false, + compression: 'DEFLATE' + }); + fs.writeFileSync(zipName, data, 'binary'); + fs.rmdirSync(dstPath); } - console.log('Téléchargement de la page ' + - (range[0] / nbHitPerPage +1 ) + ' (' + (range[0] + range[1]) + ' documents)'); - }); -}, function (err) { - if (err) return console.log(err); - console.log('Téléchargements terminés'); }); -var supportedTypes = ['all','txt','raw','pdf','tei','mods','tif','tiff','png','jpg','jpeg','zip','xml','html','gif']; - +var supportedTypes = ['all', 'txt', 'raw', 'pdf', 'tei', 'mods', 'tif', 'tiff', 'png', 'jpg', 'jpeg', 'zip', 'xml', 'html', 'gif']; // // Fonction de téléchargement d'une page // function downloadPage(range, cb, cbBody) { - var url = 'https://api.istex.fr/document/?q='+program.query+'&output=metadata' - + (program.fulltext != 0 ? ',fulltext' : '') - + ((program.corpus == 'istex') ? '' : ('&corpus=' + program.corpus)) - + '&from=' + range[0] + '&size=' + range[1]; - console.log(url); + var url = 'https://api.istex.fr/document/?q=' + program.query + '&output=metadata' + (program.fulltext != 0 ? ',fulltext' : '') + ((program.corpus == 'istex') ? '' : ('&corpus=' + program.corpus)) + '&from=' + range[0] + '&size=' + range[1]; + console.log(url); - // to ignore bad https certificate - process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; + // to ignore bad https certificate + process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; - var agent = request.agent(); - agent - .get(url) - .end(function (err, res) { - if (err) { - return cb(new Error(err)); - } - if (!res || !res.body || !res.body.hits) { - return cb(new Error('Response error: statusCode=' + res.statusCode)); - } - - // transmission du body pour les messages - cbBody(res.body); - - // lancement des téléchargement de façon séquentielle - async.mapLimit(res.body.hits, 1, function (item1, cb2) { - // extract the MODS from the returned JSON - var mods = { url: '', filename: item1.id + '.mods.xml' }; - item1.metadata.forEach(function (item2) { - if (item2.type && item2.type == 'mods') { - mods.url = item2.uri; - } - }); - if (program.fulltext) { - var wantedType = (supportedTypes.indexOf(program.fulltext.toLowerCase()) >= 0) ? program.fulltext.toLowerCase() : "pdf"; - if (program.fulltext) - // extract the fulltext from the returned JSON - var fulltext = []; - item1.fulltext.forEach(function (item2) { - if (wantedType == 'all' || item2.type == wantedType) { - fulltext.push({ - url: '' + item2.uri, - filename: '' + item1.id + '.' + item2.type - }); - } - }); - } - - // download the document (MODS and fulltext) - async.series([ - // download the MODS - function (callback) { - var stream = fs.createWriteStream(dstPath + '/' + mods.filename); - var req = request.get(mods.url); - req.pipe(stream); - stream.on('finish', function () { - if (program.verbose) { - console.log(mods.filename); + var agent = request.agent(); + agent + .get(url) + .end(function(err, res) { + if (err) { + return cb(new Error(err)); } - callback(null); - }); - stream.on('error', callback); - }, - // download the fulltext - function (callback) { - if (!program.fulltext) return callback(null); - console.log(fulltext); + if (!res || !res.body || !res.body.hits) { + return cb(new Error('Response error: statusCode=' + res.statusCode)); + } - if (Array.isArray(fulltext)) { - async.map(fulltext, function(fulltextItem, cbMap) { + // transmission du body pour les messages + cbBody(res.body); + + // lancement des téléchargement de façon séquentielle + async.mapLimit(res.body.hits, 1, function(item1, cb2) { + // extract the MODS from the returned JSON + var mods = { + url: '', + filename: item1.id + '.mods.xml' + }; + item1.metadata.forEach(function(item2) { + if (item2.type && item2.type == 'mods') { + mods.url = item2.uri; + } + }); + if (program.fulltext) { + var wantedType = (supportedTypes.indexOf(program.fulltext.toLowerCase()) >= 0) ? program.fulltext.toLowerCase() : "pdf"; + if (program.fulltext) + // extract the fulltext from the returned JSON + var fulltext = []; + item1.fulltext.forEach(function(item2) { + if (wantedType == 'all' || item2.type == wantedType) { + fulltext.push({ + url: '' + item2.uri, + filename: '' + item1.id + '.' + item2.type + }); + } + }); + } + + // download the document (MODS and fulltext) + async.series([ + // download the MODS + function(callback) { + var stream = fs.createWriteStream(dstPath + '/' + mods.filename); + var req = request.get(mods.url); + req.pipe(stream); + stream.on('finish', function() { + if (program.verbose) { + console.log(mods.filename); + } + // Si zip, on ajoute le fichier dedans et on supprime le fichier récupéré + if (program.zip) zippage(mods.filename); + callback(null); + }); + stream.on('error', callback); + + }, + // download the fulltext + function(callback) { + if (!program.fulltext) return callback(null); + console.log(fulltext); + + if (Array.isArray(fulltext)) { + async.map(fulltext, function(fulltextItem, cbMap) { + + var stream = fs.createWriteStream(dstPath + '/' + fulltextItem.filename); + var req = request.get(fulltextItem.url); + req.pipe(stream); + stream.on('finish', function() { + if (program.verbose) { + console.log(fulltextItem.filename); + } + // Si zip, on ajoute le fichier dedans et on supprime le fichier récupéré + if (program.zip) zippage(fulltextItem.filename); + cbMap(null); + }); + stream.on('error', cbMap); - var stream = fs.createWriteStream(dstPath + '/' + fulltextItem.filename); - var req = request.get(fulltextItem.url); - req.pipe(stream); - stream.on('finish', function () { - if (program.verbose) { - console.log(fulltextItem.filename); - } - cbMap(null); - }); - stream.on('error', cbMap); + }, function(err, results) { + callback(null); + }); + } else { + callback(null); + } - }, function(err, results){ - callback(null); + }, + ], + function(err) { + // MODS and fulltext downloaded + process.stdout.write('.'); + cb2(err); + }); + }, + function(err) { + console.log(''); + // page downloaded + cb(err, res.body); + }); - }); - } else { - callback(null); - } + }); +} - }, - ], function (err) { - // MODS and fulltext downloaded - process.stdout.write('.'); - cb2(err); - }); - }, function (err) { - console.log(''); - // page downloaded - cb(err, res.body); - }); - - }); +// +// Fonction de zippage +// +function zippage(filename) { + zip.file(filename, fs.readFileSync(dstPath + '/' + filename)); + fs.unlinkSync(dstPath + '/' + filename); // Suppression du fichier après compression } \ No newline at end of file diff --git a/nodejs/package.json b/nodejs/package.json index f791d46..5920e61 100644 --- a/nodejs/package.json +++ b/nodejs/package.json @@ -10,12 +10,13 @@ "author": "Stéphane Gully ", "license": "BSD", "dependencies": { - "superagent": "~0.15.2", - "commander": "~1.2.0", - "uuid": "~1.4.1", - "mkdirp": "~0.3.5", "async": "~0.2.9", - "fs-extra": "^0.9.1" + "commander": "~1.2.0", + "fs-extra": "^0.9.1", + "mkdirp": "~0.3.5", + "node-zip": "^1.1.0", + "superagent": "~0.15.2", + "uuid": "~1.4.1" }, "bin": { "istex-api-harvester": "./istex-api-harvester.njs" diff --git a/nodejs/test b/nodejs/test new file mode 100644 index 0000000..0015b9d --- /dev/null +++ b/nodejs/test @@ -0,0 +1,7 @@ +neurology +potential +minimum +kilometer +cat +dog +typhus