Web scraping 如何在NodeJs中抓取动态生成的网页?

Web scraping 如何在NodeJs中抓取动态生成的网页?,web-scraping,phantomjs,jsdom,Web Scraping,Phantomjs,Jsdom,有些站点的DOM和内容是在页面加载时动态生成的。(基于Angularjs的网站因此臭名昭著) 你用什么方法? 我尝试了phantomjs和jsdom,但似乎在抓取之前无法让页面执行其javascript 下面是一个简单的jsdom示例(不是基于angularjs的,而是动态生成的) 我尝试了phantomjs,取得了中等程度的成功 var page = new WebPage() var fs = require('fs'); page.onLoadFinished = function()

有些站点的DOM和内容是在页面加载时动态生成的。(基于Angularjs的网站因此臭名昭著)

你用什么方法? 我尝试了phantomjs和jsdom,但似乎在抓取之前无法让页面执行其javascript

下面是一个简单的jsdom示例(不是基于angularjs的,而是动态生成的)

我尝试了phantomjs,取得了中等程度的成功

var page = new WebPage()
var fs = require('fs');

page.onLoadFinished = function() {
  console.log("page load finished");
  window.setTimeout(function() {
    page.render('export.png');
    fs.write('1.html', page.content, 'w');
    phantom.exit();
  }, 10000);
};

page.open("https://www.facebook.com/elcompanies", function() {
  page.evaluate(function() {
  });
});
在这里,我等待onLoadFinished事件,甚至设置了一个10秒的计时器。有趣的是,虽然我对页面的export.png图像捕获显示了一个完全呈现的页面,但我的1.html并没有在正确的位置显示.profilePic类元素。它似乎位于一些javascript代码中,被某种“require(“TimeSlice”).guard(function(){bigPipe.onpageletarrival({…”块)包围着


如果你能为我提供一个从这个页面上刮取图像的工作示例,那会很有帮助。

如果这是一次性的事情,也就是说,如果我只想刮取一次单个页面,我只需要使用浏览器和。

这是因为基于AJAX调用生成的网页具有异步AJAX调用,并且不能依赖于onLoad事件(因为数据仍然不可用)

在我个人看来,最可靠的方法是跟踪从这个HTML调用哪些REST服务,并直接调用它们。有时,您需要使用在HTML中找到的值或从其他调用中获取的值

我知道这听起来很复杂,事实上确实如此。您需要调试页面并了解调用的内容。但这肯定会起作用


顺便说一句,使用chrome开发者工具将有助于完成这项任务。只需观察网络选项卡中发出的呼叫。您甚至可以观察每个AJAX呼叫中发送和接收的内容。

我已经使用在Facebook上进行了一些清理。
以下是我从Facebook页面的一些帖子中获取内容的代码。

module.exports = function checkFacebook(callback) {
var nightmare = Nightmare();
Promise.resolve(nightmare
  .viewport(1000, 1000)
  .goto('https://www.facebook.com/login/')
  .wait(2000)
  .evaluate(function(){
    document.querySelector('input[id="email"]').value = facebookEmail
    document.querySelector('input[id="pass"]').value = facebookPwd
    return true
  })
  .click('#loginbutton input')
  .wait(1000)
  .goto('https://www.facebook.com/groups/bierconomia')
  .evaluate(function(){
    var posts = document.getElementsByClassName('_1dwg')
    var length = posts.length
    var postsContent = []
    for(var i = 0; i < length; i++){
      var pTag = posts[i].getElementsByTagName('p')
      postsContent.push({
        content: pTag[0] ? pTag[0].innerText : '',
        productLink: posts[i].querySelector('a[rel = "nofollow"]') ? posts[i].querySelector('a[rel = "nofollow"]').href : '',
        photo: posts[i].getElementsByClassName('_46-i img')[0] ? posts[i].getElementsByClassName('_46-i img')[0].src : ''
      })
    }
    return postsContent
  }))
  .then(function(results){
    log(results)
    return new Promise(function(resolve, reject) {
      var leanLinks = results.map(function(result){
        return {
          post: {
            content: result.content,
            productLink: extractLinkFromFb(result.productLink),
            photo: result.photo
          }
        }
      })
      resolve(leanLinks)
    })
  })
module.exports=函数检查(回调){
var噩梦=噩梦();
承诺、决心(噩梦)
.视口(10001000)
后藤先生('https://www.facebook.com/login/')
.等等(2000年)
.评估(功能){
document.querySelector('input[id=“email”]”)。value=facebook电子邮件
document.querySelector('input[id=“pass”]”)。value=facebookPwd
返回真值
})
。单击(“#登录按钮输入”)
.等等(1000)
后藤先生('https://www.facebook.com/groups/bierconomia')
.评估(功能){
var posts=document.getElementsByClassName(“1dwg”)
变量长度=posts.length
var postsContent=[]
对于(变量i=0;i


我发现噩梦有用的一点是,您可以使用wait函数来等待X ms或特定类的渲染。

我从未尝试使用phantom在磁盘上写入页面,但我有两个观察结果:

1) 您正在使用fs.write将内容写入磁盘,但writeFile是一个异步调用。这意味着您需要将其更改为fs.writeFileSync,或者在关闭phantom之前使用回调


(二)我希望您不要期望将HTML写入文件并在浏览器中打开它,然后像保存png时那样呈现它,因为它不能以这种方式工作。某些对象可以直接存储在DOM属性中,当然也有存储在javascript变量中的值,这些内容将永远不会被持久化。

不确定为什么我以前的建议是:我的问题没有被删除。我的问题被否决是有原因的吗?如果我违反了SO的条款或问了一些我不应该问的问题,我想知道你没有违反任何条款,这个问题很好。请注意,通过对这个问题给予奖励,你会吸引更多的人关注,这通常会导致更多的选票。在这种情况下,你会我们需要研究javascript在目标页面上的具体执行时间,然后确定phantomjs或jsdom是否允许您在抓取之前等待那么久。例如,jsdom有三个事件可以侦听,但我认为它们中的任何一个都无法在您的情况下工作(您已经在使用最后触发的一个)。我想做这个程序。这可能适用于特定的网站,但我需要一个通用的方法,在我抓取之前呈现动态生成的页面,所以如果你想执行js,你需要使用幻影。我会对你的问题进行评论,因为我看到了一个问题。我没有尝试过噩梦,但它看起来很有希望。我会给它一个a注意,梦魇不是无头的。它依赖于Electron来运行,因此在生产环境中可能会有点沉重。是的,我已经放弃使用梦魇js。现在我使用节点horseman->将代码移植到horsemanRe非常容易。(1)fs.write不是问题。正在编写html文件。Re(2)我希望看到与我查看页面时看到的相同的DOM。当我打开保存的html时,它会在浏览器上正确呈现,但当我在记事本中打开html时,它不会显示相同的DOM
module.exports = function checkFacebook(callback) {
var nightmare = Nightmare();
Promise.resolve(nightmare
  .viewport(1000, 1000)
  .goto('https://www.facebook.com/login/')
  .wait(2000)
  .evaluate(function(){
    document.querySelector('input[id="email"]').value = facebookEmail
    document.querySelector('input[id="pass"]').value = facebookPwd
    return true
  })
  .click('#loginbutton input')
  .wait(1000)
  .goto('https://www.facebook.com/groups/bierconomia')
  .evaluate(function(){
    var posts = document.getElementsByClassName('_1dwg')
    var length = posts.length
    var postsContent = []
    for(var i = 0; i < length; i++){
      var pTag = posts[i].getElementsByTagName('p')
      postsContent.push({
        content: pTag[0] ? pTag[0].innerText : '',
        productLink: posts[i].querySelector('a[rel = "nofollow"]') ? posts[i].querySelector('a[rel = "nofollow"]').href : '',
        photo: posts[i].getElementsByClassName('_46-i img')[0] ? posts[i].getElementsByClassName('_46-i img')[0].src : ''
      })
    }
    return postsContent
  }))
  .then(function(results){
    log(results)
    return new Promise(function(resolve, reject) {
      var leanLinks = results.map(function(result){
        return {
          post: {
            content: result.content,
            productLink: extractLinkFromFb(result.productLink),
            photo: result.photo
          }
        }
      })
      resolve(leanLinks)
    })
  })