如何减少Node JS中多个异步功能的运行时间?

时间:2018-11-28 19:23:20

标签: javascript node.js promise request

我想在Node js中抓取一些网页并从中获取一些数据。我的代码可以正常工作,但是大约需要1分钟才能完成抓取并返回所有数据。我已经为每个网站使用了异步功能,并承诺会收集所有信息。我最多处理了一百个链接。我认为运行时间太多了。我的代码结构中是否存在导致延迟的任何问题(请求承诺,承诺,异步,等待等的使用)? 所有功能都可以并行/异步运行,但是我的约束是我需要等到所有结果都来自每个网站。 我已将每个请求的超时限制为10秒。如果我进一步减少它,现有的ETIMEDOUT,ECONNRESET,ESOCKETTIMEDOUT错误(我仍然无法摆脱)会增加。

这是我的抓取功能之一:

const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require("fs");
const Promise = require("bluebird");

async function ntv() {
    var posts = [];
    try {
        const baseUrl = 'http://www.ntv.com';
        const mainHtml = await rp({uri: baseUrl, timeout: 10000});
        const $ = cheerio.load(mainHtml);
        const links =
            $(".swiper-slide")
                .children("a")
                .map((i, el) => {
                    return baseUrl + $(el).attr("href");
                }).get();

        posts = await Promise.map(links, async (link) => {
            try {
                const newsHtml = await rp({uri: link, timeout: 10000});
                const $ = cheerio.load(newsHtml);
                return {
                    title: $("meta[property='og:title']").attr("content"),
                    image: $("meta[property='og:image']").attr("content"),
                    summary: $("meta[property='og:description']").attr("content")
                }
            } catch (err) {
                if (err.message == 'Error: ETIMEDOUT') console.log('TIMEOUT error ' + link);
                else if (err.message == 'Error: read ECONNRESET') console.log('CONNECTION RESET error ' + link);
                else if (err.message == 'Error: ESOCKETTIMEDOUT') console.log('SOCKET TIMEOUT error ' + link);
                else console.log(err);
            }
        })
    } catch (e) {
        console.log(e)
    }
    return posts;
}

运行所有这些抓取功能的主要功能是:

var Promise = require("bluebird")
var fs = require("fs")

async function getData() {
    const sourceFunc = [func1(), func2(), ... , func10()];
    var news = [];

    await Promise.map(sourceFunc, async (getNews) => {
        try {
            const currentNews = await getNews;
            news = news.concat(currentNews);
        } catch (err) {
            console.log(err);
        }
    },{concurrency:10});

    news.sort(function(a,b){
        return new Date(b.time) - new Date(a.time);
    });
    fs.writeFile('./news.json', JSON.stringify(news, null, 3), (err) => {
        if (err) throw err;
    });
    return news;
}

1 个答案:

答案 0 :(得分:2)

我首先要在脚本中添加一些基准。在ntv()函数中找出花费最多时间的步骤并进行调整。

我的另一个猜测是,用cheerio解析整个html是一个瓶颈。使用String.prototype.substring()RegExp()来提取链接和发布信息可能会更有效。

更新:

查看并发TCP连接是否不是瓶颈。 Here是有关如何检查/调整的一些提示。

如果并发是问题,也许将工作拆分为几个程序是有意义的。例如

  1. 进程#1生成要获取的URL列表
  2. 进程2从列表中获取一个URL,从中获取HTML并保存在本地
  3. 第3步获取HTML并进行解析

如果您这样分割工作,则可以更好地并行化它。例如,节点仅在一个核心上运行,通过并行化,您可以运行多个进程,例如进行提取,从而受益于多个内核。并且还要规避对连接等每个进程的限制。

如果将URL和HTML保存到共享数据库中,则可以在多台计算机之间分配任务,从而进一步提高性能。