在异步加载所有内容后捕获页面

时间:2016-10-13 01:59:16

标签: javascript jquery asynchronous phantomjs webpage-screenshot

我是PhantomJS的新手并试图捕捉Trade Me的主页。到目前为止,这是我的代码:

var page = require('webpage').create();

page.open('http://trademe.co.nz', function () {

  // Checks for bottom div and scrolls down from time to time
  window.setInterval(function() {
      // Checks if there is a div with class=".has-more-items" 
      // (not sure if this is the best way of doing it)
      // var count = page.content.match(/class=".site-footer"/g);
      var footer_visible = page.evaluate(function() {
        return $('.site-footer').is(':visible');
      });

      if(!footer_visible) { // Didn't find
        console.log('Scrolling');
        page.evaluate(function() {
          // Scrolls to the bottom of page
          window.document.body.scrollTop = document.body.scrollHeight;
        });
      }
      else { // Found
        console.log('Found');
        // Do what you want
        window.setTimeout( function() {
            console.log('Capturing');
            page.render('phantom-capture.png', {format: 'png'});
            phantom.exit();
        }, 10000);
      }
  }, 1000); // Number of milliseconds to wait between scrolls

});

有几件事令我困惑:

  1. Scrolling这个词永远不会被打印出来。
  2. 它最终到达Found,并且该单词被打印10次。我认为这是因为它包含在setInterval块中,间隔为1秒,并且由setTimeout导致等待10秒?
  3. 页面最终呈现为PNG文件,但这些异步加载的面板的内容仍为空,并显示Loading...消息。
  4. 我对这一切都很陌生,而且我对Javascript的了解非常生疏。

2 个答案:

答案 0 :(得分:3)

您遇到了如何判断网页何时完全加载的常见问题。这其实很难!我在很久以前写过一篇关于这个问题的博客文章:https://sorcery.smugmug.com/2013/12/17/using-phantomjs-at-scale/(请参阅问题#1)以下是我对您的代码和问题的反馈:

首先,你不需要滚动来知道页脚是否已加载,如果元素占用文档中的空格,jQuery的:visible选择器将返回true,而不是在视口中:https://api.jquery.com/visible-selector/。我一般也不会使用PhantomJS的视口可见性,因为它确实无头。

其次,page.open()回调会在页面加载时触发。根据PhantomJS。这个主要是意味着什么时候它已经完全加载了HTML及其所有包含的资产。但是,这并不意味着已加载异步加载的内容。

第三,我相信你会看到输出'发现'十倍,因为您使用window.setInterval检查页脚使用window.setTimeout进行渲染。发生了什么事:

  1. PhantomJS开始加载页面,并在加载后调用传递给page.open()的回调。
  2. 页脚在加载时可见,因此footer_visible为真
  3. 发现'块运行第一次时间。这将设置一个函数,以便将来在10秒内运行呈现页面,然后退出。但是因为它使用了window.setTimeout,你的脚本会继续。
  4. 脚本继续,由于您的外部功能设置为每秒运行一次,它会再次运行!它检查页脚,找到它并设置一个在10秒内运行的功能来呈现页面。它会继续这样做10秒钟。
  5. 10秒后,设置为渲染页面的第一个函数执行此操作,然后告诉PhantomJS退出。这会杀死所有其他在10秒内渲染页面的功能。
  6. 如果你真的想在页脚在文档中时呈现页面,这是你的固定代码:

    var page = require('webpage').create();
    
    page.open('http://trademe.co.nz', function () {
    
        window.setInterval(function() {
            var footer_visible = page.evaluate(function() {
                return $('.site-footer').is(':visible');
            });
    
            if(footer_visible) {
                page.render('phantom-capture.png', {format: 'png'});
                phantom.exit();
            }
        }, 1000);
    });
    

    但是,一旦加载了所有内容,这将无法呈现,这是很多更难的问题。请阅读我上面链接的博客文章,了解如何执行此操作的提示。这是一个非常难的问题。如果您不想阅读我的博文,这里是TLDR;

      

    通过大量的手动测试和质量保证,我们最终来到了   我们跟踪PhantomJS发出的每个HTTP请求的解决方案   并观察交易的每一步(开始,进展,结束,   失败)。每个请求只完成一次(或失败等)   我们开始'等待'。我们给500ms页面开始制作更多   请求或完成向DOM添加内容。在超时之后我们   假设页面已完成。

答案 1 :(得分:2)

Ryan Doherty提供了很好的解释,为什么console.log('Scrolling');永远不会被调用,你弄清楚为什么Found被自己打印10次!

我想谈谈如何处理那些被禁化的页面。一般来说,当你使用这些网站时,你可以找出判断页面是否已加载的标准,或者至少部分是你需要的标准(尽管有时,正如Ryan正确指出的那样,它可能非常难,特别是如果有的话是一个页面上的很多外部资源和/或iframe)。

在这种情况下,我想我们可以决定在没有&#34;加载&#34;标签留下。所以我们关闭javascript并检查这些标签。原来他们是<div class="carousel-loading-card">。这意味着我们只需要等到它们消失。但要触发加载,我们必须模拟页面滚动。在PhantomJS中你可以原生地&#34;通过更改page.scrollPosition设置来实现这一点。

var page = require('webpage').create();

// Let's not confuse the target site by our default useragent 
// and native viewport dinemsions of 400x300
page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0';
page.viewportSize = { width: 1280, height: 1024 };

var totalHeight, scroll = 0;

page.open('http://trademe.co.nz', function(){

    totalHeight = page.evaluate(function(){
        return $(document).height();
    });

    wait();

});

function wait()
{
    var loading = page.evaluate(function(){
        return $(".carousel-loading-card").length;
    });

    if(loading > 0) {

        if(scroll <= totalHeight)
        {
            scroll += 200;

            page.scrollPosition = {
                top: scroll,
                left: 0
            };

            page.render('trademe-' + (new Date()).getTime() + '.jpg');
        }

        console.log(loading + " panels left. Scroll: " + scroll + "px");
        setTimeout(wait, 3000);        

    } else {
        // Restore defaults to make a full page screenshot at the end
        page.scrollPosition = { top: 0, left: 0 };        
        page.render('trademe-ready.png');
        phantom.exit();
    }

}