【问题标题】:How to scroll down with Phantomjs to load dynamic content如何使用 Phantomjs 向下滚动以加载动态内容
【发布时间】:2013-05-09 19:41:16
【问题描述】:

我正在尝试从当用户向下滚动到底部(无限滚动)时动态生成内容的页面中抓取链接。我尝试用 Phantomjs 做不同的事情,但无法收集第一页以外的链接。假设底部加载内容的元素具有类.has-more-items。它在滚动时加载最终内容之前可用,然后在 DOM 中变得不可用(显示:无)。这是我尝试过的东西-

  • var page = require('webpage').create(); 之后将 viewportSize 设置为较大的高度

page.viewportSize = { 宽度:1600,高度:10000, };

  • page.open 中使用page.scrollPosition = { top: 10000, left: 0 } 但没有效果-
page.open('http://example.com/?q=houston', function(status) {
   if (status == "success") {
      page.scrollPosition = { top: 10000, left: 0 };  
   }
});
  • 还尝试将其放入 page.evaluate 函数中,但这给出了

引用错误:找不到变量页面

  • 尝试在 page.evaluatepage.open 中使用 jQuery 和 JS 代码,但无济于事-

$("html, body").animate({ scrollTop: $(document).height() }, 10, 功能() { //console.log('检查执行'); });

照原样,也在document.ready 内部。对于 JS 代码也是如此-

window.scrollBy(0,10000)

照原样,也在window.onload内部

我现在真的被它打动了 2 天,却找不到办法。任何帮助或提示将不胜感激。

更新

我在https://groups.google.com/forum/?fromgroups=#!topic/phantomjs/8LrWRW8ZrA0找到了一段有用的代码

var hitRockBottom = false; while (!hitRockBottom) {
    // Scroll the page (not sure if this is the best way to do so...)
    page.scrollPosition = { top: page.scrollPosition + 1000, left: 0 };

    // Check if we've hit the bottom
    hitRockBottom = page.evaluate(function() {
        return document.querySelector(".has-more-items") === null;
    }); }

.has-more-items 是我想要访问的元素类,它最初位于页面底部,当我们向下滚动时,它会进一步向下移动,直到所有数据都加载完毕,然后变得不可用。

但是,当我测试时,很明显它在没有向下滚动的情况下运行到无限循环(我渲染图片进行检查)。我也尝试用下面的代码替换page.scrollPosition = { top: page.scrollPosition + 1000, left: 0 };(一次一个)

window.document.body.scrollTop = '1000';
location.href = ".has-more-items";
page.scrollPosition = { top: page.scrollPosition + 1000, left: 0 };
document.location.href=".has-more-items";

但似乎没有任何效果。

【问题讨论】:

  • 你能提供一个示例网址吗?
  • @f.cipriani 我的网址不公开(登录后)。然而,Twitter 流提供了完全相同的场景。例如说这个帐户twitter.com/GSASTeaching。推文流的底部显示了某个元素内的加载图像。我需要在该元素可用时滚动到该元素。当所有内容加载时,该元素在我的情况和 Twitter 流的情况下都不可用。我已经编辑了我的问题以添加更多我尝试过的东西。
  • 如果该课程仍然可用怎么办?我在使用 products-bottom products-bottom--small hide 类的地方工作,一旦加载了所有内容,它仍然存在。通过检查其他类的名称,一切似乎都是使用 reactJS 构建的

标签: javascript dom web-scraping screen-scraping phantomjs


【解决方案1】:

找到了一种方法并尝试适应您的情况。我没有测试找到页面底部的最佳方法,因为我有不同的上下文,但请查看下面的解决方案。这里的问题是您必须稍等片刻才能加载页面并且 javascript 异步工作,因此您必须使用 setIntervalsetTimeout (see) 来实现这一点。

page.open('http://example.com/?q=houston', function () {

  // Check for the bottom div and scroll down from time to time
  window.setInterval(function() {
      // Check if there is a div with class=".has-more-items" 
      // (not sure if there's a better way of doing this)
      var count = page.content.match(/class=".has-more-items"/g);

      if(count === null) { // Didn't find
        page.evaluate(function() {
          // Scroll to the bottom of page
          window.document.body.scrollTop = document.body.scrollHeight;
        });
      }
      else { // Found
        // Do what you want
        ...
        phantom.exit();
      }
  }, 500); // Number of milliseconds to wait between scrolls

});

【讨论】:

  • 它就像一个魅力...谢谢卡了好几天... window.document.body.scrollTop = document.body.scrollHeight;
  • 我们尝试了您的解决方案。但似乎它不起作用。我错过了什么吗?
  • 使用window.scrollTo(0, Math.max(Math.max(document.body.scrollHeight,document.documentElement.scrollHeight),Math.max(document.body.offsetHeight,document.documentElement.offsetHeight),Math.max(document.body.clientHeight, document.documentElement.clientHeight))); 可能会有所帮助,因为这是 casperjs 在内部所做的。
  • @ArtjomB。 window.scrollTo 有时有效,但并非总是如此!这当然取决于网页包含的库。我只是发现一个案例是我需要使用 window.document.body.scrollTop 而不是 window.scrollTo 来触发正确的过程。
  • @ArtjomB.your sn-p 包含奇怪的字符,看起来像英语,但不是:) 具体来说,最后一个 Math.max 坏了
【解决方案2】:

我知道很久以前就已经回答了,但是我也找到了针对我的具体场景的解决方案。结果是一段滚动到页面底部的javascript。它经过优化以减少等待时间。

默认情况下它不是为 PhantomJS 编写的,因此必须对其进行修改。但是,对于初学者或没有 root 访问权限的人来说,带有注入 javascript 的 iframe(使用 --disable-javascript 参数运行 Google Chrome)是抓取较小的 ajax 页面集的一种很好的替代方法。主要好处是它易于调试,因为您可以直观地了解您的抓取工具的运行情况。

function ScrollForAjax () {

    scrollintervals = 50;
    scrollmaxtime = 1000;

    if(typeof(scrolltime)=="undefined"){
        scrolltime = 0;
    }

    scrolldocheight1 = $(iframeselector).contents().find("body").height();

    $("body").scrollTop(scrolldocheight1);
    setTimeout(function(){

        scrolldocheight2 = $("body").height();

        if(scrolltime===scrollmaxtime || scrolltime>scrollmaxtime){
            scrolltime = 0;
            $("body").scrollTop(0);
            ScrapeCurrentPage(iframeselector);
        }

        else if(scrolldocheight2>scrolldocheight1){
            scrolltime = 0;
            ScrollForAjax (iframeselector);
        }

        else if(scrolldocheight1>=scrolldocheight2){
            ScrollForAjax (iframeselector);
        }

    },scrollintervals);

    scrolltime += scrollintervals;
}

scrollmaxtime 是一个超时变量。希望这对某人有用:)

【讨论】:

    【解决方案3】:

    “正确”的解决方案对我不起作用。而且,从我读到的内容来看,CasperJS 不使用 window(但我可能错了),这让我怀疑 window 是否有效。

    以下内容适用于 Firefox/Chrome 控制台;但是,在 CasperJS 中不起作用(在 casper.evaluate 函数内)。

    $(document).scrollTop($(document).height());
    

    在 CasperJS 中对我有用的是:

    casper.scrollToBottom();
    casper.wait(1000, function waitCb() {
      casper.capture("loadedContent.png");
    });
    

    这在将 casper.capture 移动到 Casper 的 then 函数时也有效。

    但是,上述解决方案不适用于 Twitter 等某些网站; jQuery 似乎破坏了casper.scrollToBottom() 函数,在 Twitter 中工作时我不得不删除 clientScripts 对 jQuery 的引用。

    var casper = require('casper').create({
        clientScripts: [
           // 'jquery.js'
        ]
    });
    

    一些网站(例如 BoingBoing.net)似乎可以很好地使用 jQuery 和 CasperJS scrollToBottom()。不知道为什么有些网站可以运行而有些则不能。

    【讨论】:

      【解决方案4】:

      下面的代码 sn-p 适用于 pinterest。我进行了很多研究以在没有 phantomjs 的情况下抓取 pinterest,但无法找到无限滚动触发链接。我认为下面的代码将有助于其他无限滚动网页的抓取。

      page.open(pageUrl).then(function (status) {
          var count = 0;
          // Scrolls to the bottom of page
          function scroll2btm() {
              if (count < 500) {
                  page.evaluate(function(limit) {
                      window.scrollTo(0, document.body.scrollHeight || document.documentElement.scrollHeight);
                      return document.getElementsByClassName('pinWrapper').length; // use desired contents (eg. pin) selector for count presence number
                  }).then(function(c) {
                      count = c;
                      console.log(count); // print no of content found to check
                  });
                  setTimeout(scroll2btm,3000);
              } else {
                  // required number of item found
              }
          }
          scroll2btm();
      });
      

      【讨论】:

      • 这看起来像是一个 Slimer.js 脚本,而不是一个 PhantomJS 脚本。
      猜你喜欢
      • 2021-11-27
      • 2016-12-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多