我正在用Node和Cheerio构建一个网页刮板,对于某个网站,我得到以下错误(它只发生在这一个网站上,没有其他我试图刮的网站。

它每次都发生在不同的位置,所以有时是url x抛出错误,其他时候url x是好的,它是一个完全不同的url:

    Error!: Error: socket hang up using [insert random URL, it's different every time]

Error: socket hang up
    at createHangUpError (http.js:1445:15)
    at Socket.socketOnEnd [as onend] (http.js:1541:23)
    at Socket.g (events.js:175:14)
    at Socket.EventEmitter.emit (events.js:117:20)
    at _stream_readable.js:910:16
    at process._tickCallback (node.js:415:13)

这是非常棘手的调试,我真的不知道从哪里开始。首先,什么是套接字挂起错误?是404错误还是类似的错误?或者仅仅意味着服务器拒绝连接?

我在任何地方都找不到解释!

编辑:下面是(有时)返回错误的代码示例:

function scrapeNexts(url, oncomplete) {
    request(url, function(err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        $ = cheerio.load(body);
        // do stuff with the '$' cheerio content here
    });
}

没有直接调用关闭连接,但我使用节点请求(据我所知)使用http。get所以这是不需要的,如果我错了纠正我!

编辑2:下面是一段实际使用的代码,它会导致错误。prodURL和其他变量主要是前面定义的jquery选择器。这使用了Node的异步库。

function scrapeNexts(url, oncomplete) {
    request(url, function (err, resp, body) {

        if (err) {
            console.log("Uh-oh, ScrapeNexts Error!: " + err + " using " + url);
            errors.nexts.push(url);
        }
        async.series([
                function (callback) {
                    $ = cheerio.load(body);
                    callback();
                },
                function (callback) {
                    $(prodURL).each(function () {
                        var theHref = $(this).attr('href');
                        urls.push(baseURL + theHref);
                    });
                    var next = $(next_select).first().attr('href');
                    oncomplete(next);
                }
            ]);
    });
}

当前回答

对于请求模块用户

超时 有两种主要类型的超时:连接超时和读取超时。如果在客户端试图建立到远程机器的连接(对应于套接字上的connect()调用)时超时,则会发生连接超时。当服务器太慢而无法发送回部分响应时,就会发生读超时。

注意,连接超时发出一个ETIMEDOUT错误,读取超时发出一个ECONNRESET错误。

其他回答

我在使用Nano库连接Couch DB时也遇到了同样的问题。我尝试使用keepaliveagent库微调连接池,它一直失败的套接字挂起消息。

var KeepAliveAgent = require('agentkeepalive');

var myagent = new KeepAliveAgent({
    maxSockets: 10,
    maxKeepAliveRequests: 0,
    maxKeepAliveTime: 240000
});

nano = new Nano({
    url : uri,
    requestDefaults : {
        agent : myagent
    }
});

经过一番努力,我终于解决了这个问题——结果发现这是一个非常非常简单的错误。我通过HTTPS协议连接到数据库,但是我一直向我的nano对象传递一个keepalive代理,作为使用这个库显示的示例创建(它们依赖于一些使用http的默认值)。

使用httpagent时做了一个简单的改变:

var KeepAliveAgent = require('agentkeepalive').HttpsAgent;

我认为值得注意的是……

我正在为谷歌api创建测试。我用一个临时服务器拦截请求,然后转发给真正的api。我试图在请求中传递头部,但一些头部导致了另一端的express问题。

也就是说,在使用请求模块进行转发之前,我必须删除连接、接受和内容长度的报头。

let headers = Object.assign({}, req.headers);
delete headers['connection']
delete headers['accept']
delete headers['content-length']
res.end() // We don't need the incoming connection anymore
request({
  method: 'post',
  body: req.body,
  headers: headers,
  json: true,
  url: `http://myapi/${req.url}`
}, (err, _res, body)=>{
  if(err) return done(err);
  // Test my api response here as if Google sent it.
})

另一个值得一提的情况(对于Linux和OS X)是,如果您使用像https这样的库来执行请求,或者如果您传递https://..。作为本地服务实例的URL,您将使用端口443,这是一个保留的私有端口,您可能会在Socket挂起或ECONNREFUSED错误中结束。

相反,使用端口3000,f.e.,并执行http请求。

这给我带来了问题,因为我正在做这里列出的所有事情,但仍然抛出错误。事实证明,调用req.abort()实际上会抛出一个带有ECONNRESET代码的错误,因此您实际上必须在错误处理程序中捕获该错误。

req.on('error', function(err) {
    if (err.code === "ECONNRESET") {
        console.log("Timeout occurs");
        return;
    }
    //handle normal errors
});

我同时做web(节点)和Android开发,并一起打开Android Studio设备模拟器和docker,它们都使用端口8601,它抱怨套接字挂起错误,关闭Android Studio设备模拟器后,它在节点端工作良好。不要同时使用Android Studio设备模拟器和docker。