1. 程式人生 > 其它 >puppeteer爬取資料 await與forEach的問題解決方法

puppeteer爬取資料 await與forEach的問題解決方法

技術標籤:廣泛學習爬蟲

原文:https://www.jb51.cc/python/454296.html
在使用puppeteer爬取資料時,遇到了個報錯問題,才發現了這個forEach與await的問題。

一、問題
利用搜集到的url,再去爬去對應資料時,我採用了forEach迴圈爬取,去遇到提示如下:UnhandledPromiseRejectionWarning: Error: Navigation failed because browser has disconnected!,換句話說就是,還沒爬,瀏覽器就關閉了,大概就是非同步問題了。

《await與forEach》

簡化下問題的過程,先建立個簡單的sleep函式假裝處理業務邏輯,

async function sleep(time = 1000) {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve();
        },time);
    });
}

主體函式:

(async () => {
    await sleep();
    console.log('start');
 
    [0,1,2].forEach(async () => {
        await sleep();
        console.log(1111);
    });
    console.log('finish');
})();

用node執行下,實際,forEach中的await並沒有阻擋 finish 字元的輸出,並且forEach創造出來的函式之間也並沒有先後關聯,三個1111幾乎同時輸出。

二、原因
為什麼會造成這種原因呢?

再看下async與await的關係,只有在同一個async函式中,await才會按照對應的執行順序依次執行,我們都知道forEach創造了獨立的async函式(閉包問題),他內部的await已經與最外層的自執行async函式沒有關聯了(因為forEach並不是async函式),因此,迴圈出的三個方法也沒有關聯,都是各自等待1s就輸出。

三、解決方法
使用for迴圈
既然這樣,我們就是用不新建函式的迴圈,

for (let i = 0; i < 3; ++i) {
    await sleep();
    console.log(1111);
}

改造forEach
修改原型上的forEach方法,不推薦,你可以新寫個each方法,

Array.prototype.forEach = async function(fn) {
    let len = this.length;
    for (let i = 0; i < len; ++i) {
        await fn.call(this[i],i);
    }
};

這樣就可以了,最後,使用await寫非同步真是太爽了。

附上demo:await與foreach。