Node.js:给定 URL 数组,确定哪些是有效的

Posted

技术标签:

【中文标题】Node.js:给定 URL 数组,确定哪些是有效的【英文标题】:Node.js: given array of URLs, determine which are valid 【发布时间】:2016-10-20 09:03:44 【问题描述】:

我是 node http 模块的一个彻底的清理工具并且遇到了一些问题。

这里的最终目标是获取一个巨大的 url 列表,找出哪些是有效的,然后从这些页面中抓取某些数据。所以第一步是确定一个 URL 是否有效,这个简单的练习让我感到困惑。

假设我们有一个数组 allURLs:

["www.yahoo.com", "www.***.com", "www.sdfhksdjfksjdhg.net"]

我们的目标是迭代这个数组,向每个数组发出一个 get 请求,如果有响应,则将链接添加到工作 URL 列表(现在只是另一个数组),否则它会转到一个损坏 URL 列表。

var workingURLs = [];
var brokenURLs = [];
for (var i = 0; i < allURLs.length; i++) 
  var url = allURLs[i];
  var req = http.get(url, function (res) 
    if (res) 
      workingURLs.push(?????);  // How to derive URL from response?
    
  );

  req.on('error', function (e) 
    brokenURLs.push(e.host);
  );

我不知道如何从请求/响应对象本身正确获取 url,或者真正如何构造这种异步代码 - 因为我再次是一个 nodejs 擦洗:(

对于大多数使用 res.headers.location 的网站都有效,但有时标头没有此属性,这会在以后给我带来问题。我也尝试过控制台记录响应对象本身,这是一个混乱而徒劳的努力

我已经尝试将 url 变量推送到 workingURLs,但是当任何会触发推送的响应返回时,for 循环已经结束并且 url 永远指向 allURLs 数组的最后一个元素。

感谢任何可以提供帮助的人

【问题讨论】:

【参考方案1】:

您需要关闭 url 值才能访问它并保护它在下一次循环迭代时免受更改。 例如:

(function(url)
  // use url here
)(allUrls[i]);

对此最简单的解决方案是使用forEach 而不是for

allURLs.forEach(function(url)
  //....
);

承诺的解决方案可让您在工作完成后获得片刻:

    var http = require('http');
    var allURLs = [
      "http://www.yahoo.com/",
      "http://www.***.com/",
      "http://www.sdfhksdjfksjdhg.net/"
    ];
    var workingURLs = [];
    var brokenURLs = [];
    var promises = allURLs.map(url => validateUrl(url)
      .then(res => (res?workingURLs:brokenURLs).push(url)));
    Promise.all(promises).then(() => 
      console.log(workingURLs, brokenURLs);
    );
    // ----
    function validateUrl(url) 
      return new Promise((ok, fail) => 
        http.get(url, res => return ok(res.statusCode == 200))
          .on('error', e => ok(false));
      );
    

// Prevent nodejs from exit, don't need if any server listen.
var t = setTimeout(() =>  console.log('Time is over'); , 1000).ref();

【讨论】:

“您需要关闭 url 值才能访问它。” 这是不正确的。 http.get(...) 回调已经是一个闭包。 OP 需要的是在每次迭代时创建一个新范围,这就是 forEach 所做的。 to closure url value 我的意思是to encapse url value to some scope on each iterationhttp.get回调与初始url无关。 最好的答案是将两个答案结合起来,一个检查状态,一个处理.on('error') 只添加状态检查。 这很酷,我只知道会有一个使用 Promise 的解决方案(还没有使用过)。我实际上能够使用节点“request-promise”模块获得类似的东西,尽管这要简洁得多。谢谢!【参考方案2】:

你可以使用这样的东西(未测试):

const arr = ["", "/a", "", ""];

Promise.all(arr.map(fetch)
.then(responses=>responses.filter(res=> res.ok).map(res=>res.url))
.then(workingUrls=>
  console.log(workingUrls);
  console.log(arr.filter(url=> workingUrls.indexOf(url) == -1 ))
);

已编辑

Working fiddle(请注意,由于跨域,您不能在浏览器中向其他站点发出请求)。

已更新 @vp_arth 建议

const arr = ["/", "/a", "/", "/"];
let working=[], notWorking=[],
    find = url=> fetch(url)
    .then(res=> res.ok ? 
        working.push(res.url) && res : notWorking.push(res.url) && res);

Promise.all(arr.map(find))
.then(responses=>
    console.log('woking', working, 'notWorking', notWorking);
    /* Do whatever with the responses if needed */
);

Fiddle

【讨论】:

很舒服fetch api。可以写成arr.map(fetch)。对于所有 200-299 状态,还有布尔值 res.ok 所以在这个例子中, fetch 返回一个承诺吗? (不熟悉此 API)无论如何感谢您的帮助,这真的让我的承诺变得神秘! 是的!获取返回一个承诺。更多示例请看这里:developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

以上是关于Node.js:给定 URL 数组,确定哪些是有效的的主要内容,如果未能解决你的问题,请参考以下文章

如何在Node.js中提取部分字符串和字符串数组?

将 CSV 文件从 URL 导入 Node.js

node.js 数组实际上是哈希图吗?

GraphQL Node.js:确定查询中使用的类型

将 c++ 双缓冲区传递给 Node Js(直接)[Node JS : V8 c++ : Nan]

如何从给定 ID 获取用户信息并使用 Node.js 和 EJS 显示它[关闭]