前述文章中提到的puppeteer爬取IATA数据的方法,此方法遗留下两个性能问题:
1.puppeteer大量使用async/await来实现同步代码,这样方便大家理解,但是爬取速度上有问题,没有发挥javascript异步的优势
2.程序中puppeteer仅仅打开了一个页面来进行爬取,可以预先打开多个页面来进行多页面爬取。
本文将就此两个问题给出具体的优化方案

优化说明

image.png
1.优化版的流程中加入了页面缓存,也就是提前使用puppeteer打开N个页面,然后推入到一个数组中,需要用时从这个数组中随机拿到页面的handle并打开新的页面。
创建页面缓存代码

for (let i = 0; i < 5; i++) {
    let tmpPage = await globeBrowser.newPage();
            if (tmpPage) {
                pageQuen.push(tmpPage);
            }
        }

随机抽取页面的handle

let randomIndex = Math.floor(Math.random() * pageQuen.length);
let currPageHandleArr = pageQuen.splice(randomIndex, 1);
let currPageHandle = currPageHandleArr[0];
await openPage(currPageHandle, currPageNumber);

2.使用了Node.js 的EventEmitter待页面打开之后就通知获取数据获取和写入数据库,而不用等到这两个过程完成之后再打开下一页。
事件触发

async function openPage(pageHandle, pageNumber) {
    const _url = `${CHANGEURL}${pageNumber}&airline.search=`;
    await pageHandle.goto(_url, {waitUntil: 'networkidle2'});
    let elementPath = '';
    elementPath = '.airlinecodessearchblock';
    await pageHandle.waitForSelector(elementPath);
    pageEmitter.emit('getPageData', pageHandle, pageNumber);
}

触发事件监听器

pageEmitter.on('getPageData', (pageHandle, pageNumber) => {
    consoleOutPut(`开始获取数据${pageNumber}的数据`);
    (async (airlineData, pageNumber) => {
        consoleOutPut(`开始解析第${pageNumber}的数据...`);
        const parseResult = await pageHandle.evaluate(() => {
            let flightList = [];
            const airlineBlock = document.querySelector('div.airlinecodessearchblock');
            const trItems = airlineBlock.querySelectorAll('tbody>tr');
            if (trItems.length > 0) {
                for (let i = 0; i < trItems.length; i++) {
                    let tdItems = trItems[i].querySelectorAll('td');
                    let data = {};
                    data.companyName = tdItems[0].innerText.replace('\'', '');
                    data.country = tdItems[1].innerText.replace('\'', '');
                    data.code2 = tdItems[2].innerText;
                    data.airlinePrefix = tdItems[3].innerText;
                    data.pax = tdItems[4].innerText;
                    flightList.push(data);
                }
            }
            return flightList;
        });
        consoleOutPut(`完成解析第${pageNumber}的数据...`);
        if (parseResult.length > 0) {
            pageEmitter.emit('writeToDB', parseResult, pageNumber);
            pageQuen.push(pageHandle);
        }
    })(pageHandle, pageNumber);
    consoleOutPut(`结束获取数据${pageNumber}的数据`);
})

注意事件监听器中使用了异步的立即执行函数,避免了事件监听器需要异步等待。

(async (airlineData, pageNumber) => {
   //实际代码
   
}(airlineData, pageNumber);

结论

原版代码爬取50个页面大约需要400秒,优化之后大概节约了1/3的时间。


深圳飘
55 声望40 粉丝

暂时没有啥介绍的。


引用和评论

0 条评论