Nightmare.js 截图缓冲长度为 0。

7
我正在运行一个nightmare.js脚本,试图在页面上截取多个元素的屏幕截图。第一个元素可以成功地捕捉,但是所有其他在折叠下方的元素的长度都为零。我正在努力调试这个问题。任何帮助都将不胜感激。
基本上,该脚本遍历页面并选择与选择器匹配的所有元素。然后,使用异步调用收集响应并返回对象缓冲区。问题是折叠下方的元素没有被拍摄(缓冲区的长度最终为零)。我尝试使用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)

我正在尝试复现这个问题,但我发现在yahoo.com页面上没有".navbar"这样的元素。你能否澄清一下? - Evgeny Sorokin
Evgeny,当然可以。请在一个使用Bootstrap的页面上尝试,例如:https://getbootstrap.com/ - auser
@auser 为了悬赏有点自私 :). 我的回答有帮助吗? - devilpreet
@devilpreet 并非有意自私。我选择了您的答案,因为它可以解决特定的例子。当我选择多个站点时仍然存在问题,但您的答案肯定会有所帮助。 - auser
如果您正在运行Docker,请仔细检查SHM,因为默认值64MB可能太低了。 - WojonsTech
显示剩余2条评论
2个回答

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
尝试使用不同的流程,可以获得更好的结果: 方法上的差异是:先滚动到元素,然后获取其边界,再进行截图。
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);
}

这很棒。谢谢! - auser

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接