Cheerio网页抓取错误

时间:2017-11-24 22:03:50

标签: node.js express cheerio

我正试图为所有教授刮取http://www.ratemyprofessors.com/。我的代码似乎得到以下错误:

    FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: node::Abort() [node]
 2: 0x10d3f9c [node]
 3: v8::Utils::ReportApiFailure(char const*, char const*) [node]
 4: v8::internal::V8::FatalProcessOutOfMemory(char const*, bool) [node]
 5: v8::internal::Factory::NewFillerObject(int, bool, v8::internal::AllocationSpace) [node]
 6: v8::internal::Runtime_AllocateInTargetSpace(int, v8::internal::Object**, v8::internal::Isolate*) [node]
 7: 0x292aec062bf
Aborted

我不知道我做了什么导致这个错误,但可能是因为我的循环?我需要循环超过1000万页但我不知道为什么它甚至只用10个循环给我这个错误。这是代码:

var express = require('express');
var path = require('path');
var request = require('request');
var cheerio = require('cheerio');
var fs = require('fs');
var app = express();
var count = 1;
var url;

while(count != 10){
    url = "http://www.ratemyprofessors.com/ShowRatings.jsp?tid=" + count;
    request(url, function(err, resp, body){
        var $ = cheerio.load(body);
        if($('.error').text().substring(0, 14) == "Page Not Found"){
            console.log("hello");
            count++;
            return;
        }else{
        console.log($('.error').text().substring(0, 14) );
        var pfname = $('.pfname');
        var plname = $('.plname');
        var professorName = pfname.text().replace(/\s/g, '') + " " +plname.text().replace(/\s/g, '');
        console.log(professorName);
        console.log(url);
        count++;
        }
        return;
    })
}

app.listen(3000, function(){
    console.log("server is now listening");
})

3 个答案:

答案 0 :(得分:0)

你可能正在做超过10个循环。您只在请求的回调中递增计数,这可能在发送请求后的几百毫秒内发生。在那段时间,while循环正在尽可能快地发送请求。

如果您只使用普通的for循环而不是while循环,这可能会更好。

答案 1 :(得分:0)

您应该创建一个URL数组,然后使用Cheerio迭代该数组。这段代码应该让你开始,虽然它可以使用很多改进。最后的超时是网址可以完成填充。

var request = require('request');
var cheerio = require('cheerio');

var url;
var urls = [];

for (i = 1; i < 10; i++) {
    url = 'http://www.ratemyprofessors.com/ShowRatings.jsp?tid=' + i;
    urls.push(url);
}

function done() {
    var arrayLength = urls.length;
    var promiseArray = [];
    for (var i = 0; i < arrayLength; i++) {
        request(urls[i], function(err, resp, body) {
            var $ = cheerio.load(body);
            if (
                $('.error')
                    .text()
                    .substring(0, 14) == 'Page Not Found'
            ) {
                console.log('hello');
                return;
            } else {
                console.log(
                    $('.error')
                        .text()
                        .substring(0, 14)
                );
                var pfname = $('.pfname');
                var plname = $('.plname');
                var professorName =
                    pfname.text().replace(/\s/g, '') +
                    ' ' +
                    plname.text().replace(/\s/g, '');
                console.log(professorName);
                console.log(url);
            }
            return;
        });
    }
}

setTimeout(function() {
    done();
}, 3000);
console.log(urls);

答案 2 :(得分:0)

我认为Raphael是正确的,因为你正在进行10个以上的循环,因为在请求的回调之前,计数不会增加。您可以使用async.whilst之类的东西来解决这个问题,它允许您在while循环中串行运行异步代码:

const request = require('request')
const async = require('async')

let count = 1

const test = () => count < 10

const iteratee = callback => {
  const url = 'http://www.ratemyprofessors.com/ShowRatings.jsp?tid=' + count

  request(url, (error, response, body) => {
    if (error) return callback(error)
    // do other stuff here
    count++
    callback()
  })
}

const done = error => {
  // all done
}

async.whilst(test, iteratee, done)

这可能更安全,更负责任,因为你阻止向他们的服务器发出并发请求(想象一下,如果你只是同时向同一个地方发射了1000万个HTTP请求 - 不好)。如果您确实想要发出并发请求,可以考虑使用async.mapasync.each等“并行”方法以及bottleneck等速率限制器。