PhantomJS能够与Node.js一起使用吗?

28

我想在我的node.js脚本中使用phantomjs。有一个phantomjs-node库..但不幸的是作者用这种奇怪的咖啡脚本代码来解释他在做什么:

phantom = require 'phantom'

phantom.create (ph) ->
  ph.createPage (page) ->
    page.open "http://www.google.com", (status) ->
      console.log "opened google? ", status
      page.evaluate (-> document.title), (result) ->
        console.log 'Page title is ' + result
        ph.exit()

现在,如果我直接使用JavaScript来操作phantomjs,代码应该是这样的this

var page = require('webpage').create();
page.open(url, function (status) {
    var title = page.evaluate(function () {
        return document.title;
    });
    console.log('Page title is ' + title);
});

基本上,我正在尝试用普通JavaScript编写上面第一个代码片段的等效代码(通过阅读CoffeeScript documentation.. 这是我所做的:

// file name: phantomTest.js

var phantom = require('phantom');

phantom.create(function(ph) {
    ph.createPage(function(page) {
        page.open('http://www.google.com', function(status) {
            console.log('opened google?', status);
            var title = page.evaluate(function() {
                return document.title;
            });
            console.log('page title is ' + title);              
        });
    });
    ph.exit();
});

不幸的是它没有工作!如果我运行

node phantomTest.js

在shell上,什么也没有发生...没有返回值,进程也不会停止...有什么想法吗?
更新:
我刚刚在phantomjs faq中读到了这个:
问:为什么PhantomJS没有写成Node.js模块?
答:简短的答案是:“一个人不能同时服务于两个主人。”
更长的解释如下。
目前,这样做在技术上非常具有挑战性。
每个Node.js模块本质上都是“从属于”Node.js核心的“主人”。在当前状态下,PhantomJS(及其包含的WebKit)需要对所有事情(事件循环、网络堆栈和JavaScript执行)进行完全控制(以同步方式)。
如果打算从Node.js中运行的脚本中直接使用PhantomJS,可以通过启动PhantomJS进程并与之交互来实现这种“松散绑定”。
嗯...这可能与此有关吗?但那整个库就没有意义了!
更新2:

我在网站上找到了一段相同效果的代码:

var phantom = require('phantom');
phantom.create(function(ph) {
  return ph.createPage(function(page) {
    return page.open("http://www.google.com", function(status) {
      console.log("opened google? ", status);
      return page.evaluate((function() {
        return document.title;
      }), function(result) {
        console.log('Page title is ' + result);
        return ph.exit();
      });
    });
  });
});

很遗憾,这也不起作用...结果相同!


3
因为你不理解某个东西的工作原理和/或无法使其在你的情况下正常工作,就称其为“愚蠢”的行为是很粗鲁的。 - Ariya Hidayat
2
另外,https://github.com/sheebz/phantom-proxy 是比其他 Node.js 桥更推荐的。人们一直在尝试使用 PhantomJS 与 Ruby、PHP、Node.js 进行桥接,并取得了不同程度的成功。 - Ariya Hidayat
3
抱歉我的措辞过于强烈,我将从问题中删除它。 我也会查看“phantom-proxy”..最终我的目标是使事情正常工作,而不是贬低他人的努力。 - abbood
这已经过时了,应该进行编辑或删除。它不能反映最新的phantomjs-node包及其功能。 - Cory Robinson
7个回答

39

phantomjs-node不是官方支持的phantomjs npm包。相反,它通过创建一个Web服务器,使用Websockets作为Node和Phantom之间的IPC通道,实现了一个“令人恶心的巧妙桥梁”来在Node和Phantom之间进行通信。 我没有编造这个:

因此,我们通过启动ExpressJS的实例,在子进程中打开Phantom,并将其指向一个特殊的网页,将socket.io消息转换为alert()调用来与PhantomJS通信。那些alert()调用被Phantom接收,就这样!

因此,如果phantomjs-node工作、不工作、默默失败或惨烈失败,我都不会感到意外。也没有人能够解决phantomjs-node的故障排除,除了phantomjs-node的作者。

你最初的问题的答案是来自phantomjs faq的答案:不行。Phantom和node有不可调和的差异。两者都希望对基本的低级功能(如事件循环、网络堆栈和JS执行)拥有完全控制权,因此它们无法在同一进程中合作。


5
哇,那太恶心了!那么接下来的问题是:使用jQuery刮取动态页面的最佳方法是什么? - abbood
@abbood 我认为这是不可能的。你的目标是什么? - Rein Henrichs
1
为什么不可能呢?我觉得我找到了我的答案(https://github.com/tmpvar/jsdom/)...像往常一样...我开始走一条非常困难的路线,最终发现了一个更简单的解决方案哈哈.. 我会给你正确答案奖励的。 - abbood
@abbood,你能告诉我如何在Windows上安装jsdom吗? - yasmuru
@xpertgun,我非常讨厌Windows,所以恐怕我无法为你提供太多帮助。 - abbood
显示剩余4条评论

9
我现在是phantom-node包的新维护者。它不再使用coffeescript。你可以像这样操作:
var phantom = require('phantom');

phantom.create().then(function(ph) {
  ph.createPage().then(function(page) {
    page.open('https://stackoverflow.com/').then(function(status) {
      console.log(status);
      page.property('content').then(function(content) {
        console.log(content);
        page.close();
        ph.exit();
      });
    });
  });
});

新版本更快、更强大,而且不再使用WebSockets。

9
您也可以尝试使用 phridge。您的示例将会被写成这样:
var phantom;

// spawn a new PhantomJS process
phridge.spawn()
    .then(function (ph) {
        phantom = ph;
        return phantom.openPage("http://www.google.com");
    })
    .then(function (page) {
        return page.run(function () {
            // this function runs inside PhantomJS with this bound to a webpage instance
            return this.title;
        });
    })
    .then(function (title) {
        console.log('Page title is ' + title);
        // terminates the process cleanly
        phantom.dispose();
    });

1
似乎这个正在运作中..

var phantom = require('phantom');

phantom.create().then(function(ph) {
  ph.createPage().then(function(page) {
    page.open('https://stackoverflow.com/').then(function(status) {
      console.log(status);
      page.property('content').then(function(content) {
        console.log(content);
        page.close();
        ph.exit();
      });
    });
  });
});

但是我正在尝试生成一个带有外部脚本文件的html页面。它无法注入脚本文件。我尝试了以下方式。回调函数无法从page.injectJs('./jQuery.min.js',function() {这一行返回。

var phantom = require('phantom');

    phantom.create().then(function(ph) {
      ph.createPage().then(function(page) {
        page.injectJs('./jQuery.min.js', function() {
          page.property('content').then(function(content) {
            console.log(content);
            page.close();
            ph.exit();
          });
        });
      });
    });

1
将您的代码更改为以下内容,它将能够工作:

change your code to this, and it will be working:

 var phantom = require('phantom');
 phantom.create(function(ph) {
   ph.createPage(function(page) {
     page.open("http://www.google.com", function(status) {
       console.log("opened google? ", status);
       page.evaluate((function() {
         return document.title;
       }), function(result) {
         console.log('Page title is ' + result);
         ph.exit();
       });
     });
   });
 });

1
你可以像我一样放弃PhantomJS,因为它的包装器不太好用,选择Zombie.js,这也是非常流行的。

0

我遇到了和你一样的问题,显然,phantomjs-node 和新版本的 nodejs 存在已知问题。根据问题评论,似乎它在 node 0.9.3 左右停止工作。因此,在问题得到解决之前,你可以降级 nodejs,或尝试其他模块,比如node-phantom,或者使用 exec/spawn


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