前述文章中提到的puppeteer爬取IATA数据的方法,此方法遗留下两个性能问题:
1.puppeteer大量使用async/await来实现同步代码,这样方便大家理解,但是爬取速度上有问题,没有发挥javascript异步的优势
2.程序中puppeteer仅仅打开了一个页面来进行爬取,可以预先打开多个页面来进行多页面爬取。
本文将就此两个问题给出具体的优化方案
优化说明
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的时间。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。