2

Я новичок в 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 секунду, и есть 10-секундное ожидание, вызванное setTimeout?
  3. Страница, наконец, отображается в PNG-файле, но содержимое этих асинхронно загруженных панелей по-прежнему пуст и отображается сообщение Loading....

Я новичок во всем этом, и мои знания Javascript очень ржавые.

ответ

2

Ryan Doherty предоставил большое объяснение, почему console.log('Scrolling'); никогда не вызывается, и вы выяснили, почему Found печатается в 10 раз самостоятельно!

И я хотел бы поговорить о том, как бороться с этими ajaxified страницами. Как правило, когда вы работаете с такими сайтами, вы можете определить критерий, по которому можно судить, загружена ли страница или, по крайней мере, ее части (хотя иногда, по праву отмечает Райан, это может быть очень сложно, особенно если много внешних ресурсов и/или iframes на странице).

В этом случае я предполагаю, что мы можем решить, что страница загрузилась, когда на этикетках «Загрузка» не осталось. Поэтому мы отключили javascript и проверяем эти ярлыки. Оказывается, они <div class="carousel-loading-card">. Это означает, что нам остается только ждать, пока они не исчезнут. Но для запуска их загрузки мы должны моделировать прокрутку страницы. В PhantomJS вы можете «изначально» сделать это, изменив настройку 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(); 
    } 

} 
+1

Хорошая идея по проверке элементов 'carousel-loading-card'! –

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 и все его включенные активы. Однако это не означает, что загружен асинхронно загруженный контент.

В-третьих, я считаю, вы видите выход «Found» в десять раз, потому что вы используете window.setInterval для проверки сноске и с помощью window.setTimeout сделать визуализацию. То, что происходит это:

  1. PhantomJS начинает загрузку страницы и вызывает ваш обратный вызов передается page.open() после загрузки.
  2. Колонтитул виден на нагрузке, так footer_visible верно
  3. В «нашли» блок работает для первого времени. Это устанавливает функцию для запуска через 10 секунд в будущем, которая отображает страницу, а затем выйдет. НО, потому что он использует window.setTimeout, ваш скрипт продолжается.
  4. Сценарий продолжается, и поскольку ваша внешняя функция настроена на запуск каждую секунду, она запускается снова! Он проверяет нижний колонтитул, находит его и настраивает функцию, выполняемую за 10 секунд, чтобы отобразить страницу. Он продолжает делать это в течение 10 секунд.
  5. Через 10 секунд первая функция, которая была настроена для отображения страницы, делает это, а затем сообщает PhantomJS о выходе. Это убивает все другие функции, которые были настроены для рендеринга страницы за 10 секунд.

Если вы действительно хотите, чтобы отобразить страницу, когда колонтитула в документе, вот ваш фиксированный код:

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;

Через много ручного тестирования и контроля качества, мы в конце концов пришли к решения, где мы гусеничного каждый HTTP PhantomJS запрос делает и следить за каждым шагом сделки (старт, прогресс, конец, не удалось). Только после завершения каждого отдельного запроса (или неудачного и т. Д.) мы начинаем «ждать». Мы даем странице 500ms либо начинать делать больше запросов, либо заканчивать добавление контента в DOM. После этого таймаут предположим, что страница выполнена.

+0

[Этот вопрос] (http://stackoverflow.com/q/11340038/1816580) предоставил вам покрытие. Я считаю, что эти ответы имеют лучшие идеи: [1] (http://stackoverflow.com/a/21401636/1816580), [2] (http://stackoverflow.com/a/38468106/1816580), [3 ] (http://stackoverflow.com/a/38132403/1816580). –