質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

1452閲覧

オライリーのサンプルが上手く稼働しない

Radec

総合スコア21

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2020/05/10 14:56

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に戻すと動く)

質問
・上記事象が発生してしまう原因は何になりますでしょうか

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

上記事象が発生してしまう原因

TaskQueue の コンストラクタに与える引数は concurrency に保持されるわけですが
next() メソッド内で whileの条件式に利用されています this.running < this.concurrency

concurrency の意味(同時並行性)するとおり、2 以上の数値を想定しているはずです。

投稿2020/05/10 15:14

AkitoshiManabe

総合スコア5434

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Radec

2020/05/11 09:25

このプログラムは、同時並行性 concurrency ≧ ネスト nesting が成立しないと正しく動かないということでしょうか。 (理解が誤っていたらご指摘ください。) 例えば、main → content1 → content2 / content3 を処理する過程で、  ・TaskQueueには content1 が push され task 実行中  ・content1 が再帰的に content2 を download したい  ・content2 / content3 が push され queue に滞留  ・しかし concurrency = 1 のため、content2 は task として開始できない
AkitoshiManabe

2020/05/11 09:28

> 同時並行性 concurrency ≧ ネスト nesting が成立しないと正しく動かない 私もそのように理解しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問