Nightmare.js屏幕截图缓冲区长度为0

时间:2017-06-16 03:31:50

标签: javascript typescript screenshot nightmare

我正在运行一个nightmare.js脚本,我试图在页面上截取多个元素。

第一个元素被捕获得很好,但是折叠下方的每个其他元素都以零长度捕获。我正在努力调试这个问题。任何帮助都会非常感激。

基本上,此脚本遍历页面并选择 all 页面上与选择器匹配的元素。然后,使用async收集响应并返回对象缓冲区。问题是折叠下方的元素没有截屏(缓冲区长度最终为零)。我尝试wait()并滚动到元素,但我还没有取得任何成功。

import * as Nightmare from 'nightmare'
import * as vo from 'vo'
import * as async from 'async'
import * as fs from 'fs'

const urls:String[] = [
  'https://yahoo.com/'
]


Nightmare.action('snap', function(selector:String, done:Function) {
  const self = this;

  this.evaluate_now(function (selector) {
    return Array.from(document.querySelectorAll(selector))
    .map((ele:Element) => {
      if (ele) {
        const rect = ele.getBoundingClientRect()
        const r:Function = Math.round
        return {
          x: r(rect.left),
          y: r(rect.top),
          width: r(rect.width),
          height: r(rect.height)
        }
      }
    })
  }, function(err, clips) {
    if (err) return done(err)
    if (!clips) return done(new Error(`Selector not found`))
    let snaps = []
    const snap = (clip, cb) => {
      self
        .scrollTo(clip.y - clip.height, clip.x)
        .screenshot(clip, cb)
        .run()
    }
    async.mapSeries(clips.reverse(), snap, (err, res) => {
      done(err, res)
    })
  }, selector)
})

const scrape = (url) => {
  const nightmare = Nightmare({
    show: true
  });
  nightmare
    .goto(url)
    .snap('.navbar')
    .end()
    .then((buffers:Buffer[]) => {
      buffers.forEach((data, index) => {
        fs.writeFileSync(`images/navbar-${index}.png`, data)
      })
    })
}

urls.forEach(scrape)

2 个答案:

答案 0 :(得分:4)

实际上,screenshot()函数从可见屏幕获取坐标 例如,如果任何元素的(x,y)是(10,1000)并且你的窗口大小是(800,600)那么你可以滚动(900:element.y,0)然后在(element.y-scroll)截取屏幕截图。 y = 100,element.x)

我终于让代码工作了:

const Nightmare = require('nightmare');
const fs = require('fs');
const nightmare = Nightmare({
  show: true,
  openDevTools: true,
});

nightmare.goto('https://in.news.yahoo.com/')
  .wait(1000)
  .evaluate(getBounds, '.Cf')
  .then(function(rects) {
    console.log(rects);

    function getScreenshot(rects, index) {
      if (index == rects.length) return;
      nightmare.scrollTo(rects[index].y, 0)
        .screenshot(__dirname + '/images/navbar' + index + '.png', {
          //60 is height of the top element which remains
          x: rects[index].x-10,
          y: 60,
          width: rects[index].width+30,
          height: rects[index].height +60
        })
        .then(function() {
          console.log("Calling next. " + index);
          getScreenshot(rects, index + 1);
        }).catch(function(err) {
          console.log(err);
        })
    };

    getScreenshot(rects, 0);
  })
  .catch(function(err) {
    console.log(err);
  });

function getBounds(selector) {
  var elements = document.querySelectorAll(selector);
  if (elements && elements.length > 0) {
    var arr = [];
    const r = Math.round;
    for (var ii = 0; ii < elements.length; ii++) {
      var rect = elements[ii].getBoundingClientRect();
      arr.push({
        x: r(rect.left),
        y: r(rect.top),
        width: r(rect.width),
        height: r(rect.height)
      })
    }
    console.log("Elements found: ", arr.length);
    return arr;
  }
  return null;
}

答案 1 :(得分:1)

从不同的流程尝试,给出了更好的结果: 方法的不同之处在于:首先滚动到元素,然后取其边界,然后继续进行截屏。

const Nightmare = require('nightmare');
const fs = require('fs');
const nightmare = Nightmare({
  show: true,
  openDevTools: false,
  gotoTimeout: 45000
});

nightmare.goto('https://www.google.co.in/?#safe=off&q=nightmare')
  .wait(1000)
  .evaluate(getElements, 'div.g')
  .then(() => {
    console.log("Calling screenshots: ");
    getAllScreenshots(0);
  })
  .catch(function(err) {
    console.log(err);
  });

function getAllScreenshots(index) {
  console.log("Called with index: ", index)
  nightmare.evaluate(function(index) {
      const r = Math.round;
      if(index >= window.__nightmare.output.length) {
        return false;
      }
      var element = window.__nightmare.output[index];
      console.log(index, element.innerHTML);
      element.scrollIntoView(false);
      var bound = element.getBoundingClientRect();
      return {
        x: r(bound.left)-10,
        y: r(bound.top)-10,
        width: r(element.clientWidth)+40,
        height: r(element.clientHeight)+10
      }
    }, index)
    .then(function(bound) {
      if(!bound) {
        return;
      }
      console.log("Taking screenshot: ", bound);
      nightmare.wait(500).screenshot(__dirname + '/images/navbar' + index + '.png', bound)
        .then(function() {
          console.log("Calling Next of: ", index);
          getAllScreenshots(index + 1);
        }).catch(function(err) {
          console.log(err);
        })
    })
    .catch(function(err) {
      console.log(err);
    });
}

function getElements(selector) {
  var elements = document.querySelectorAll(selector);
  window.__nightmare.output = elements;
  console.log(elements.length);
}