Node.js デザインパターン第2版のサンプルプログラム(https://github.com/mushahiroyuki/ndp2)Chaptar3の06_web_spider_v4で一部箇所を書き換えると上手く稼働しなくなります。
ソース(Node.js)
index
1"use strict"; 2 3const request = require('request'); 4const fs = require('fs'); 5const mkdirp = require('mkdirp'); 6const path = require('path'); 7const utilities = require('./utilities'); 8const TaskQueue = require('./taskQueue'); 9let downloadQueue = new TaskQueue(1); //←元々は2、これを1に書き換えて実行 10 11function spiderLinks(currentUrl, body, nesting, callback) { 12 if(nesting === 0) { 13 return process.nextTick(callback); 14 } 15 16 const links = utilities.getPageLinks(currentUrl, body); 17 if(links.length === 0) { 18 return process.nextTick(callback); 19 } 20 21 let completed = 0, hasErrors = false; 22 links.forEach(link => { 23 downloadQueue.pushTask(done => { 24 spider(link, nesting - 1, err => { 25 if(err) { 26 hasErrors = true; 27 return callback(err); 28 } 29 if(++completed === links.length && !hasErrors) { 30 callback(); 31 } 32 done(); 33 }); 34 }); 35 }); 36} 37 38function saveFile(filename, contents, callback) { 39 mkdirp(path.dirname(filename), err => { 40 if(err) { 41 return callback(err); 42 } 43 fs.writeFile(filename, contents, callback); 44 }); 45} 46 47function download(url, filename, callback) { 48 console.log(`Downloading ${url}`); 49 request(url, (err, response, body) => { 50 if(err) { 51 return callback(err); 52 } 53 saveFile(filename, body, err => { 54 if(err) { 55 return callback(err); 56 } 57 console.log(`Downloaded and saved: ${url}`); 58 callback(null, body); 59 }); 60 }); 61} 62 63let spidering = new Map(); 64function spider(url, nesting, callback) { 65 if(spidering.has(url)) { 66 return process.nextTick(callback); 67 } 68 spidering.set(url, true); 69 70 const filename = utilities.urlToFilename(url); 71 fs.readFile(filename, 'utf8', function(err, body) { 72 if(err) { 73 if(err.code !== 'ENOENT') { 74 return callback(err); 75 } 76 77 return download(url, filename, function(err, body) { 78 if(err) { 79 return callback(err); 80 } 81 spiderLinks(url, body, nesting, callback); 82 }); 83 } 84 85 spiderLinks(url, body, nesting, callback); 86 }); 87} 88 89spider(process.argv[2], 1, (err) => { 90 if(err) { 91 console.log(err); 92 process.exit(); 93 } else { 94 console.log('Download complete'); 95 } 96});
TaskQueue
1"use strict"; 2 3module.exports = 4class TaskQueue { 5 constructor (concurrency) { 6 this.concurrency = concurrency; 7 this.running = 0; 8 this.queue = []; 9 } 10 11 pushTask (task) { 12 this.queue.push(task); 13 this.next(); 14 } 15 16 next() { 17 while (this.running < this.concurrency && this.queue.length) { 18 const task = this.queue.shift(); 19 task (() => { 20 this.running--; 21 this.next(); 22 }); 23 this.running++; 24 } 25 } 26};
utility
1"use strict"; 2 3const urlParse = require('url').parse; 4const urlResolve = require('url').resolve; 5const slug = require('slug'); 6const path = require('path'); 7const cheerio = require('cheerio'); 8 9module.exports.urlToFilename = function urlToFilename(url) { 10 const parsedUrl = urlParse(url); 11 const urlPath = parsedUrl.path.split('/') 12 .filter(function(component) { 13 return component !== ''; 14 }) 15 .map(function(component) { 16 return slug(component); 17 }) 18 .join('/'); 19 let filename = path.join(parsedUrl.hostname, urlPath); 20 if(!path.extname(filename).match(/htm/)) { 21 filename += '.html'; 22 } 23 return filename; 24}; 25 26module.exports.getLinkUrl = function getLinkUrl(currentUrl, element) { 27 const link = urlResolve(currentUrl, element.attribs.href || ""); 28 const parsedLink = urlParse(link); 29 const currentParsedUrl = urlParse(currentUrl); 30 if(parsedLink.hostname !== currentParsedUrl.hostname 31 || !parsedLink.pathname) { 32 return null; 33 } 34 return link; 35}; 36 37module.exports.getPageLinks = function getPageLinks(currentUrl, body) { 38 return [].slice.call(cheerio.load(body)('a')) 39 .map(function(element) { 40 return module.exports.getLinkUrl(currentUrl, element); 41 }) 42 .filter(function(element) { 43 return !!element; 44 }); 45};
仕様
・引数としてURLを指定
・指定したURLのbody中にある<a>タグを検知し、指定した回数分だけ再帰的に処理を呼び出しリンクを辿りローカルにダウンロードする
・例えばnest=2で指定すれば引数をlocalhost:3000/homeで与えた場合は/home→/content1→/contetn2, /contetn3までダウンロードする(homeはcontetn1へのリンクを持ち、content1はcontent2とcontetn3へのリンクをbody中に持っている)
一部書き換えた箇所
・TaskQueueクラス宣言時に与える引数を2→1に変更(即ちプログラムの多重度を1にする)
書き換え後の挙動
・上記の例で言うと、homeとcontetn1はダウンロードできるがcontent2とcontetn3がダウンロードできない(多重度を2に戻すと動く)
質問
・上記事象が発生してしまう原因は何になりますでしょうか
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/05/11 09:25
2020/05/11 09:28