Web搜寻器的异步请求

时间:2019-02-12 12:41:43

标签: javascript web-scraping request

我有一个URL数组,我想从每个URL中抓取一个html表,并将其按与原始数组相同的顺序保存在另一个数组中。

由于节点的异步特性,我认为它无法按预期工作,每次的结果顺序都不同。

我在Google上搜索了很多,并尝试了不同的操作,例如使用自定义的async-forEach-function或request-promise而不是request,但没有任何效果。

const request = require('request');
const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');

let verbs = [];
let conjugations = [];
fs.readFileSync('verbs.txt', 'utf-8').split(/\r?\n/).forEach
(function(line){
  verbs.push(line);
});

verbs.forEach((verb) => {
    const URI = encodeURI("https://ru.wiktionary.org/wiki/" + verb);


    var options = {
        uri: URI,
        transform: function (body) {
            return cheerio.load(body);
        }


    };

    rp(options).then(function ($) {
        let table = $('span#Русский.mw-headline').parent().nextAll('table').first();
        conjugations.push(table.text());
        console.log(conjugations[0]);

    })
    .catch(function (err) {
    });


})




        
  
    

1 个答案:

答案 0 :(得分:2)

如果顺序很重要,请使用Promise.all

  

Promise.all()方法返回一个Promise,当作为可迭代对象传递的所有promise已解决或可迭代对象不包含promise时,该Promise进行解析。它以第一个承诺被拒绝的理由拒绝。

使事物井然有序的示例:

const verbs = ["hello", "world", "example"];

let timeout = 2000;
const promises = verbs.map(verb=>{
  timeout -= 500;
  return new Promise((resolve,reject)=>{
    setTimeout(function(){
      resolve(verb);
    }, timeout);
  });
});

Promise.all(promises).then(dataArray=>console.log(dataArray));

您的代码的解决方案。

const promises = verbs.map((verb) => {
  const URI = encodeURI("https://ru.wiktionary.org/wiki/" + verb);
  var options = {
    uri: URI,
    transform: function(body) {
      return cheerio.load(body);
    }


  };

  return rp(options);
})

Promise.all(promises).then(dataArray=>{
     dataArray.forEach(function($) {
      let table = $('span#Русский.mw-headline').parent().nextAll('table').first();
      conjugations.push(table.text());
      console.log(conjugations[0]);
    })
}).catch(function(err) {});

不利的是,如果一个请求失败,它们都会全部失败。

或者,您可以通过使用每个动词的索引来执行类似的操作(使用Promise.all来确定何时完成所有操作,但是可以忽略该步骤...)

const verbs = ["hello", "world", "example"];

const conjugations = [];
let timeout = 2000;
const promises = verbs.map((verb, index)=>{
  return new Promise((resolve, reject)=>{
    setTimeout(function(){
      conjugations[index] = verb;
      resolve();
    }, timeout);
    timeout -= 500;
  });
});

Promise.all(promises).then(()=>console.log(conjugations));

示例代码。

const request = require('request');
const rp = require('request-promise');
const cheerio = require('cheerio');
const fs = require('fs');

let verbs = [];
let conjugations = [];
fs.readFileSync('verbs.txt', 'utf-8').split(/\r?\n/).forEach(function(line) {
  verbs.push(line);
});

verbs.forEach((verb, index) => {
      const URI = encodeURI("https://ru.wiktionary.org/wiki/" + verb);


      var options = {
        uri: URI,
        transform: function(body) {
          return cheerio.load(body);
        }
      };

      rp(options).then(function($) {
          let table = $('span#Русский.mw-headline').parent().nextAll('table').first();
          conjugations[index] = table.text();
          console.log(conjugations[index]);

        })
        .catch(function(err) {});