如何使用nodejs发送批量获取请求?

How to send bulk get requests using nodejs?

我用nodejs写了一个网络爬虫,发送get请求大概300个urls。 这是主循环:

for (let i = 1; i <= 300; i++) { 
    let page= `https://xxxxxxxxx/forum-103-${i}.html`
    await getPage(page,(arr)=>{
        console.log(`page ${i}`)
    })
}

这里是函数 getPage(url,callback):

export default async function getPage(url, callback) {
    await https.get(url, (res) => {
        let html = ""
        res.on("data", data => {
            html += data
        })
        res.on("end", () => {
            const $ = cheerio.load(html)
            let obj = {}
            let arr = []
            obj = $("#threadlisttableid tbody")
            for (let i in obj) {
                if (obj[i].attribs?.id?.substr(0, 6) === 'normal') {
                    arr.push(`https://xxxxxxx/${obj[i].attribs.id.substr(6).split("_").join("-")}-1-1.html`)
                }
            }
            callback(arr)
            console.log("success!")
        })
    })
        .on('error', (e) => {
            console.log(`Got error: ${e.message}`);
        })
}

我使用 cheerio 分析 HTML 并将我需要的所有信息放入名为 'arr' 的变量中。 程序在运行后正常运行一段时间后会报错,比如:

...
success!
page 121
success!
page 113
success!
page 115
success!
Got error: connect ETIMEDOUT 172.67.139.206:443
Got error: connect ETIMEDOUT 172.67.139.206:443
Got error: connect ETIMEDOUT 172.67.139.206:443
Got error: connect ETIMEDOUT 172.67.139.206:443
Got error: connect ETIMEDOUT 172.67.139.206:443
Got error: connect ETIMEDOUT 172.67.139.206:443

我有两个问题:

1.What是错误的原因?是因为我发送了太多的获取请求吗?如何限制请求频率?

2.As可以看到,页面访问顺序乱了,怎么控制?

我尝试过使用其他模块发送get请求(比如axios),但是没有成功。

As you can see, The order in which the pages are accessed is chaotic,how to control them?

await 是没有意义的,除非你在右边放一个 promisehttp.get 不做承诺。

你可以 wrap it in a promise but it would be easier to use an API which supports then natively such as node-fetch, axios, or Node.js's native fetch。 (在我看来,所有 API 都比 http.get 更易于使用,一般而言也不只是在流量控制方面)。

What is the reason for the error?

不清楚。

Is it because I am sending too many get requests?

这是一个可能的假设。

How can I limit the request frequency?

一旦您的 for 循环使用 promises 以便请求以串行方式发送而不是并行发送,您可以在每个请求之间插入一个 sleep

由于错误使用 await,循环没有等待上一个请求,因此同时触发了 http 请求。适当控制循环会限制请求频率。


for (let i = 1; i <= 300; i++) { 
    let page= `https://xxxxxxxxx/forum-103-${i}.html`
    var arr = await getPage(page);
    // use arr in the way you want
    console.log(`page ${i}`);
}

export default async function getPage(url) {
    // Declare a new promise, wait for the promise to resolve and return its value.
    return await new Promise((reso, rej) => {
        https.get(url, (res) => {
            let html = ""
            res.on("data", data => {
                html += data
            })
            res.on("end", () => {
                const $ = cheerio.load(html)
                let obj = {}
                let arr = []
                obj = $("#threadlisttableid tbody")
                for (let i in obj) {
                    if (obj[i].attribs?.id?.substr(0, 6) === 'normal') {
                        arr.push(`https://xxxxxxx/${obj[i].attribs.id.substr(6).split("_").join("-")}-1-1.html`)
                    }
                }
                reso(arr) // Resolve with arr
                console.log("success!")
            })
        })
        .on('error', (e) => {
            console.log(`Got error: ${e.message}`);
            throw e;
        })
    })
}