前提・実現したいこと
Promiseの勉強を兼ねて、node.jsでクローラーを作っています。基本的には、URLにアクセスしてサイト内リンクが見つかったら再起的にリンクを探すというものです。
色々と試しているのですが、「クローリングが完了する前に次のステップに進んでしまう」「取得したURLを無視して永久ループに陥ってしまう」など、こちらの期待通りに動いてくれません。
Promiseの挙動を完全に理解していないこともあり、アドバイスを頂戴できれば幸いです。
これをマスターしたら async/awaitにも挑戦したいのですが、まずはpromiseの理解を優先しています。
該当のソースコード
nodejs
1/* 2 * Modules 3 */ 4 5const fetch = require('node-fetch'); 6const jsdom = require('jsdom'); 7const { JSDOM } = jsdom; 8 9/* 10 * Config 11 */ 12 13const base_url = "https://www.example.com/"; 14 15/* 16 * Functions 17 */ 18 19let urls = {}; 20 21function parserHtmlByUrlPromised(url) { 22 23 return new Promise((resolve, reject) => { 24 25 26 // 処理開始 27 console.log("Requesting HTML: " + url); 28 29 // 結果用オブジェクトの初期化 30 if (typeof urls[url] !== 'undefined') { 31 resolve("Already Created"); 32 } 33 urls[url] = {}; 34 35 // HTMLを取得 36 fetch(url, {}) 37 .then(res => { 38 if (res.ok) { // res.status >= 200 && res.status < 300 39 return res; 40 } else { 41 urls[url].success = false; 42 urls[url].error_msg = res.statusText; 43 reject(); 44 } 45 }) 46 .then(res => res.text()) 47 .then(html => { 48 // HTMLのパース 49 const dom = new JSDOM(html, { url: base_url }); 50 // TITLE 51 urls[url].title = dom.window.document.querySelector('title').textContent; 52 // URLの洗い出し 53 var abc = dom.window.document.querySelectorAll('a') 54 .forEach(a => { 55 // 不適切なURLは無視 56 if (typeof a.href !== 'undefined' && a.href.indexOf(base_url) === -1) return; 57 if (a.href.match(/.(css|jpg|png|gif|pdf)($|?.*)/)) return; 58 59 // 処理済みかどうかを確認 60 if (typeof urls[target_url] === 'undefined') { 61 62 // ここを無効にすると無限ループ(なぜ?) 63 urls[target_url] = {}; 64 65 // 再帰的に処理 66 parserHtmlByUrlPromised(target_url) 67 .then(() => { 68 return Promise.resolve(); 69 }); 70 } 71 }) 72 // HTMLのパース終了 73 resolve(url); 74 }).catch(e => { 75 urls[url].success = false; 76 urls[url].error_msg = e; 77 reject(); 78 }) 79 }); 80}; 81 82/* 83 * Main 84 */ 85parserHtmlByUrlPromised(base_url) 86 .then(() => { 87 console.log(urls); 88 }) 89 .catch(error => { 90 console.error(error); 91 });
試したこと
- 上記コードだと、クロールが完了する前に次のステップ(最後のthen)が実行される
- 結果を入れる変数(urls)をparserHtmlByUrlPromised()の中に入れてresolve()で返すようにしたら無限ループになる
- Promiseを使ってディレクトリを再帰的に辿ってファイル一覧を取得する - Qiitaを参考に再帰のところでPromise.all()を使ってみたけど無限ループになる
補足情報(FW/ツールのバージョンなど)
- v16.3.0
回答1件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/06/28 01:32
2021/06/28 01:34
2021/06/28 15:46
2021/06/29 01:45
2021/06/29 01:49
2021/06/29 02:05
2021/06/29 15:39