使用 restful api 调用带有 promise 包装器的 nightmare scraping 多站点

Use restful api to invoke nightmare scraping multi site with promise wrapper

我想用一个restfulapi报废多站点,我用express来实现。 但是我只是第一次用我的 api 成功触发了噩梦, 当我再次打电话给我的 api 时,我再也无法触发噩梦了:(

有什么想法吗?

另一个问题,在下面的情况下,我需要单独实例化新的 Nightmare 对象,以便我可以废弃三个不同的站点,有没有更聪明的方法来实现?

波纹管 getScrap 是我的 api 带有快速 Router GET 回调的控制器函数, 你也可以检查要点: https://gist.github.com/sevenLee/7091f8c56ccad3c0551b512f725af7da

import Nightmare from 'nightmare';
import cheerio from 'cheerio';

let nightmare = Nightmare({show: false});
let nightmare2 = Nightmare({show: false});
let nightmare3 = Nightmare({show: false});

const urlObject = {
  site1: 'http://www.site1.com',
  site2: 'http://www.site2.com',
  site3: 'http://www.site3.com'
};

export function getScrap(req, res){
  let result = {};

  result.site1 = {
    topList: []
  };
  result.site2 = {
    topList: []
  };
  result.site3 = {
    topList: []
  };

  const pro1 = Promise.resolve(
    nightmare
      .goto(urlObject.site1)
      .wait(200)
      .evaluate(() => {
        console.log('site1 into evaluate');
        return document.querySelector('.ninenine').innerHTML;
      })
      .end()
  )
  .then((html) => {
    let $ = cheerio.load(html);
    let tt = $('.horizontal-li');
    let sections = $(".section-board-title");

    sections.each((index, elm) => {
      if($(elm).text() === 'TopList'){
        $(elm).next('ul').find('li').each((index, elm_li) => {
          let title =$(elm_li).find('.cabinet-instruction').text();
          let price =$(elm_li).find('.cabinet-middle .price').text();
          let imgSrc = $(elm_li).find('.cabinet-img').attr('data-temp-src');
          if(title !== '' && price !==''){
            result.site1.topList.push({
              title,
              price,
              imgSrc
            });
          }
        });
      }
    });
  })
  .catch((err) => {
    console.log('site1 scrap err:', err);
    return res.status(400).send({reason:'site1 scrap err'});
  });

  const pro2 = Promise.resolve(
    nightmare2
      .goto(urlObject.site2)
      .wait(200)
      .evaluate(() => {
        return document.querySelector('.ninenine').innerHTML;
      })
      .end()
  )
  .then((html) => {
    let $ = cheerio.load(html);
    let tt = $('.horizontal-li');
    let sections = $(".section-board-title");

    sections.each((index, elm) => {
      if($(elm).text() === 'TopList'){
        $(elm).next('ul').find('li').each((index, elm_li) => {
          let title =$(elm_li).find('.cabinet-instruction').text();
          let price =$(elm_li).find('.cabinet-middle .price').text();
          let imgSrc = $(elm_li).find('.cabinet-img').attr('data-temp-src');
          if(title !== '' && price !==''){
            result.site2.topList.push({
              title,
              price,
              imgSrc
            });
          }
        });
      }
    });
  })
  .catch((err) => {
    console.log('site2 scrap err:', err);
    return res.status(400).send({reason:'site2 scrap err'});
  });

  const pro3 = Promise.resolve(
    nightmare3
      .goto(urlObject.site3)
      .wait(200)
      .evaluate(() => {
        return document.querySelector('#layout').innerHTML;
      })
      .end()
  )
  .then((html) => {
    let $ = cheerio.load(html);
    let sections = $(".pditem");

    sections.each((index, elm) => {

      let title = $(elm).find('.name').text();
      let price = $(elm).find('.price').find('span').eq(1).text();
      let imgSrc = ['www.site3.com',$(elm).find('li').eq(1).find('img').attr('src')].join('');

      result.site3.topList.push({
        title,
        price,
        imgSrc
      });
    });
  })
  .catch((err) => {
    console.log('site3 scrap err:', err);
    return res.status(400).send({reason:'site3 scrap err'});
  });


  Promise.all([pro1, pro2, pro3])
  .then(values => {
    res.json(result);
  })
  .catch((err) => {
    return res.status(500).send({reason:err.toString()});
  });
}

(来自我在 segmentio/nightmare#715 的原始回答。)

But I only triggered nightmare successfully in first time with my api, when I call again my api I can't trigger nightmare any more

您似乎在 getScrap() 之外定义实例,然后在 getScrap() 内部调用 .end(),这将结束并销毁 Nightmare/Electron 实例。一旦结束,它们将无法再使用。尝试将 Nightmare 实例的创建移动到 getScrap() 方法中。

another question, in below case, I need to instantiate new Nightmare object individually , so that I can scrap three different site, have any smarter way to achieve that?

取决于您的用例。您可以使用单个 Nightmare 实例并遍历 URL,但这将花费更多时间,因为 Nightmare 执行 必须 是顺序的。如果您对如何做这样的事情感到好奇,this article from nightmare-examples 可能值得一读。

最后,可能值得指出的是,根据您上面的代码,您不必使用 cheerio。我认为您可以使用 .evaluate() 和 CSS 查询来完成您想要的。