0

ここスタックにはいくつかの同様の質問がありますが、私にとってはうまくいく答えが得られません。私はノードと非同期プログラミングのアイデアにまったく慣れていないので、ご容赦ください。

私は現在4ステップのプロセスを持つスクレーパーを構築しています:

  1. リンク集をあげます
  2. img srcこれらの各リンクに移動し、ページ上の関連するすべてを見つけます
  3. 「次のページ」リンクを見つけて、そのhrefを取得し、上記から dom を取得して、hrefステップ #2 を繰り返します。
  4. これらimg srcはすべて配列に入れられて返されます

これがコードです。getLinks非同期に呼び出すことができますが、その中のwhileループは現在できません:

function scrape(url, oncomplete) {
    console.log("Scrape Function: " + url);
    request(url, function(err, resp, body) {
        if (err) {
            console.log(UHOH);
            throw err;
        }
        var html = cheerio.load(body);
        oncomplete(html);
    }
    );
}
function getLinks(url, prodURL, baseURL, next_select) {
    var urls = [];
    while(url) {
        console.log("GetLinks Indexing: " + url);
        var html = scrape(url, function(data) {
            $ = data;
            $(prodURL).each(function() {
                var theHref = $(this).attr('href');
                urls.push(baseURL + theHref);
            }
            );
            next = $(next_select).first().attr('href');
            url  = next ? baseurl + next : null;
        }
        );
    }
    console.log(urls);
    return urls;
}

現在、これは何もスクレイピングせずに無限ループに入ります。url = next ? baseurl + next : null;コールバックの外側に置くと、"next" is not definedエラーが発生します。

ノードフレンドリーにするためにこれを再加工する方法についてのアイデアはありますか? この問題の性質上、ブロックする必要があるようですね。

4

1 に答える 1

1

ループはしたいのにコールバックで非同期関数を使いたいというのはよくあるパターンです。非同期関数を待つことができないため、単純に while ループを使用することはできません。

1 つの解決策は、「スタック」(または配列) を使用することです。これに、処理する初期要素を入力します。処理する要素がさらに見つかったら、それらをこのスタックに追加します。そして、インデックスが配列の長さを超えるまで、インデックスをインクリメントして関数を再帰的に呼び出します。

例えば

function do_scrape( stack, this_url, callback ) {
    // get list of URLs from webpage at this_url
    ...
    stack.push( new_url ); // adding new element to array
    ...
    ...
    callback(); // process callback
}

function process_stack( stack_of_urls, idx ) {
    var this_url = stack_of_urls[idx];

    do_scrape(
        stack_of_urls,
        this_url,           
        function () {
            if ( idx + 1 < stack_of_urls.length ) {
                process_stack( stack_of_urls, (idx + 1) );
            } else {
                process.exit( 0 );
            }
        }
    );
}

var stack_of_urls = [ "http://www.yahoo.com/" ];
process_stack( stack_of_urls, 0 ); // start from index zero

これにアプローチする多くの方法があることに注意してください。効率のために、処理された要素をスタックから削除できます。また、スタックを最初から最後まで処理するか、最初から最後まで処理するかなどを選択できます。最後に、関数内で非同期関数を呼び出さないと、do_scrapeノードによってコールバックのタイトなループが発生することに注意してください。 js は、スタックが大きすぎると不平を言って中止します。

于 2013-06-04T15:05:04.900 に答える