【问题标题】:request() function returns undefined valuesrequest() 函数返回未定义的值
【发布时间】:2017-05-24 16:08:08
【问题描述】:

所以我目前正在制作一个谷歌浏览器扩展程序,每当我所有课程的新成绩发布到我的大学成绩册时都会通知我,所以目前我正在尝试迭代地抓取和抓取 URL 并将其与最后一次迭代(...?对此的建议将不胜感激!),目前当我使用 request() 函数时,即使使用异步,该函数当前返回未定义的响应和正文,并给了我另一个奇怪的东西如果我尝试 console.log 所有这些错误。

这是我之后遇到的错误:

bundle.js:24 Uncaught TypeError: Cannot read property 'headers' of undefined
    at Request._callback (bundle.js:24)
    at self.callback (bundle.js:54273)
    at Request.EventEmitter.emit (bundle.js:95413)
    at Request.start (bundle.js:54842)
    at Request.end (bundle.js:55610)
    at end (bundle.js:54652)
    at bundle.js:54666
    at Item.run (bundle.js:103974)
    at drainQueue (bundle.js:103944)

这是我的代码(更改了网址,因此您看不到我学校的登录网址):

var Crawler = require("simplecrawler"),
    url = require("url"),
    cheerio = require("cheerio"),
    request = require("request");

var initialURL = "https://www.fakeURL.com/";


var crawler = new Crawler(initialURL);

request("https://www.fakeURL.com/", {
    // The jar option isn't necessary for simplecrawler integration, but it's
    // the easiest way to have request remember the session cookie between this
    // request and the next
    jar: true,
    mode: 'no-cors'
}, function(error, response, body) {
    // Start by saving the cookies. We'll likely be assigned a session cookie
    // straight off the bat, and then the server will remember the fact that
    // this session is logged in as user "iamauser" after we've successfully
    // logged in

    crawler.cookies.addFromHeaders(response.headers["set-cookie"]);

    // We want to get the names and values of all relevant inputs on the page,
    // so that any CSRF tokens or similar things are included in the POST
    // request
    var $ = cheerio.load(body),
        formDefaults = {},
        // You should adapt these selectors so that they target the
        // appropriate form and inputs
        formAction = $("#login").attr("action"),
        loginInputs = $("input");

    // We loop over the input elements and extract their names and values so
    // that we can include them in the login POST request
    loginInputs.each(function(i, input) {
        var inputName = $(input).attr("name"),
            inputValue = $(input).val();

        formDefaults[inputName] = inputValue;
    });

    // Time for the login request!
    request.post(url.resolve(initialURL, formAction), {
        // We can't be sure that all of the input fields have a correct default
        // value. Maybe the user has to tick a checkbox or something similar in
        // order to log in. This is something you have to find this out manually
        // by logging in to the site in your browser and inspecting in the
        // network panel of your favorite dev tools what parameters are included
        // in the request.
        form: Object.assign(formDefaults, {
            username: "secretusername",
            password: "secretpassword"
        }),
        // We want to include the saved cookies from the last request in this
        // one as well
        jar: true
    }, function(error, response, body) {
        // That should do it! We're now ready to start the crawler
        crawler.interval = 10000 //600000 // 10 minutes
        crawler.maxConcurrency = 1; // 1 active check at a time
        crawler.maxDepth = 5;
        crawler.start();
    });
});

crawler.on("fetchcomplete", function(queueItem, responseBuffer, response) {
    console.log("Fetched", queueItem.url, responseBuffer.toString());
});

// crawler.interval = 600000 // 10 minutes
// crawler.maxConcurrency = 1; // 1 active check at a time
// crawler.maxDepth = 5;
//
// crawler.start();

需要注意的一点是,我在请求中添加了“no-cors”模式,这样每当我测试这个时我就可以停止遇到 CORS 问题,但这可能是导致此问题的原因吗?

谢谢!

编辑:我正在使用 Browserify 在浏览器中使用 require() 东西。我无法从 bundle.js 发布实际代码,因为它非常长,不适合这里。只是想澄清一下。谢谢!

EDIT2:这是我尝试执行 console.log(error) 时得到的:

Error: Invalid value for opts.mode
    at new module.exports (bundle.js:108605)
    at Object.http.request (bundle.js:108428)
    at Object.https.request (bundle.js:97056)
    at Request.start (bundle.js:54843)
    at Request.end (bundle.js:55613)
    at end (bundle.js:54655)
    at bundle.js:54669
    at Item.run (bundle.js:103977)
    at drainQueue (bundle.js:103947)

【问题讨论】:

  • 尝试弄清楚error的内容是什么,并检查response.status。您的 http 请求中似乎存在“一些错误”。没有更多信息,我只能说。
  • 我尝试检查错误,但问题是它给了我这个:错误:opts.mode 的值无效(原始帖子中的完整跟踪)。而且我无法检查 response.status 因为响应未定义。
  • @OmarBaradei 那么,这个答案最终对你有帮助吗?

标签: javascript google-chrome-extension web-scraping cors web-crawler


【解决方案1】:

正如 James 所说,如果您遇到错误,请通过将错误记录到控制台或您最喜欢的任何方法来显示调试信息来检查错误。

如果你得到Cannot read property 'headers' of undefined,正如你所说,response 是未定义的,所以你的第一个回调行将失败,因为它试图访问response.headers

这里简单调试的方法是console.log() 错误,然后到达问题所在的行(因为它停在那里),因此您只需添加console.log(error); 作为回调的第一行。

要走的路:

尽管您可以解决您在console.log(error) 中看到的问题,但此代码注定要失败,因为您没有检查是否收到错误并假设请求已成功完成。网络连接很混乱,请求可能由于很多原因而失败,因此在访问 request.headers 之前,您必须检查是否发生任何错误并将其记录下来(或将其显示给您的客户端,在 X 秒后重试请求,无论如何你最喜欢)。

提示:如果您有带有错误参数的回调,请检查它。是否有第一个参数是有原因的。

代码将如下所示:

request("https://www.fakeURL.com/", {
    jar: true,
    mode: 'no-cors'
}, function(error, response, body) {
    if (error) {
        console.log(error);
        makeTheRequestAgainIn(5000); // Milliseconds
    } else {
        doWhateverWith(response, body);
    }
});

错误:

只是您不能在浏览器中禁用 CORS。您可以在 node.js 中禁用它,因为它不是浏览器,这就是为什么在请求模块中有一个选项,但浏览器有安全措施是有原因的。如果可以避免它们,那么它们就没有意义了。

简而言之:是的,如果您没有在服务器中启用 CORS,您就会遇到问题。

Protip:在浏览器中处理 JavaScript 时,打开开发人员工具 (F12) 是一种很好的做法,就像您一样,这样您就可以看到 CORS 错误自动记录在控制台(或发生任何网络错误)。此外,切换到 Network 选项卡并检查请求标头、响应等也是一个好习惯。

编辑: 刚刚注意到 Chrome 扩展程序的事情(当)。扩展的限制较少,因此可以进行这些调用,您可以在此处阅读:https://developer.chrome.com/extensions/xhr

另外,检查了request npm 模块source code 并且没有no-cors 值。我认为您将Request APIrequest 模块混合在一起。

【讨论】:

  • 我尝试执行 console.log(error),它给了我这个:错误:opts.mode 的值无效(我将在上面的原始帖子中发布完整的内容,因为格式很奇怪厘米)。另外,根据您对 CORS 问题的看法,这是否意味着如果我尝试从学校的成绩簿网站上抓取/抓取,我将无法继续进行此项目?有没有什么可以做的来完成我想做的事情?再次感谢。
  • @OmarBaradei 是的,这意味着您不能在浏览器上禁用 CORS。顺便说一句,刚刚注意到“Chrome Extesion”的事情。以为你在做一个网站什么的。扩展不像普通浏览那样受限,你可以在这里查看,所以你可能可以做你想做的,只要是一个扩展:developer.chrome.com/extensions/xhr 只需删除模式参数,在清单中设置权限并执行您的电话,但不要忘记遵循我的错误处理指南。错误检查是开发时要做的主要事情,而您正在跳过它。
  • @OmarBaradei 另外,我检查了request npm 模块,它没有no-cors 参数的任何no-cors 值。我猜你在互联网上混合了信息。如果您使用 request 模块(与请求 API 不同),您必须检查其上的正确选项,此处为:github.com/request/request。如果您检查源代码中的正确行,您会发现没有“no-cors”选项:github.com/jhiesey/stream-http/blob/master/lib/request.js#L51
  • 哇,非常感谢您提供的所有帮助!所以,我能够取得重大进展,但经过一些研究,我意识到我大学的登录系统是在 CAS 后面设置的。对于如何通过 POST 请求使用 CAS 进行身份验证,这让我非常困惑。您对如何处理此问题有任何建议,或者可能是代码 sn-p 示例或我可以查看的内容以获取想法吗?我现在非常卡在这部分上,好像我尝试在我的发布请求中使用 /serviceValidate url,它给了我一个无效的请求,并说服务和票证参数都是必需的。
  • 所以我认为工作流的工作方式是这样的:我在门户页面上,单击登录按钮,它会将我带到由 CAS 支持的大学登录页面,用户输入他的 ID和密码,点击提交,它通过CAS服务器验证,成功或失败返回登录,如果成功,它将带我回到成功登录的门户页面(可以找到我的成绩)。希望有帮助给一个更好的主意。再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-02
  • 2017-08-06
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多