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

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

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

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

Q&A

解決済

1回答

2755閲覧

node.jsのpuppeteerで並列?処理する方法

kurosuke___

総合スコア217

Node.js

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

マルチスレッド

マルチスレッドは、どのように機能がコンピュータによって実行したのかを、(一般的にはスレッドとして参照される)実行の複合的な共同作用するストリームへ区分することが出来ます。

0グッド

1クリップ

投稿2018/04/19 03:22

node.jsのpuppeteerモジュールの練習で、複数のURLのスクリーンショットを保存していくようなコードを書いているのですが、ブラウザを起動するためのpuppeteer.launch()がとても遅いので、並列で行いたいのですが、nodeでのやり方がわかりません。

javascript

1const pp = require("puppeteer"); 2const uuid = require("node-uuid"); 3 4const urls = [ 5 "url0", 6 "url1", 7 "url2" 8] 9 10async function getScreenshot(url) { 11 const browser = await pp.launch(); 12 let page = await browser.newPage(); 13 await page.goto(url); 14 await page.screenshot({ 15 path: `${uuid.v4()}.png`, 16 fullpage: true 17 }); 18 browser.close(); 19} 20 21(async () => { 22 for (let url of urls) { 23 await getScreenshot(url); 24 } 25})();

上記のコードだと順次処理です。

node.jsの特徴としてシングルスレッドが紹介されている記事をよく見るので、並列化はできないのでしょうか?

よろしくお願いします。

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

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

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

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

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

karamarimo

2018/04/19 04:18

browserを1つにして共用するのはだめなのでしょうか?
kurosuke___

2018/04/19 09:28

ひとつにしたのですが、あんまり速くなかったのでブラウザ起動が遅いわけじゃなかったようです。
guest

回答1

0

ベストアンサー

GitHubのPage.jsを見に行くと、
817行目にcloseメソッドが用意されていますね。

ブラウザを消して立ち上げるのではなく、1ページ毎に立ち上げて操作という風にしてみてはどうでしょう?
ちょっと下のコードに修正して動かしてみてください。

JavaScript

1const pp = require("puppeteer"); 2const uuid = require("node-uuid"); 3 4const urls = [ 5 "url0", 6 "url1", 7 "url2" 8] 9 10const getScreenshot = async (browser, url) => { 11 let page = await browser.newPage(); 12 await page.goto(url); 13 await page.screenshot({ 14 path: `${uuid.v4()}.png`, 15 fullpage: true 16 }); 17 await page.close(); 18} 19 20(async () => { 21 const browser = await pp.launch(); 22 for (const url of urls) { 23 await getScreenshot(browser, url); 24 } 25 browser.close(); 26})()

おまけ: 並列っぽく操作していく

これだと逐次処理なのでそこそこ速くはなったかと思いますが、
もっと速度が欲しいかもしれません。

そういう時は2通りの手段があります。
(やってることはどちらも同じです)

  • 動作の発火とawaitを分ける
  • Promise.allを使う

動作の発火とawaitを分ける場合のコードはこうなります。
即時実行関数以外は同じなので省いておきます。

JavaScript

1(async () => { 2 const browser = await pp.launch(); 3 cosnt promises = []; 4 for (const url of urls) { 5 promises.push(getScreenshot(browser, url)); 6 } 7 for (const promise of promises) { 8 await promise; 9 } 10 browser.close(); 11})()

Promise.allを使う手法はこんな感じです。

JavaScript

1(async () => { 2 const browser = await pp.launch(); 3 await Promise.all( 4 urls.map(url => getScreenshot(browser, url)) 5 ); 6 browser.close(); 7})()

投稿2018/04/19 06:09

miyabi-sun

総合スコア21158

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

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

kurosuke___

2018/04/19 09:34

回答ありがとございます! おまけでご紹介いただいた方法を試してみたいと思います。 このようにすると何故並列っぽくなるのかが理解出来ていないので、Promiseとasync/awaitをもっと勉強してきます。
miyabi-sun

2018/04/19 09:50 編集

ざっくり説明すると、Promiseというのは「今実行中だよ」オブジェクトなのです。 https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise > pending, fulfilled, rejected この3状態ですね。 promise内で`resolve(value)`が叩かれた時点で状態がpending -> fulfilledに変化。 それと同時にthenに繋いだコールバック関数を発火します。 失敗すれば`reject(err)`が実行され、状態がpending -> rejectedに変化。 それと同時にcatchに繋いだコールバック関数を発火します。 async / awaitというのは糖衣構文です。 asyncで包まれた関数は内部をどう書こうが、絶対に`new Promise(fn)`を返します。 awaitは実行中のPromiseをひたすら見張る構文で、pending -> fulfilledに変化するまで一生待ち続けます。 従って、まず一度pending中のPromiseを全部作ってしまうのがうまいやり方で、 Promiseを返させた時点で裏で実行し続けているわけですね。 各Promiseは各々が勝手に処理を完了させ、まばらにfulfilledを返し始めます。 後からそれぞれがfulfilledになるのを待つという事をしたのがおまけのコードです。 Promise.allは引数の配列全てがPromiseである必要があり、 その配列の中身のPromise全てを見張り、全てがfulfilledになったら自分がfulfilledになるという挙動をします。 for文+awaitで各Promiseを1個ずつawaitで待っても同様の事が実現可能です。 もしこの説明で分からなくても、Promiseやasync / awaitを見に行けば理解が進むかと思います。 がんばって下さいね
kurosuke___

2018/04/19 09:55

なるほど・・・ mapで一括で作ったpending状態のPromiseの配列を一括でawaitしてるんですね。 asyncで包んだ関数をmapの処理やforで回しおわった状態が「並列っぽく」処理している状態で、待ってあげないとプログラムが終了しちゃうからawaitで待っているという感じですかね。 使い方に慣れるの大変そうですが、すごく便利なので頑張って勉強してみます! わかりやすい説明ありがとうございました!
miyabi-sun

2018/04/19 10:01

質問文のコードの時点で只者ではないとは思ってましたが、 この説明で分かるとは流石の理解力ですね。 勉強頑張ってください!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問