🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
Node.js

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

JavaScript

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

Q&A

解決済

1回答

427閲覧

ファイルを読み込んだ順番に出力されません。

TrueOgre2

総合スコア26

Node.js

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

JavaScript

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

0グッド

1クリップ

投稿2019/10/24 11:38

編集2019/10/24 14:17

スクレイピングのプログラムなのですが、読み込んだ順に表示がされません。
配列urlsに登録してあるページ順に、出力したいと思っています。
どのようにプログラムを直せば良いのでしょうか?

Node.jsは、シングルスレッドの非同期だということは、聞きかじった程度には知っています。
あまり大量にスクレイピングするのは良くないと怒られてしまう可能性もあるので
ここでは、実際のスクレイピング対象のURLではないもので、質問をさせて頂きます。
実際のプログラムは、GitHubにあげておきます。
疑問が解消された後に、GitHubはプライベートにしておきます。

どのように修正をすれば、読み込んだ順番どおりに、出力されるのでしょうか?

Node.js

1'use strict'; 2const rpn = require('request-promise-native'); 3const {JSDOM} = require('jsdom'); 4 5const urls = []; 6urls.push("https://www.○○○○.jp/1"); 7urls.push("https://www.○○○○.jp/2"); 8urls.push("https://www.○○○○.jp/3"); 9urls.push("https://www.○○○○.jp/4"); 10urls.push("https://www.○○○○.jp/5"); 11 12urls.forEach((url)=>{ 13 getTitle(url); 14}); 15 16function getTitle(url){ 17 rpn(url).then((body)=>{ 18 try { 19 // const dom = new JSDOM(body); 20 getDom(body).then((dom)=>{ 21 22 const selector = "#primary > div > div > div > article > header > h2 > a"; 23 const aList = dom.window.document.querySelectorAll(selector); 24 25 aList.forEach((a)=>{ 26 console.log(a.textContent); 27 }); 28 29 console.log("--------------------------------------------------------------------"); 30 }); 31 32 } catch (e) { 33 console.error(e); 34 } 35 }); 36} 37 38function getDom(body){ 39 return new Promise((resolve)=>{ 40 resolve(new JSDOM(body)); 41 }); 42}

GitHubのリンクです。
https://github.com/Megafriday/scraping/tree/request_promise

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

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

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

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

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

jun68ykt

2019/10/24 13:55

ご質問に > どのように修正をすれば、読み込んだ順番どおりに、出力されるのでしょうか? とありますが、上記にある、 > 順番どおりに とは、より詳しくいうと 配列urlsに入っているURLの順に という解釈でよいでしょうか?
TrueOgre2

2019/10/24 14:01

はい、配列urlsに入っているURLの順です。
jun68ykt

2019/10/24 14:36

回答しました。参考になれば幸いです。
guest

回答1

0

ベストアンサー

こんにちは

javascript

1urls.forEach((url)=>{ 2 getTitle(url); 3});

とすることで、urls の先頭から順に getTitle(url) が実行されますが、だからといって、各URLに対するHTTP(S)リクエストの返信が、リクエストした順に返ってくるとは限りません。urls に入っている順で結果も得たいのであれば、すべてのリクエストに対するレスポンスが返ってきてから、それぞれの結果を、urls に入っている順を保って得る必要がありますが、これを実現する方法として、

を使うことが考えられます。すなわち、各リクエストをPromise にして、 すべてのリクエストが返ってきてから、urls の要素の順に、各レスポンスから取得した結果を表示するという制御を Promise.all で行います。

以下、そのコード例です。

javascript

1'use strict'; 2const request = require('request'); 3const { JSDOM } = require('jsdom'); 4 5const urls = []; 6urls.push("https://www.example.com/xxxxxxx"); 7urls.push("https://www.example.com/xxxxxxx/eapg/2"); 8urls.push("https://www.example.com/hogehoge"); 9urls.push("https://www.example.com/hogehoge/eapg/2"); 10urls.push("https://www.example.com/hogehoge/eapg/3"); 11urls.push("https://www.example.com/hogehoge/eapg/4"); 12urls.push("https://www.example.com/hogehoge/eapg/5"); 13urls.push("https://www.example.com/hogehoge/eapg/6"); 14urls.push("https://www.example.com/hogehoge/eapg/7"); 15urls.push("https://www.example.com/hogehoge/eapg/8"); 16urls.push("https://www.example.com/hogehoge/eapg/9"); 17 18Promise 19 .all(urls.map(url => printTitle(url))) 20 .then(results => { 21 results.forEach(({ url, aList }) => { 22 console.log(url); 23 console.log([...aList].map(a => `\t${a.textContent}`).join('\n')); 24 }) 25 }).catch(e => { 26 console.error(e); 27 }) 28 29function printTitle(url) { 30 return new Promise((resolve, reject) => { 31 request(url, (e, response, body) => { 32 if (e) reject(e); 33 try { 34 const dom = new JSDOM(body); 35 const selector = "#primary > div > div > div > article > header > h2 > a"; 36 const aList = dom.window.document.querySelectorAll(selector); 37 resolve({ url, aList }); 38 } catch (e2) { 39 reject(e2); 40 } 41 }); 42 }); 43} 44

なお上記では、ご質問にある GitHubレポジトリにあるコードをもとに修正しており、各URLを差し障りのないものに変えています。

以上、参考になれば幸いです。

追記

request のドキュメント を眺めていましたら、これを作っている同じチームで、Promiseでラップしたものも提供しているとの一節がありました。その提供されているものの中の request/request-promise を使うと、以下のような感じで、もっと短く書けそうです。

javascript

1'use strict'; 2const rp = require('request-promise'); 3const { 4 JSDOM 5} = require('jsdom'); 6 7const urls = []; 8urls.push("https://www.example.com/xxxxxxx"); 9urls.push("https://www.example.com/xxxxxxx/eapg/2"); 10urls.push("https://www.example.com/hogehoge"); 11urls.push("https://www.example.com/hogehoge/eapg/2"); 12urls.push("https://www.example.com/hogehoge/eapg/3"); 13urls.push("https://www.example.com/hogehoge/eapg/4"); 14urls.push("https://www.example.com/hogehoge/eapg/5"); 15urls.push("https://www.example.com/hogehoge/eapg/6"); 16urls.push("https://www.example.com/hogehoge/eapg/7"); 17urls.push("https://www.example.com/hogehoge/eapg/8"); 18urls.push("https://www.example.com/hogehoge/eapg/9"); 19 20const selector = "#primary > div > div > div > article > header > h2 > a"; 21 22Promise 23 .all(urls.map(url => rp(url))) 24 .then(htmls => { 25 htmls.forEach((html, i) => { 26 console.log(urls[i]); 27 28 const dom = new JSDOM(html); 29 const aList = dom.window.document.querySelectorAll(selector); 30 console.log([...aList].map(a => `\t${a.textContent}`).join('\n')); 31 }) 32 }).catch(e => { 33 console.error(e); 34 });

投稿2019/10/24 14:30

編集2019/10/24 15:31
jun68ykt

総合スコア9058

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

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

TrueOgre2

2019/10/24 15:21 編集

回答して頂いて、どうもありがとうございました。 めちゃめちゃ色々なテクニックやネーミングなど、勉強になることがありすぎて、大変うれしいです。 まだ、全部理解しきれていなのですが、これからじっくりと教えて頂いた回答とコードを、読んで習得したいと思います。
jun68ykt

2019/10/24 15:33

どういたしまして。解決されたようでよかったです。 request の関連パッケージで request-promise というのがあり、これを使うとより短く書けそうだったので、回答に追記しました。ご参考まで。
TrueOgre2

2019/10/24 15:49 編集

どうもありがとうございます。 質問前に、「request-promise」や「request-promise-native」で何とかしようと考えたのですが、自分の実力が追いつかず、使いこなせなかったので追記のコード、大変嬉しいです。
jun68ykt

2019/10/24 15:49

あ、そうなんですね。それはよかったです ????
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問